X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/391d6cbc649f387a731f724cd4cf7dbcb02e26c2..2de0fde86873c879961365b4b9ce3904a158e84f:/src/catalogue/models.py diff --git a/src/catalogue/models.py b/src/catalogue/models.py index ea7bf54b..1aa145c8 100644 --- a/src/catalogue/models.py +++ b/src/catalogue/models.py @@ -1,6 +1,10 @@ from collections import Counter +from datetime import date, timedelta import decimal +import re +from urllib.request import urlopen from django.apps import apps +from django.conf import settings from django.db import models from django.template.loader import render_to_string from django.urls import reverse @@ -16,16 +20,25 @@ class Author(WikidataModel): slug = models.SlugField(max_length=255, null=True, blank=True, unique=True) first_name = models.CharField(_("first name"), max_length=255, blank=True) last_name = models.CharField(_("last name"), max_length=255, blank=True) + genitive = models.CharField( + 'dopełniacz', max_length=255, blank=True, + help_text='utwory … (czyje?)' + ) name_de = models.CharField(_("name (de)"), max_length=255, blank=True) name_lt = models.CharField(_("name (lt)"), max_length=255, blank=True) gender = models.CharField(_("gender"), max_length=255, blank=True) nationality = models.CharField(_("nationality"), max_length=255, blank=True) + year_of_birth = models.SmallIntegerField(_("year of birth"), null=True, blank=True) year_of_birth_inexact = models.BooleanField(_("inexact"), default=False) year_of_birth_range = models.SmallIntegerField(_("year of birth, range end"), null=True, blank=True) date_of_birth = models.DateField(_("date_of_birth"), null=True, blank=True) + century_of_birth = models.SmallIntegerField( + _("century of birth"), null=True, blank=True, + help_text=_('Set if year unknown. Negative for BC.') + ) place_of_birth = models.ForeignKey( 'Place', models.PROTECT, null=True, blank=True, verbose_name=_('place of birth'), @@ -35,6 +48,10 @@ class Author(WikidataModel): year_of_death_inexact = models.BooleanField(_("inexact"), default=False) year_of_death_range = models.SmallIntegerField(_("year of death, range end"), null=True, blank=True) date_of_death = models.DateField(_("date_of_death"), null=True, blank=True) + century_of_death = models.SmallIntegerField( + _("century of death"), null=True, blank=True, + help_text=_('Set if year unknown. Negative for BC.') + ) place_of_death = models.ForeignKey( 'Place', models.PROTECT, null=True, blank=True, verbose_name=_('place of death'), @@ -122,6 +139,47 @@ class Author(WikidataModel): ) return t + def century_description(self, number): + n = abs(number) + letters = '' + while n > 10: + letters += 'X' + n -= 10 + if n == 9: + letters += 'IX' + n = 0 + elif n >= 5: + letters += 'V' + n -= 5 + if n == 4: + letters += 'IV' + n = 0 + letters += 'I' * n + letters += ' w.' + if number < 0: + letters += ' p.n.e.' + return letters + + def birth_century_description(self): + return self.century_description(self.century_of_birth) + + def death_century_description(self): + return self.century_description(self.century_of_death) + + def year_description(self, number): + n = abs(number) + letters = str(n) + letters += ' r.' + if number < 0: + letters += ' p.n.e.' + return letters + + def year_of_birth_description(self): + return self.year_description(self.year_of_birth) + def year_of_death_description(self): + return self.year_description(self.year_of_death) + + class NotableBook(OrderableModel): author = models.ForeignKey(Author, models.CASCADE) book = models.ForeignKey('Book', models.CASCADE) @@ -130,6 +188,7 @@ class NotableBook(OrderableModel): class Category(WikidataModel): name = models.CharField(_("name"), max_length=255) slug = models.SlugField(max_length=255, unique=True) + description = models.TextField(_("description"), blank=True, help_text=_('for publication')) class Meta: abstract = True @@ -137,19 +196,43 @@ class Category(WikidataModel): def __str__(self): return self.name + class Epoch(Category): + adjective_feminine_singular = models.CharField( + 'przymiotnik pojedynczy żeński', max_length=255, blank=True, + help_text='twórczość … Adama Mickiewicza' + ) + adjective_nonmasculine_plural = models.CharField( + 'przymiotnik mnogi niemęskoosobowy', max_length=255, blank=True, + help_text='utwory … Adama Mickiewicza' + ) + class Meta: verbose_name = _('epoch') verbose_name_plural = _('epochs') class Genre(Category): + plural = models.CharField( + 'liczba mnoga', max_length=255, blank=True, + help_text='dotyczy gatunków' + ) + is_epoch_specific = models.BooleanField( + default=False, + help_text='Po wskazaniu tego gatunku, dodanie epoki byłoby nadmiarowe, np. „dramat romantyczny”' + ) + class Meta: verbose_name = _('genre') verbose_name_plural = _('genres') class Kind(Category): + collective_noun = models.CharField( + 'określenie zbiorowe', max_length=255, blank=True, + help_text='np. „Liryka” albo „Twórczość dramatyczna”' + ) + class Meta: verbose_name = _('kind') verbose_name_plural = _('kinds') @@ -193,6 +276,9 @@ class Book(WikidataModel): free_license = models.BooleanField(_('free license'), default=False) polona_missing = models.BooleanField(_('missing on Polona'), default=False) + monthly_views_reader = models.IntegerField(default=0) + monthly_views_page = models.IntegerField(default=0) + class Meta: ordering = ("title",) verbose_name = _('book') @@ -252,12 +338,68 @@ class Book(WikidataModel): def translators_last_names(self): return ', '.join(a.last_name for a in self.translators.all()) + def document_book__project(self): + b = self.document_books.first() + if b is None: return '' + if b.project is None: return '' + return b.project.name + + def audience(self): + try: + return self.document_books.first().wldocument().book_info.audience or '' + except: + return '' + def get_estimated_costs(self): return { work_type: work_type.calculate(self) for work_type in WorkType.objects.all() } + def update_monthly_stats(self): + # Find publication date. + # By default, get previous 12 months. + this_month = date.today().replace(day=1) + cutoff = this_month.replace(year=this_month.year - 1) + months = 12 + + # If the book was published later, + # find out the denominator. + pbr = apps.get_model('documents', 'BookPublishRecord').objects.filter( + book__catalogue_book=self).order_by('timestamp').first() + if pbr is not None and pbr.timestamp.date() > cutoff: + months = (this_month - pbr.timestamp.date()).days / 365 * 12 + + if not months: + return + + stats = self.bookmonthlystats_set.filter(date__gte=cutoff).aggregate( + views_page=models.Sum('views_page'), + views_reader=models.Sum('views_reader') + ) + self.monthly_views_page = stats['views_page'] / months + self.monthly_views_reader = stats['views_reader'] / months + self.save(update_fields=['monthly_views_page', 'monthly_views_reader']) + + @property + def content_stats(self): + if hasattr(self, '_content_stats'): + return self._content_stats + try: + stats = self.document_books.first().wldocument().get_statistics()['total'] + except Exception as e: + stats = {} + self._content_stats = stats + return stats + + chars = lambda self: self.content_stats.get('chars', '') + chars_with_fn = lambda self: self.content_stats.get('chars_with_fn', '') + words = lambda self: self.content_stats.get('words', '') + words_with_fn = lambda self: self.content_stats.get('words_with_fn', '') + verses = lambda self: self.content_stats.get('verses', '') + verses_with_fn = lambda self: self.content_stats.get('verses_with_fn', '') + chars_out_verse = lambda self: self.content_stats.get('chars_out_verse', '') + chars_out_verse_with_fn = lambda self: self.content_stats.get('chars_out_verse_with_fn', '') class CollectionCategory(models.Model): name = models.CharField(_("name"), max_length=255) @@ -376,3 +518,53 @@ class Place(WikidataModel): def __str__(self): return self.name + + +class BookMonthlyStats(models.Model): + book = models.ForeignKey('catalogue.Book', models.CASCADE) + date = models.DateField() + views_reader = models.IntegerField(default=0) + views_page = models.IntegerField(default=0) + + @classmethod + def build_for_month(cls, date): + date = date.replace(day=1) + period = 'month' + + date = date.isoformat() + url = f'{settings.PIWIK_URL}?date={date}&filter_limit=-1&format=CSV&idSite={settings.PIWIK_WL_SITE_ID}&language=pl&method=Actions.getPageUrls&module=API&period={period}&segment=&token_auth={settings.PIWIK_TOKEN}&flat=1' + data = urlopen(url).read().decode('utf-16') + lines = data.split('\n')[1:] + for line in lines: + m = re.match('^/katalog/lektura/([^,./]+)\.html,', line) + if m is not None: + which = 'views_reader' + else: + m = re.match('^/katalog/lektura/([^,./]+)/,', line) + if m is not None: + which = 'views_page' + if m is not None: + slug = m.group(1) + _url, _uviews, views, _rest = line.split(',', 3) + views = int(views) + try: + book = Book.objects.get(slug=slug) + except Book.DoesNotExist: + continue + else: + cls.objects.update_or_create( + book=book, date=date, + defaults={which: views} + ) + book.update_monthly_stats() + + +class Thema(models.Model): + code = models.CharField(max_length=128, unique=True) + name = models.CharField(max_length=1024) + description = models.TextField(blank=True) + usable = models.BooleanField() + hidden = models.BooleanField(default=False) + + class Meta: + ordering = ('code',)