From c4309c888873728d417c2964c40cb9d03a89d441 Mon Sep 17 00:00:00 2001
From: Radek Czajka <rczajka@rczajka.pl>
Date: Mon, 26 Sep 2022 12:52:02 +0200
Subject: [PATCH] Author description generator

---
 src/catalogue/admin.py                        |   8 +-
 src/catalogue/constants.py                    |   1 +
 src/catalogue/locale/pl/LC_MESSAGES/django.mo | Bin 4068 -> 4137 bytes
 src/catalogue/locale/pl/LC_MESSAGES/django.po |  88 +++++++++---------
 .../migrations/0038_book_original_year.py     |  18 ++++
 src/catalogue/models.py                       |  11 +++
 .../catalogue/author_description.html         |  47 ++++++++++
 src/catalogue/urls.py                         |   2 +
 src/catalogue/views.py                        |  25 +++++
 src/catalogue/wikidata.py                     |  16 ++--
 10 files changed, 166 insertions(+), 50 deletions(-)
 create mode 100644 src/catalogue/migrations/0038_book_original_year.py
 create mode 100644 src/catalogue/templates/catalogue/author_description.html

diff --git a/src/catalogue/admin.py b/src/catalogue/admin.py
index 213e9ca7..087bfd74 100644
--- a/src/catalogue/admin.py
+++ b/src/catalogue/admin.py
@@ -45,7 +45,7 @@ class AuthorAdmin(WikidataAdminMixin, TabbedTranslationAdmin):
     ]
     list_per_page = 10000000
     search_fields = ["first_name", "last_name", "wikidata"]
-    readonly_fields = ["wikidata_link"]
+    readonly_fields = ["wikidata_link", "description_preview"]
 
     fieldsets = [
         (None, {"fields": [("wikidata", "wikidata_link")]}),
@@ -59,7 +59,7 @@ class AuthorAdmin(WikidataAdminMixin, TabbedTranslationAdmin):
                     "nationality",
                     ("date_of_birth", "year_of_birth", "year_of_birth_inexact", "year_of_birth_range", "place_of_birth"),
                     ("date_of_death", "year_of_death", "year_of_death_inexact", "year_of_death_range", "place_of_death"),
-                    "description",
+                    ("description", "description_preview"),
                     "status",
                     "collections",
                     "priority",
@@ -78,6 +78,9 @@ class AuthorAdmin(WikidataAdminMixin, TabbedTranslationAdmin):
         NotableBookInline,
     ]
 
+    def description_preview(self, obj):
+        return obj.generate_description()
+
 
 admin.site.register(models.Author, AuthorAdmin)
 
@@ -209,6 +212,7 @@ class BookAdmin(WikidataAdminMixin, NumericFilterModelAdmin):
                     "translators",
                     "language",
                     "based_on",
+                    "original_year",
                     "pd_year",
                 ]
             },
diff --git a/src/catalogue/constants.py b/src/catalogue/constants.py
index 0f961b9d..0acc116a 100644
--- a/src/catalogue/constants.py
+++ b/src/catalogue/constants.py
@@ -10,6 +10,7 @@ class WIKIDATA:
     LANGUAGE = "P407"
     DATE_OF_BIRTH = "P569"
     DATE_OF_DEATH = "P570"
+    PUBLICATION_DATE = "P577"
     LAST_NAME = "P734"
     GIVEN_NAME = "P735"
     TRANSLATOR = "P655"
diff --git a/src/catalogue/locale/pl/LC_MESSAGES/django.mo b/src/catalogue/locale/pl/LC_MESSAGES/django.mo
index 39239f881d2b50e2719f557197b2a02ac0521480..4502885fc608a6c17601f2c779a752b66b8159c5 100644
GIT binary patch
delta 1595
zcmYk+OGs2v9LMo9>Kz>|^HG^;Iav^lZ8WnovxiyQQ=(QzF^ZKsjG+Zd*&r13SVT-l
zg@M(gB#^>_Af%`kRunCQ7J^U+p%7tE5cK-~?7!%8&-tAHdEawq?pQ&8FgBW%)Z@77
zNcp7R1m{{+_Q<$~{m#wB7pST+<aQHeNjQlC{BE&Jb}oh3L$#-4I%eZ4EJXF+i;2#~
z+(8POFoGJ;fm-M&rs4_Yc4x@c?}EjbQ495<#`UAd4Osns)c7Hsg~OPQqnL(YaR%$V
z-xQ`(k<M%~AJrj*ny?JDa0SlCD%6QIpcd%DX?Pknt_RisI%@oFiwBUU+ykp0!X(yr
z&#Yn?x!oHw9npJ>KVT;D1nNnDTDy-<>X$~Qg=e7_T!6W_7`2ggs3)&MZnuX_{q|!_
z?<PV)9zzW{i>%=;pq}h1YJywV{s^_;Q`AIntp1(Vk07`EM5Ys(wD_mRekyg+nW>!r
z90~<iu>uuuM7^_pc;DyT5eyNB*o_w4ZPud}3Zss;)$FkLW2gt}MlE<6H{&IXKTPBN
zHQ^U3G{HC25&uFBOkk822%t_R8`VD_)xXf%LuRR2Y1W{|?ZhmsH=|bH9ixy#!&%fT
zxPjd69+@`s7`5PY^Cjwp-kRf9pTJ;^Pa)H*%0WFq5o)6)W;ya~Zar%L*bWNnxXU`!
zTil4+c?;@Yci;>SKyBze@>X1r*@v3=4r;-NsDH^5)chkj8$Vk6kAKFTk6HEW4qyi6
zp(a>@I{H#;uf&<eb*KrOQ2iqocbO+q<IY*!WA>spc+<R#)7>II5ESOo@X8v-QD4IE
zs2^c=Mm*IERqNxmK_N-kI+Ffn+UrtM1xW|?|Fx1pd)B4Hs*ZPhd4ck&*txCoYhFR-
zYNF*<vjVroUwe68Eu}T_&)&iXo2;z_x07^;8%Z@JKA^5LUhL2D?TFX<i!#>|>qEYt
zq?c3`Kjkm*wo%$V*zb=f<hHdpA83w-Bf&#Q8Y0b&;ls^s(O_p&xP5ST;J7cjELdDz
iUb<}XO6s4)oYr7ld*@W|Xj99-0j=T2mcfhZb-urz{FM&?

delta 1529
zcmYk+OGs2v9LMo9>Kz@O)G<p_QzLqyZB)cZmXFlZOfv`zLWGC{OIn2yF}UcpiDWV?
zNLpA>;6g@7p~s@aMIiJbMA9fC33`K2MAr8w|3#O3?&tjf_q^`8cjk8f`@Ha6THJu+
zXrts%F2^|6jX{qK$GG3QESx~4ULvQP;Sz^$F@PT|{)P#}3l=Y8GI4CYbDJ>@)xH&D
zoeR5tRA}Q4pawjMn&=27Vh?h<lU&rU&*Jl_i7uh~T}Aa9vivZr{|K(Yaa@j5n1t^z
zh56kVD$7VLnn6ZYgDlj5>roSL!dxst?Z_U~1jjIly{LX?Q0@Cs{Rb@`LZ)yxEkA;B
z%<rO>7)MU`fQz<h(&A?rBA!7#=_kAY-TaN3*rSss451c~fqLRx<aEVcbiWGqO6oAI
zN;?&G=t9PFhfz;-95ujcyWfwRXb?5fu;uSqeiS*~eJ<LWNsFIb{2H~>^O%VX0rp>s
zWLhhcgL+q`IOcP1Cl(NEHJY%{EJDU`WvDH!HJj{yJL-WtP~&%EGaj}0E^53dLH1t*
zJSCy6eSzvYjhf&MYDYezI{ZMj|7rI%y4oe0>1Hmf-$qQsBD3D|9hgDBD@;YNpcgsa
zIWAhr0BXW(<_**i-7+6qej3$(4)v-&qaMIVQVUHmQ;{{hOw{=6Q0>A6)}Y7|6{wZh
zpx$*8rr=)Gf({{X#dVt}Py_d&CcKCm_zJGX>y{rg@1Z{C$C#?$|0}yOkJ`$w*5DVW
z6DKjd2Fyn~xD6JUo7Jd(jTY}VJ5USjG`o?_alN=2&o1SeVVH{k`9@J6-&9H@%PVN8
zq`HlwUDZ)TSw+!9mQeI(rj7dlC@0XGb!d~?A{V^uK*dtb*Blw~@<LTaWtOSO=E$s<
z?d_npHS)(>yQb0Z72__7HnD-Sox&fc+Y+huXZTtp`~8KXYGNI=6uq3f$bdi3YoXQ@
fo$&X>L~8@5eDTHkMWvOcWzk28e`BMw$t}Kr{W5}y

diff --git a/src/catalogue/locale/pl/LC_MESSAGES/django.po b/src/catalogue/locale/pl/LC_MESSAGES/django.po
index 044f9605..86f4d12d 100644
--- a/src/catalogue/locale/pl/LC_MESSAGES/django.po
+++ b/src/catalogue/locale/pl/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2022-09-22 15:57+0200\n"
+"PO-Revision-Date: 2022-09-26 11:45+0200\n"
 "Last-Translator: \n"
 "Language-Team: \n"
 "Language: pl\n"
@@ -23,23 +23,23 @@ msgstr ""
 msgid "Identification"
 msgstr "Identyfikacja"
 
-#: catalogue/admin.py:217
+#: catalogue/admin.py:218
 msgid "Features"
 msgstr "Cechy"
 
-#: catalogue/admin.py:227
+#: catalogue/admin.py:228
 msgid "Plan"
 msgstr "Plan"
 
-#: catalogue/admin.py:269
+#: catalogue/admin.py:270
 msgid "Title"
 msgstr "tytuł"
 
-#: catalogue/admin.py:275
+#: catalogue/admin.py:276
 msgid "Book"
 msgstr "książka"
 
-#: catalogue/admin.py:285 catalogue/models.py:160
+#: catalogue/admin.py:286 catalogue/models.py:160
 msgid "scans source"
 msgstr "źródło skanów"
 
@@ -123,12 +123,12 @@ msgstr "Dawno zmarły"
 msgid "Unknown"
 msgstr "Nieznany"
 
-#: catalogue/models.py:52 catalogue/models.py:162 catalogue/models.py:228
-#: catalogue/models.py:246
+#: catalogue/models.py:52 catalogue/models.py:162 catalogue/models.py:232
+#: catalogue/models.py:250
 msgid "notes"
 msgstr "notatki"
 
-#: catalogue/models.py:53 catalogue/models.py:168
+#: catalogue/models.py:53 catalogue/models.py:169
 msgid "gazeta link"
 msgstr "link do bazy gazety"
 
@@ -140,7 +140,7 @@ msgstr "link do bazy culture.pl"
 msgid "description"
 msgstr "opis"
 
-#: catalogue/models.py:59 catalogue/models.py:164 catalogue/models.py:296
+#: catalogue/models.py:59 catalogue/models.py:164 catalogue/models.py:300
 msgid "priority"
 msgstr "priorytet"
 
@@ -156,8 +156,8 @@ msgstr "Średni"
 msgid "High"
 msgstr "Wysoki"
 
-#: catalogue/models.py:62 catalogue/models.py:169 catalogue/models.py:251
-#: catalogue/models.py:303
+#: catalogue/models.py:62 catalogue/models.py:170 catalogue/models.py:255
+#: catalogue/models.py:307
 msgid "collections"
 msgstr "kolekcje"
 
@@ -169,8 +169,8 @@ msgstr "autor"
 msgid "authors"
 msgstr "autorzy"
 
-#: catalogue/models.py:114 catalogue/models.py:226 catalogue/models.py:243
-#: catalogue/models.py:276 catalogue/models.py:329
+#: catalogue/models.py:114 catalogue/models.py:230 catalogue/models.py:247
+#: catalogue/models.py:280 catalogue/models.py:333
 msgid "name"
 msgstr "nazwa"
 
@@ -178,7 +178,7 @@ msgstr "nazwa"
 msgid "epoch"
 msgstr "epoka"
 
-#: catalogue/models.py:126 catalogue/models.py:151 catalogue/models.py:300
+#: catalogue/models.py:126 catalogue/models.py:151 catalogue/models.py:304
 msgid "epochs"
 msgstr "epoki"
 
@@ -186,7 +186,7 @@ msgstr "epoki"
 msgid "genre"
 msgstr "gatunek"
 
-#: catalogue/models.py:132 catalogue/models.py:153 catalogue/models.py:302
+#: catalogue/models.py:132 catalogue/models.py:153 catalogue/models.py:306
 msgid "genres"
 msgstr "gatunki"
 
@@ -194,7 +194,7 @@ msgstr "gatunki"
 msgid "kind"
 msgstr "rodzaj"
 
-#: catalogue/models.py:138 catalogue/models.py:152 catalogue/models.py:301
+#: catalogue/models.py:138 catalogue/models.py:152 catalogue/models.py:305
 msgid "kinds"
 msgstr "rodzaje"
 
@@ -219,102 +219,106 @@ msgid "text source"
 msgstr "źródło tekstu"
 
 #: catalogue/models.py:167
+msgid "original publication year"
+msgstr "rok oryginalnej publikacji"
+
+#: catalogue/models.py:168
 msgid "year of entry into PD"
 msgstr "rok wstąpienia do DP"
 
-#: catalogue/models.py:171
+#: catalogue/models.py:172
 msgid "estimated number of characters"
 msgstr "szacowana liczba znaków"
 
-#: catalogue/models.py:172
+#: catalogue/models.py:173
 msgid "estimated number of verses"
 msgstr "szacowana liczba wersów"
 
-#: catalogue/models.py:173
+#: catalogue/models.py:174
 msgid "source of estimates"
 msgstr "źródło szacunków"
 
-#: catalogue/models.py:175
+#: catalogue/models.py:176
 msgid "free license"
 msgstr "wolna licencja"
 
-#: catalogue/models.py:176
+#: catalogue/models.py:177
 msgid "missing on Polona"
 msgstr "brak na Polonie"
 
-#: catalogue/models.py:180
+#: catalogue/models.py:181
 msgid "book"
 msgstr "książka"
 
-#: catalogue/models.py:181
+#: catalogue/models.py:182
 msgid "books"
 msgstr "książki"
 
-#: catalogue/models.py:211
+#: catalogue/models.py:215
 msgid "Author"
 msgstr "autor"
 
-#: catalogue/models.py:216
+#: catalogue/models.py:220
 msgid "Translator"
 msgstr "tłumacze"
 
-#: catalogue/models.py:227
+#: catalogue/models.py:231
 msgid "parent"
 msgstr "rodzic"
 
-#: catalogue/models.py:232
+#: catalogue/models.py:236
 msgid "collection category"
 msgstr "kategoria kolekcji"
 
-#: catalogue/models.py:233
+#: catalogue/models.py:237
 msgid "collection categories"
 msgstr "kategorie kolekcji"
 
-#: catalogue/models.py:245
+#: catalogue/models.py:249
 msgid "category"
 msgstr "kategoria"
 
-#: catalogue/models.py:250
+#: catalogue/models.py:254
 msgid "collection"
 msgstr "kolekcja"
 
-#: catalogue/models.py:280 catalogue/models.py:299
+#: catalogue/models.py:284 catalogue/models.py:303
 msgid "work type"
 msgstr "rodzaj pracy"
 
-#: catalogue/models.py:281
+#: catalogue/models.py:285
 msgid "work types"
 msgstr "rodzaje prac"
 
-#: catalogue/models.py:297
+#: catalogue/models.py:301
 msgid "per normalized page"
 msgstr "za stronę maszynopisu"
 
-#: catalogue/models.py:298
+#: catalogue/models.py:302
 msgid "per verse"
 msgstr "za wers"
 
-#: catalogue/models.py:307
+#: catalogue/models.py:311
 msgid "work rate"
 msgstr "stawka"
 
-#: catalogue/models.py:308
+#: catalogue/models.py:312
 msgid "work rates"
 msgstr "stawki"
 
-#: catalogue/models.py:330
+#: catalogue/models.py:334
 msgid "locative"
 msgstr "miejscownik"
 
-#: catalogue/models.py:330
+#: catalogue/models.py:334
 msgid "in…"
 msgstr "w…"
 
-#: catalogue/models.py:333
+#: catalogue/models.py:337
 msgid "place"
 msgstr "miejsce"
 
-#: catalogue/models.py:334
+#: catalogue/models.py:338
 msgid "places"
 msgstr "miejsca"
 
@@ -341,7 +345,7 @@ msgstr[1] "%(c)s autorów"
 msgstr[2] "%(c)s autorów"
 msgstr[3] ""
 
-#: catalogue/wikidata.py:18
+#: catalogue/wikidata.py:21
 msgid "If you have a Wikidata ID, like \"Q1337\", enter it and save."
 msgstr "Jeśli masz identyfikator Wikidanych , jak „Q1337”, wklej go i zapisz."
 
diff --git a/src/catalogue/migrations/0038_book_original_year.py b/src/catalogue/migrations/0038_book_original_year.py
new file mode 100644
index 00000000..d8408dda
--- /dev/null
+++ b/src/catalogue/migrations/0038_book_original_year.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.0.6 on 2022-09-26 11:36
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('catalogue', '0037_alter_place_options_author_year_of_birth_inexact_and_more'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='book',
+            name='original_year',
+            field=models.IntegerField(blank=True, null=True, verbose_name='original publication year'),
+        ),
+    ]
diff --git a/src/catalogue/models.py b/src/catalogue/models.py
index b974de14..92275ecf 100644
--- a/src/catalogue/models.py
+++ b/src/catalogue/models.py
@@ -2,6 +2,7 @@ from collections import Counter
 import decimal
 from django.apps import apps
 from django.db import models
+from django.template.loader import render_to_string
 from django.urls import reverse
 from django.utils.translation import gettext_lazy as _
 from admin_ordering.models import OrderableModel
@@ -104,6 +105,12 @@ class Author(WikidataModel):
         else:
             return None
 
+    def generate_description(self):
+        t = render_to_string(
+            'catalogue/author_description.html',
+            {'obj': self}
+        )
+        return t
 
 class NotableBook(OrderableModel):
     author = models.ForeignKey(Author, models.CASCADE)
@@ -164,6 +171,7 @@ class Book(WikidataModel):
         _("priority"),
         default=0, choices=[(0, _("Low")), (1, _("Medium")), (2, _("High"))]
     )
+    original_year = models.IntegerField(_('original publication year'), null=True, blank=True)
     pd_year = models.IntegerField(_('year of entry into PD'), null=True, blank=True)
     gazeta_link = models.CharField(_("gazeta link"), max_length=255, blank=True)
     collections = models.ManyToManyField("Collection", blank=True, verbose_name=_("collections"))
@@ -186,10 +194,13 @@ class Book(WikidataModel):
         title = WIKIDATA.TITLE
         language = WIKIDATA.LANGUAGE
         based_on = WIKIDATA.BASED_ON
+        original_year = WIKIDATA.PUBLICATION_DATE
         notes = "description"
 
     def __str__(self):
         txt = self.title
+        if self.original_year:
+            txt = f"{txt} ({self.original_year})"
         astr = self.authors_str()
         if astr:
             txt = f"{txt}, {astr}"
diff --git a/src/catalogue/templates/catalogue/author_description.html b/src/catalogue/templates/catalogue/author_description.html
new file mode 100644
index 00000000..934e4b6c
--- /dev/null
+++ b/src/catalogue/templates/catalogue/author_description.html
@@ -0,0 +1,47 @@
+<dl>
+  {% if obj.date_of_birth or obj.place_of_birth %}
+    <dt>Ur.</dt>
+    <dd>
+      {% if obj.date_of_birth %}
+        {{ obj.date_of_birth }}
+      {% endif %}
+      {% if obj.place_of_birth %}
+        w
+        {% if obj.place_of_birth.locative %}
+          {{ obj.place_of_birth.locative }}
+        {% else %}
+          {{ obj.place_of_birth.name }}
+        {% endif %}
+      {% endif %}
+    </dd>
+  {% endif %}
+  {% if obj.date_of_death or obj.place_of_death %}
+    <dt>Zm.</dt>
+    <dd>
+      {% if obj.date_of_death %}
+        {{ obj.date_of_death }}
+      {% endif %}
+      {% if obj.place_of_death %}
+        w
+        {% if obj.place_of_death.locative %}
+          {{ obj.place_of_death.locative }}
+        {% else %}
+          {{ obj.place_of_death.name }}
+        {% endif %}
+      {% endif %}
+    </dd>
+  {% endif %}
+
+  {% if obj.notablebook_set.exists %}
+    <dt>Najważniejsze dzieła:</dt>
+    <dd>
+      {% for nb in obj.notablebook_set.all %}
+        <i>{{ nb.book.title }}</i>{% if nb.book.original_year %}
+          ({{ nb.book.original_year }}){% endif %}{% if not forloop.last %}, {% endif %}
+      {% endfor %}
+    </dd>
+  {% endif %}
+
+</dl>
+
+{{ obj.description }}
diff --git a/src/catalogue/urls.py b/src/catalogue/urls.py
index d4fd6b6a..b7625388 100644
--- a/src/catalogue/urls.py
+++ b/src/catalogue/urls.py
@@ -20,4 +20,6 @@ urlpatterns = [
     path('terms/editor/', views.EditorTerms.as_view()),
 
     path('wikidata/<slug:model>/<qid>', views.WikidataView.as_view()),
+
+    path('publish/author/<int:pk>/', views.publish_author, name='catalogue_publish_author'),
 ]
diff --git a/src/catalogue/views.py b/src/catalogue/views.py
index 65b29808..c507aca4 100644
--- a/src/catalogue/views.py
+++ b/src/catalogue/views.py
@@ -4,9 +4,14 @@
 from django.apps import apps
 from django.db.models import Prefetch
 from django.http import Http404
+from django.urls import reverse
 from django.utils.formats import localize_input
+from django.contrib.auth.decorators import login_required
 from django.contrib.auth.models import User
+from django.shortcuts import get_object_or_404, redirect
+from django.views.decorators.http import require_POST
 from django.views.generic import DetailView, TemplateView
+import apiclient
 from . import models
 import documents.models
 from rest_framework.generics import ListAPIView
@@ -126,6 +131,13 @@ class WikidataView(APIView):
                         "wd": d[fieldname].wikidata,
                         "label": str(d[fieldname]) or d[fieldname]._wikidata_label,
                     }
+                elif hasattr(d[fieldname], 'all'):
+                    d[attname] = [
+                            {"model": type(item)._meta.model_name,
+                                "wd": item.wikidata,
+                                "label": str(item) or item._wikidata_label
+                                } for item in d[attname].all()
+                            ]
                 else:
                     d[fieldname] = localize_input(d[fieldname])
         return Response(d)
@@ -135,3 +147,16 @@ class WikidataView(APIView):
 
     def post(self, request, model, qid):
         return self.get_object(model, qid, save=True)
+
+
+@require_POST
+@login_required
+def publish_author(request, pk):
+    author = get_object_or_404(models.Author, pk=pk)
+    data = {
+        "description_pl": author.generate_description(),
+    }
+    apiclient.api_call(request.user, f"authors/{author.slug}/", data)
+    return redirect(reverse('admin:catalogue_author_change', args=[author.pk]))
+        
+    
diff --git a/src/catalogue/wikidata.py b/src/catalogue/wikidata.py
index b5f3e94f..3ec843a7 100644
--- a/src/catalogue/wikidata.py
+++ b/src/catalogue/wikidata.py
@@ -65,12 +65,16 @@ class WikidataModel(models.Model):
     def wikidata_fields_for_attribute(self, attname):
         field = getattr(type(self), attname)
         if type(self) in translator._registry:
-            opts = translator.get_options_for_model(type(self))
-            if attname in opts.fields:
-                tfields = opts.fields[attname]
-                for tf in tfields:
-                    yield tf.name, tf.language
-                return
+            try:
+                opts = translator.get_options_for_model(type(self))
+            except:
+                pass
+            else:
+                if attname in opts.fields:
+                    tfields = opts.fields[attname]
+                    for tf in tfields:
+                        yield tf.name, tf.language
+                    return
 
         yield attname, settings.LANGUAGE_CODE
 
-- 
2.20.1