From db255ed9d242c56b010061fbdef8de9b696869d4 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Thu, 14 Jul 2022 11:28:28 +0200 Subject: [PATCH] Legimi button! --- src/depot/legimi.py | 4 +- src/depot/management/commands/depot.py | 10 ++++ ...02_legimibookpublish_legimichunkpublish.py | 38 ++++++++++++++ src/depot/models.py | 52 +++++++++++++++++++ src/depot/urls.py | 11 ++++ src/depot/views.py | 18 ++++++- src/documents/models/book.py | 3 ++ .../templates/documents/book_detail.html | 29 +++++++++-- src/redakcja/urls.py | 1 + 9 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 src/depot/management/commands/depot.py create mode 100644 src/depot/migrations/0002_legimibookpublish_legimichunkpublish.py create mode 100644 src/depot/urls.py diff --git a/src/depot/legimi.py b/src/depot/legimi.py index 99f70624..1810ec57 100644 --- a/src/depot/legimi.py +++ b/src/depot/legimi.py @@ -175,8 +175,8 @@ class Legimi: # success: true # model.Url - def send_book(self, book): - wlbook = book.wldocument(librarian2=True) + def send_book(self, book, changes=None): + wlbook = book.wldocument(librarian2=True, changes=changes) meta = wlbook.meta cover = LabelMarquiseCover(meta, width=1200).output_file() diff --git a/src/depot/management/commands/depot.py b/src/depot/management/commands/depot.py new file mode 100644 index 00000000..4b8b99af --- /dev/null +++ b/src/depot/management/commands/depot.py @@ -0,0 +1,10 @@ +from django.core.management.base import BaseCommand +from depot.models import LegimiBookPublish + + +class Command(BaseCommand): + def handle(self, **options): + for p in LegimiBookPublish.objects.filter(status=0).order_by('created_at'): + print(p, p.book.slug, p.created_at) + p.publish() + diff --git a/src/depot/migrations/0002_legimibookpublish_legimichunkpublish.py b/src/depot/migrations/0002_legimibookpublish_legimichunkpublish.py new file mode 100644 index 00000000..38266060 --- /dev/null +++ b/src/depot/migrations/0002_legimibookpublish_legimichunkpublish.py @@ -0,0 +1,38 @@ +# Generated by Django 3.2.12 on 2022-07-14 10:36 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('documents', '0008_book_legimi_id'), + ('depot', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='LegimiBookPublish', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField()), + ('started_at', models.DateTimeField(blank=True, null=True)), + ('finished_at', models.DateTimeField(blank=True, null=True)), + ('status', models.PositiveSmallIntegerField(choices=[(0, 'queued'), (10, 'running'), (100, 'done'), (110, 'error')], default=0)), + ('error', models.TextField(blank=True)), + ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='documents.book')), + ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='LegimiChunkPublish', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('book_publish', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='depot.legimibookpublish')), + ('change', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='documents.chunkchange')), + ], + ), + ] diff --git a/src/depot/models.py b/src/depot/models.py index e3b6dfd8..b68b5365 100644 --- a/src/depot/models.py +++ b/src/depot/models.py @@ -1,11 +1,15 @@ import json import os import tempfile +import traceback import zipfile from datetime import datetime +from django.conf import settings from django.db import models +from django.utils.timezone import now from librarian.cover import make_cover from librarian.builders import EpubBuilder, MobiBuilder +from .legimi import legimi class Package(models.Model): @@ -115,3 +119,51 @@ class Package(models.Model): fname, output.get_bytes() ) + + +class LegimiBookPublish(models.Model): + book = models.ForeignKey('documents.Book', models.CASCADE) + user = models.ForeignKey(settings.AUTH_USER_MODEL, models.SET_NULL, null=True) + created_at = models.DateTimeField() + started_at = models.DateTimeField(null=True, blank=True) + finished_at = models.DateTimeField(null=True, blank=True) + status = models.PositiveSmallIntegerField(choices=[ + (0, 'queued'), + (10, 'running'), + (100, 'done'), + (110, 'error'), + ], default=0) + error = models.TextField(blank=True) + + @classmethod + def create_for(cls, book, user): + book.assert_publishable() + changes = book.get_current_changes(publishable=True) + me = cls.objects.create(book=book, user=user, created_at=now()) + for change in changes: + me.legimichunkpublish_set.create(change=change) + return me + + def publish(self): + self.status = 10 + self.started_at = now() + self.save(update_fields=['status', 'started_at']) + try: + changes = [ + p.change for p in + self.legimichunkpublish_set.order_by('change__chunk__number') + ] + legimi.send_book(self.book, changes=changes) + except Exception: + self.status = 110 + self.error = traceback.format_exc() + else: + self.status = 100 + self.error = '' + self.finished_at = now() + self.save(update_fields=['status', 'finished_at', 'error']) + + +class LegimiChunkPublish(models.Model): + book_publish = models.ForeignKey(LegimiBookPublish, models.CASCADE) + change = models.ForeignKey('documents.ChunkChange', models.CASCADE) diff --git a/src/depot/urls.py b/src/depot/urls.py new file mode 100644 index 00000000..98045158 --- /dev/null +++ b/src/depot/urls.py @@ -0,0 +1,11 @@ +from django.urls import path +from . import views + + +urlpatterns = [ + path( + 'legimi-publish//', + views.LegimiPublishView.as_view(), + name='depot_legimi_publish' + ) +] diff --git a/src/depot/views.py b/src/depot/views.py index 91ea44a2..5e468b67 100644 --- a/src/depot/views.py +++ b/src/depot/views.py @@ -1,3 +1,17 @@ -from django.shortcuts import render +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.shortcuts import get_object_or_404, redirect +from django.views import View +from documents.models import Book +from . import models -# Create your views here. + +class LegimiPublishView(PermissionRequiredMixin, View): + permission_required = 'depot.add_legimibookpublish' + + def post(self, request, book_id): + book = get_object_or_404(Book, pk=book_id) + try: + publish = models.LegimiBookPublish.create_for(book, request.user) + except AssertionError: + pass + return redirect(book.get_absolute_url()) diff --git a/src/documents/models/book.py b/src/documents/models/book.py index 41e0bcd6..1580f74a 100644 --- a/src/documents/models/book.py +++ b/src/documents/models/book.py @@ -278,6 +278,9 @@ class Book(models.Model): except IndexError: return None + def last_legimi_publish(self): + return self.legimibookpublish_set.order_by('-created_at').first() + def assert_publishable(self): assert self.chunk_set.exists(), _('No chunks in the book.') try: diff --git a/src/documents/templates/documents/book_detail.html b/src/documents/templates/documents/book_detail.html index 77377542..f89e1e76 100644 --- a/src/documents/templates/documents/book_detail.html +++ b/src/documents/templates/documents/book_detail.html @@ -134,9 +134,27 @@ {{ publish_options_form.as_p }} + {% trans "Publish" %} - + + + {% if perms.depot.add_legimibookpublish %} +
+
+ {% csrf_token %} + + {% with llp=book.last_legimi_publish %} + {% if llp %} + {{ llp.created_at }} → + {{ llp.started_at }} → + {{ llp.finished_at }} + ({{ llp.get_status_display }}) + + {% endif %} + {% endwith %} +
+ {% endif %} + {% else %} {% trans "Log in to publish." %} {% endif %} @@ -145,11 +163,14 @@ {% endif %} - + + + + {% if doc %}
@@ -169,7 +190,7 @@ - {% with stats=book.wldocument.get_statistics %} + {% with stats=doc.get_statistics %} {% include 'documents/book_stats.html' with book=book stats=stats depth=0 %} {% endwith %} diff --git a/src/redakcja/urls.py b/src/redakcja/urls.py index 6e607305..2f36af18 100644 --- a/src/redakcja/urls.py +++ b/src/redakcja/urls.py @@ -26,6 +26,7 @@ urlpatterns = [ url(r'^editor/', include('wiki.urls')), url(r'^images/', include('wiki_img.urls')), url(r'^cover/', include('cover.urls')), + url(r'^depot/', include('depot.urls')), url(r'^wlxml/', include('wlxml.urls')), path('api/', include('redakcja.api.urls')), -- 2.20.1