Allow multiple sources.
authorRadek Czajka <rczajka@rczajka.pl>
Tue, 28 May 2024 12:22:44 +0000 (14:22 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Tue, 28 May 2024 12:22:44 +0000 (14:22 +0200)
src/catalogue/models.py
src/catalogue/templates/catalogue/book_detail.html
src/sources/document.py
src/sources/migrations/0004_alter_booksource_options_booksource_ordering.py [new file with mode: 0644]
src/sources/models.py
src/sources/urls.py
src/sources/views.py
src/wiki/templates/wiki/document_details_base.html
src/wiki/urls.py
src/wiki/views.py

index f492809..be7c37f 100644 (file)
@@ -384,11 +384,8 @@ class Book(WikidataModel):
             for work_type in WorkType.objects.all()
         }
 
-    def scans_gallery(self):
-        bs = self.booksource_set.first()
-        if bs is None:
-            return ''
-        return bs.pk
+    def scans_galleries(self):
+        return [bs.pk for bs in self.booksource_set.all()]
 
     def is_published(self):
         return any(b.is_published() for b in self.document_books.all())
@@ -429,6 +426,15 @@ class Book(WikidataModel):
         self._content_stats = stats
         return stats
 
+    @property
+    def are_sources_ready(self):
+        if not self.booksource_set.exists():
+            return False
+        for bs in self.booksource_set.all():
+            if not bs.source.has_view_files() or not bs.source.has_ocr_files() or bs.source.modified_at > bs.source.processed_at:
+                return False
+        return True
+
     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', '')
index 37cbb66..331ba84 100644 (file)
       Źródło
     </div>
     <div class="card-body">
-      {% for bs in book.booksource_set.all %}
-        <a href="{{ bs.source.get_absolute_url }}">
-          {{ bs.source }}
-        </a>
-        {% if bs.page_start or bs.page_end %}
-          (strony {{ bs.page_start }}—{{ bs.page_end }})
-        {% else %}
-          <small>
-            (źródło w całości przypisane do utworu <em>{{ book }}</em>)
-          </small>
-        {% endif %}
-
-        {% if not bs.source.has_upload_files %}
-
-          <div class="alert alert-info">
-            Źródło nie ma jeszcze załadowanych skanów.
-            <a href="{% url 'source_upload' bs.source.id %}">
-              Załaduj skany</a>.
-          </div>
+      <table class="table">
+        <tbody>
+          {% for bs in book.booksource_set.all %}
+            <tr>
+              <td>
+                <a href="{{ bs.source.get_absolute_url }}">
+                  {{ bs.source }}
+                </a>
+              </td>
+              <td>
+                {% if bs.page_start or bs.page_end %}
+                  (strony {{ bs.page_start }}—{{ bs.page_end }})
+                {% else %}
+                  <small>
+                    (źródło w całości przypisane do utworu <em>{{ book }}</em>)
+                  </small>
+                {% endif %}
+              </td>
+              <td>
+                {% if not bs.source.has_upload_files %}
+                  <div class="alert alert-info">
+                    Źródło nie ma jeszcze załadowanych skanów.
+                    <a href="{% url 'source_upload' bs.source.id %}">
+                      Załaduj skany</a>.
+                  </div>
+                {% elif not bs.source.has_view_files %}
+                  <div class="alert alert-info">
+                    Trwa generowanie podglądu.
+                  </div>
+                {% elif not bs.source.has_ocr_files %}
+                  <div class="alert alert-info">
+                    Trwa OCR.
+                  </div>
+                {% elif bs.source.modified_at > bs.source.processed_at %}
+                  <div class="alert alert-info">
+                    Zmodyfikowano skany, trwa aktualizacja.
+                  </div>
+                {% endif %}
+              </td>
+            </tr>
+          {% empty %}
+            Brak źródła.
+            Możesz je dodać.
+          {% endfor %}
+        </tbody>
+      </table>
 
-        {% elif not bs.source.has_view_files %}
-          <div class="alert alert-info">
-            Trwa generowanie podglądu.
-          </div>
-        {% elif not bs.source.has_ocr_files %}
-          <div class="alert alert-info">
-            Trwa OCR.
-          </div>
-        {% elif bs.source.modified_at > bs.source.processed_at %}
-          <div class="alert alert-info">
-            Zmodyfikowano skany, trwa aktualizacja.
-          </div>
-        {% else %}
-          <form method="post" action="{% url 'source_book_prepare' bs.pk %}">
-            {% csrf_token %}
-            <button class="btn btn-primary">Utwórz tekst książki</button>
-          </form>
-        {% endif %}
-      {% empty %}
-        Brak źródła.
-        Możesz je dodać.
-      {% endfor %}
+      <form method="post" action="{% url 'source_book_prepare' book.pk %}">
+        {% csrf_token %}
+        <button class="btn btn-primary"
+                {% if not book.are_sources_ready %}disabled="disabled"{% endif %}
+        >Utwórz tekst książki</button>
+      </form>
     </div>
   </div>
 
index 876bda9..7c31110 100644 (file)
@@ -6,19 +6,20 @@ from . import ocr
 from django.conf import settings
 
 
-def build_document_texts(book_source):
+def build_document_texts(book):
     texts = []
     for builder in text_builders:
         root = etree.Element('utwor')
         # add meta
-        add_rdf(root, book_source)
+        add_rdf(root, book)
 
         # add master
         master = etree.SubElement(root, 'powiesc')
 
-        for page in book_source.get_ocr_files():
-            builder(master, page)
-    
+        for book_source in book.booksource_set.all():
+            for page in book_source.get_ocr_files():
+                builder(master, page)
+
         texts.append(etree.tostring(root, encoding='unicode', pretty_print=True))
     return texts
 
@@ -30,9 +31,7 @@ text_builders = [
 ]
 
 
-def add_rdf(root, book_source):
-    book = book_source.book
-    
+def add_rdf(root, book):
     # TODO: to librarian
     rdf = etree.SubElement(root, RDFNS('RDF'))
     desc = etree.SubElement(rdf, RDFNS('Description'), **{})
@@ -55,7 +54,11 @@ def add_rdf(root, book_source):
     etree.SubElement(desc, DCNS('language')).text = book.language # 3to2?
     #description
     #source_name
-    etree.SubElement(desc, DCNS('source')).text = book_source.source.name
+    # TODO: allow multiple source meta entries.
+    sources = []
+    for book_source in book.booksource_set.all():
+        sources.append(book_source.source.name)
+    etree.SubElement(desc, DCNS('source')).text = ';\n '.join(sources)
     #url
     etree.SubElement(desc, DCNS('identifier.url')).text = f'https://wolnelektury.pl/katalog/lektura/{book.slug}/'
     #license?
diff --git a/src/sources/migrations/0004_alter_booksource_options_booksource_ordering.py b/src/sources/migrations/0004_alter_booksource_options_booksource_ordering.py
new file mode 100644 (file)
index 0000000..a47c0d0
--- /dev/null
@@ -0,0 +1,22 @@
+# Generated by Django 4.1.9 on 2024-05-22 15:26
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("sources", "0003_source_modified_at_source_processed_at"),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name="booksource",
+            options={"ordering": ("ordering", "page_start")},
+        ),
+        migrations.AddField(
+            model_name="booksource",
+            name="ordering",
+            field=models.IntegerField(default=1),
+        ),
+    ]
index efa086a..dd521da 100644 (file)
@@ -119,11 +119,12 @@ class Source(models.Model):
 class BookSource(models.Model):
     book = models.ForeignKey('catalogue.Book', models.CASCADE)
     source = models.ForeignKey(Source, models.CASCADE)
+    ordering = models.IntegerField(default=1)
     page_start = models.IntegerField(null=True, blank=True)
     page_end = models.IntegerField(null=True, blank=True)
         
     class Meta:
-        ordering = ('page_start',)
+        ordering = ('ordering', 'page_start',)
 
     def __str__(self):
         return f'{self.source} -> {self.book}'
@@ -151,16 +152,17 @@ class BookSource(models.Model):
 
     def get_document(self):
         return self.book.document_books.first()
-        
-    def prepare_document(self, user=None):
+
+    @classmethod
+    def prepare_document(cls, book, user=None):
         DBook = apps.get_model('documents', 'Book')
-        texts = document.build_document_texts(self)
+        texts = document.build_document_texts(book)
 
-        dbook = self.get_document()
+        dbook = book.document_books.first()
         if dbook is None:
             dbook = DBook.create(
                 user, texts[0],
-                title=self.book.title,
+                title=book.title,
                 slug=str(uuid.uuid4()),
             )
         else:
index 49e2763..de52d04 100644 (file)
@@ -5,5 +5,5 @@ from . import views
 urlpatterns = [
     path('source/<int:pk>/', views.SourceView.as_view(), name='source'),
     path('upload/<int:sid>/', views.SourceUploadView.as_view(), name='source_upload'),
-    path('prepare/<int:bsid>/', views.prepare, name='source_book_prepare'),
+    path('prepare/book/<int:pk>/', views.prepare, name='source_book_prepare'),
 ]
index 2faaece..663ebac 100644 (file)
@@ -3,6 +3,7 @@ from django.shortcuts import render, get_object_or_404, redirect
 from django.utils.translation import gettext as _
 from django.views.generic import DetailView
 from fileupload.views import UploadView
+import catalogue.models
 from . import models
 
 
@@ -39,17 +40,11 @@ class SourceUploadView(UploadView):
         return response
 
 
-def prepare(request, bsid):
-    bs = get_object_or_404(models.BookSource, id=bsid)
+def prepare(request, pk):
+    book = get_object_or_404(catalogue.models.Book, id=pk)
 
     if request.POST:
-        dbook = bs.prepare_document(request.user)
+        dbook = models.BookSource.prepare_document(book, request.user)
         return redirect('wiki_editor', dbook.slug, dbook[0].slug)
     else:
-        return render(
-            request,
-            'sources/prepare.html',
-            {
-                'book_source': bs,
-            }
-        )
+        return redirect(book.get_absolute_url())
index a7009c5..1906f01 100644 (file)
@@ -26,7 +26,7 @@
        data-chunk-id="{{ chunk.pk }}" style="display:none">
 
     <span data-key="book-slug">{{ chunk.book.slug }}</span>
-    <span data-key="scans">{{ chunk.book.catalogue_book.scans_gallery }}</span>
+    <span data-key="scans">{{ chunk.book.catalogue_book.scans_galleries|join:',' }}</span>
     <span data-key="gallery">{{ chunk.book.gallery }}</span>
     <span data-key="gallery-start">{% if chunk.gallery_start %}{{ chunk.gallery_start }}{% endif %}</span>
     <span data-key="revision">{{ revision }}</span>
index bf7f5b8..9940dc8 100644 (file)
@@ -15,7 +15,7 @@ urlpatterns = [
          views.editor_readonly, name="wiki_editor_readonly"),
 
     path('gallery/<directory>/', views.gallery, name="wiki_gallery"),
-    path('scans/<int:pk>/', views.scans_list, name="wiki_scans"),
+    path('scans/<str:pks>/', views.scans_list, name="wiki_scans"),
     path('history/<int:chunk_id>/', views.history, name="wiki_history"),
     path('rev/<int:chunk_id>/', views.revision, name="wiki_revision"),
     path('text/<int:chunk_id>/', views.text, name="wiki_text"),
index e22c028..47f41f1 100644 (file)
@@ -271,15 +271,21 @@ def gallery(request, directory):
 
 
 @never_cache
-def scans_list(request, pk):
-    bs = get_object_or_404(sources.models.BookSource, pk=pk)
+def scans_list(request, pks):
+    pks = pks.split(',')
+    bss = [
+        get_object_or_404(sources.models.BookSource, pk=pk)
+        for pk in pks
+    ]
     def map_to_url(filename):
         return quote(("%s/%s" % (settings.MEDIA_URL, filename)))
-    images = [
-        {
-            "url": map_to_url(f),
-        } for f in bs.get_view_files()
-    ]
+    images = []
+    for bs in bss:
+        images.extend([
+            {
+                "url": map_to_url(f),
+            } for f in bs.get_view_files()
+        ])
     return JSONResponse(images)