Audience and thema editing.
authorRadek Czajka <rczajka@rczajka.pl>
Fri, 28 Jul 2023 11:56:41 +0000 (13:56 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Fri, 28 Jul 2023 11:56:41 +0000 (13:56 +0200)
requirements/requirements.txt
src/catalogue/urls.py
src/catalogue/views.py
src/depot/legimi.py
src/documents/templates/documents/book_detail.html
src/documents/views.py
src/redakcja/static/js/wiki/view_properties.js
src/wlxml/views.py

index cc6b09d..921bc06 100644 (file)
@@ -10,7 +10,7 @@ python-slugify==8.0.1
 python-docx==0.8.11
 Wikidata==0.7
 
-librarian==2.6
+librarian==23.07
 
 ## Django
 Django==4.1.9
index ac9cede..d512eea 100644 (file)
@@ -11,6 +11,7 @@ urlpatterns = [
     path("book/<slug:slug>/", views.BookView.as_view(), name="catalogue_book"),
     path("book/<slug:slug>.json", views.BookAPIView.as_view(), name="catalogue_book_api"),
 
+    path('terms/audience/', views.AudienceTerms.as_view()),
     path('terms/epoch/', views.EpochTerms.as_view()),
     path('terms/kind/', views.KindTerms.as_view()),
     path('terms/genre/', views.GenreTerms.as_view()),
@@ -18,6 +19,7 @@ urlpatterns = [
     path('terms/book_title/', views.BookTitleTerms.as_view()),
     path('terms/author/', views.AuthorTerms.as_view()),
     path('terms/thema/', views.ThemaTerms.as_view()),
+    path('terms/thema-main/', views.MainThemaTerms.as_view()),
 
     path('terms/editor/', views.EditorTerms.as_view()),
 
index 6eb6e63..44ad305 100644 (file)
@@ -106,6 +106,15 @@ class Terms(ListAPIView):
         label = serializers.CharField(source='name')
 
 
+class AudienceTerms(Terms):
+    queryset = models.Audience.objects.all()
+    search_fields = ['code', 'name', 'description']
+
+    class serializer_class(serializers.Serializer):
+        label = serializers.CharField(source='code')
+        name = serializers.CharField()
+        description = serializers.CharField()
+
 class EpochTerms(Terms):
     queryset = models.Epoch.objects.all()
 class KindTerms(Terms):
@@ -150,6 +159,9 @@ class ThemaTerms(Terms):
         name = serializers.CharField()
         description = serializers.CharField()
 
+class MainThemaTerms(ThemaTerms):
+    queryset = models.Thema.objects.filter(usable=True, hidden=False, usable_as_main=True)
+
 
 class WikidataView(APIView):
     permission_classes = [IsAdminUser]
index f61bbfd..464eeca 100644 (file)
@@ -184,13 +184,18 @@ class Legimi:
             base_url='file://' + book.gallery_path() + '/'
         ).build(wlbook).get_file()
 
+        thema = []
+        if meta.thema_main:
+            thema.append(meta.thema_main)
+        thema.extend(meta.thema)
+        
         book_data = {
             "Title": meta.title,
             "Author": ", ".join(p.readable() for p in meta.authors),
             "Year": str(date.today().year),
 
             'GenreId': str(self.get_genre(wlbook)),
-            'themaCategories': ';'.join(meta.thema),
+            'themaCategories': ';'.join(thema),
             'thema-search': '',
             'Isbn': '',
             'LanguageLocale': lang_code_3to2(meta.language),
@@ -289,12 +294,13 @@ class Legimi:
             for p in wlbook.meta.genres
         ) + '</p>'
 
-        if wlbook.meta.audience:
+        # TODO: Move away from using audiences for this.
+        if wlbook.meta.audience in ('L', 'SP1', 'SP2', 'SP3', 'SP4'):
             description += '<p><em>{}</em> to lektura szkolna.'.format(wlbook.meta.title)
             if wlbook.tree.find('//pe') is not None:
                 description += '<br>Ebook <em>{title}</em> zawiera przypisy opracowane specjalnie dla uczennic i uczniów {school}.'.format(
                     title=wlbook.meta.title,
-                    school='szkoły podstawowej' if wlbook.meta.audience == 'SP' else 'liceum i technikum'
+                    school='szkoły podstawowej' if wlbook.meta.audience.startswith('SP') else 'liceum i technikum'
                 )
             description += '</p>'
         return description
index ee053d5..caa3de5 100644 (file)
 
         {% if perms.depot.add_legimibookpublish %}
           <hr>
-          {% with thema=doc.book_info.thema %}
-            {% if thema %}
+          {% with thema_main=doc.book_info.thema_main thema=doc.book_info.thema %}
+            {% if thema_main or thema %}
               <form method="post" action="{% url 'depot_legimi_publish' book.pk %}">
                 {% csrf_token %}
                 <button class="btn btn-primary" type="submit">
                   Opublikuj na Legimi<br><small>w kategorii:
+                  {% if thema_main %}
+                    <tt>{{ thema_main }}</tt>
+                  {% endif %}
                   {% for t in thema %}
+                    {% if forloop.first and thema_main %}oraz: {% endif %}
                     <tt>{{ t }}</tt>
                     {% if not forloop.last %}, {% endif %}
                   {% endfor %}
+                  {% if not thema_main %}
+                    <span class="badge badge-secondary" title="Nie ustalono głównej kategorii Thema">&nbsp;*&nbsp;</small>
+                  {% endif %}
                   </small></button>
                 {% with llp=book.last_legimi_publish %}
                   {% if llp %}
                 {% endwith %}
               </form>
             {% else %}
-              <div class="alert alert-warning">Nie można opublikować na Legimi, ponieważ nie ustaiono kategorii Thema.</div>
+              <div class="alert alert-warning">Nie można opublikować na Legimi, ponieważ nie ustalono kategorii Thema.</div>
             {% endif %}
           {% endwith %}
         {% endif %}
index a930b8d..225ad55 100644 (file)
@@ -295,11 +295,13 @@ def book_epub(request, slug):
         return HttpResponseForbidden("Not authorized.")
 
     # TODO: move to celery
-    doc = book.wldocument()
+    doc = book.wldocument(librarian2=True)
     # TODO: error handling
 
-    #### Problemas: images in children.
-    epub = doc.as_epub(base_url='file://' + book.gallery_path() + '/').get_bytes()
+    from librarian.builders import EpubBuilder
+    epub = EpubBuilder(
+        base_url='file://' + book.gallery_path() + '/'
+    ).build(doc).get_bytes()
     response = HttpResponse(content_type='application/epub+zip')
     response['Content-Disposition'] = 'attachment; filename=%s' % book.slug + '.epub'
     response.write(epub)
@@ -314,9 +316,12 @@ def book_mobi(request, slug):
         return HttpResponseForbidden("Not authorized.")
 
     # TODO: move to celery
-    doc = book.wldocument()
+    doc = book.wldocument(librarian2=True)
     # TODO: error handling
-    mobi = doc.as_mobi(base_url='file://' + book.gallery_path() + '/').get_bytes()
+    from librarian.builders import MobiBuilder
+    mobi = MobiBuilder(
+        base_url='file://' + book.gallery_path() + '/'
+    ).build(doc).get_bytes()
     response = HttpResponse(content_type='application/x-mobipocket-ebook')
     response['Content-Disposition'] = 'attachment; filename=%s' % book.slug + '.mobi'
     response.write(mobi)
index a3fe432..9fa1c9d 100644 (file)
 
                 self.$pane.on('click', '.meta-delete', function() {
                     let $fg = $(this).closest('.form-group');
-                    $('input', $fg).data('edited').remove();
+                    let $ig = $(this).closest('.input-group');
+                    $('input', $ig).data('edited').remove();
                     self.displayMetaProperty($fg);
                     $.wiki.perspectives.VisualPerspective.flush();
                     return false;
index f1f5dae..2039569 100644 (file)
@@ -7,7 +7,7 @@ from librarian.dcparser import BookInfo
 from librarian.document import WLDocument
 from librarian.builders import StandaloneHtmlBuilder
 from librarian.meta.types.wluri import WLURI
-from librarian.meta.types.text import LegimiCategory, Epoch, Kind, Genre, Audience, ThemaCategory
+from librarian.meta.types.text import LegimiCategory, Epoch, Kind, Genre, Audience, ThemaCategory, MainThemaCategory
 from depot.legimi import legimi
 
 
@@ -50,11 +50,21 @@ VALUE_TYPES = {
         'widget': 'select',
         'options': [''] + list(legimi.CATEGORIES.keys()),
     },
+    Audience: {
+        'autocomplete': {
+            'source': '/catalogue/terms/audience/',
+        }
+    },
     ThemaCategory: {
         'autocomplete': {
             'source': '/catalogue/terms/thema/',
         }
     },
+    ThemaCategory: {
+        'autocomplete': {
+            'source': '/catalogue/terms/thema-main/',
+        }
+    },
     Epoch: {
         'autocomplete': {
             'source': '/catalogue/terms/epoch/',