import logging from typing import Dict from uuid import uuid4 from django_extensions.db.models import TimeStampedModel import pendulum import requests from taggit.managers import TaggableManager import trafilatura from django.apps import apps from django.conf import settings from django.contrib.auth import get_user_model from django.db import models from django.urls import reverse from htmldate import find_date from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin logger = logging.getLogger(__name__) BNULL = {"blank": True, "null": True} User = get_user_model() class Domain(TimeStampedModel): uuid = models.UUIDField(default=uuid4, editable=False, **BNULL) root = models.CharField(max_length=255) name = models.CharField(max_length=255, **BNULL) tags = TaggableManager(blank=True) def __str__(self) -> str: if self.name: return str(self.name) return str(self.root) def scrobbles_for_user(self, user_id): from scrobbles.models import Scrobble return Scrobble.objects.filter( web_page__domain=self, user_id=user_id ).order_by("-timestamp") class WebPage(ScrobblableMixin): COMPLETION_PERCENT = getattr(settings, "WEBSITE_COMPLETION_PERCENT", 100) uuid = models.UUIDField(default=uuid4, editable=False, **BNULL) url = models.URLField(max_length=500) date = models.DateField(**BNULL) domain = models.ForeignKey(Domain, on_delete=models.DO_NOTHING, **BNULL) extract = models.TextField(**BNULL) def __str__(self) -> str: if self.title: return "{} ({})".format(self.title, self.domain) if self.domain: return "Unknown ({})".format(self.domain) return str(self.uuid) def _raw_domain(self): self.url.split("//")[-1].split("/")[0] def _update_extract_from_web(self, raw_text: str = "", force=True): if not raw_text: raw_text = requests.get(self.url, headers=headers).text if not self.extract or force: self.extract = trafilatura.extract(raw_text) self.save(update_fields=["extract"]) def get_absolute_url(self): return reverse("webpages:webpage-detail", kwargs={"slug": self.uuid}) def get_read_url(self): return reverse("webpages:webpage-read", kwargs={"slug": self.uuid}) @property def estimated_time_to_read_in_seconds(self): if not self.extract: return 600 words_per_minute = getattr(settings, "READING_WORDS_PER_MINUTE", 200) words = len(self.extract.split(" ")) return int(words / words_per_minute) * 60 @property def estimated_time_to_read_in_minutes(self): return int(self.estimated_time_to_read_in_seconds / 60) @property def strings(self) -> ScrobblableConstants: return ScrobblableConstants(verb="Browsing", tags="earth_americas") @property def subtitle(self): return self.domain @property def primary_image_url(self) -> str: # TODO Figure out how to add a preview? return "" def scrobble_for_user(self, user_id): Scrobble = apps.get_model("scrobbles", "Scrobble") scrobble_data = self.basic_scrobble_data(user_id) logger.info( "[scrobble_for_user] called for webpage", extra={ "webpage_id": self.id, "user_id": user_id, "scrobble_data": scrobble_data, "media_type": Scrobble.MediaType.WEBPAGE, }, ) scrobble = Scrobble.create_or_update(self, user_id, scrobble_data) # TODO Possibly make this async? scrobble.push_to_archivebox() return scrobble def scrobbles(self, user): Scrobble = apps.get_model("scrobbles", "Scrobble") return Scrobble.objects.filter(user=user, web_page=self).order_by( "-timestamp" ) def clean_title(self, title: str, save=True): if len(title.split("|")) > 1: title = title.split("|")[0] if len(title.split("–")) > 1: title = title.split("–")[0] if len(title.split(" - ")) > 1: title = title.split(" - ")[0] self.title = title.strip() if save: self.save(update_fields=["title"]) def _update_domain_from_url(self, save=False): domain = self.url.split("//")[-1].split("/")[0].split("www.")[-1] self.domain, created = Domain.objects.get_or_create(root=domain) if save: self.save(update_fields=["domain"]) def _update_title_from_web(self, raw_text: str, save=False): if not raw_text: return self.title = raw_text[ raw_text.find("