6 from datetime import datetime
7 from django.conf import settings
8 from django.db import models
9 from django.utils.timezone import now
10 from librarian.cover import make_cover
11 from librarian.builders import EpubBuilder, MobiBuilder
12 from .legimi import legimi
15 class Package(models.Model):
16 created_at = models.DateTimeField(auto_now_add=True)
17 placed_at = models.DateTimeField(null=True, blank=True)
18 finished_at = models.DateTimeField(null=True, blank=True)
19 definition_json = models.TextField(blank=True)
20 books = models.ManyToManyField('documents.Book')
21 status_json = models.TextField(blank=True)
22 logo = models.FileField(blank=True, upload_to='depot/logo')
23 file = models.FileField(blank=True, upload_to='depot/package/')
25 def save(self, *args, **kwargs):
27 self.set_status(self.get_status())
32 self.set_definition(self.get_definition())
36 super().save(*args, **kwargs)
39 return json.loads(self.status_json)
41 def set_status(self, status):
42 self.status_json = json.dumps(status, indent=4, ensure_ascii=False)
44 def get_definition(self):
45 return json.loads(self.definition_json)
47 def set_definition(self, definition):
48 self.definition_json = json.dumps(definition, indent=4, ensure_ascii=False)
51 f = tempfile.NamedTemporaryFile(prefix='depot-', suffix='.zip', mode='wb', delete=False)
52 book_count = self.books.all().count()
53 with zipfile.ZipFile(f, 'w') as z:
54 for i, book in enumerate(self.books.all()):
55 print(f'{i}/{book_count} {book.slug}')
56 self.build_for(book, z)
58 with open(f.name, 'rb') as ff:
59 self.file.save('package-{}.zip'.format(datetime.now().isoformat(timespec='seconds')), ff)
62 def build_for(self, book, z):
63 wldoc2 = book.wldocument(librarian2=True)
64 slug = wldoc2.meta.url.slug
65 for item in self.get_definition():
66 wldoc = book.wldocument()
67 wldoc2 = book.wldocument(librarian2=True)
68 base_url = 'file://' + book.gallery_path() + '/'
72 if item['type'] == 'cover':
75 kwargs['cover_logo'] = self.logo.path
76 for k in 'format', 'width', 'height', 'cover_class':
79 cover = make_cover(wldoc.book_info, **kwargs)
80 output = cover.output_file()
83 elif item['type'] == 'pdf':
85 if 'cover_class' in item:
86 cover_kwargs['cover_class'] = item['cover_class']
88 cover_kwargs['cover_logo'] = self.logo.path
89 cover = lambda *args, **kwargs: make_cover(*args, **kwargs, **cover_kwargs)
90 output = wldoc.as_pdf(cover=cover, base_url=base_url)
92 elif item['type'] == 'epub':
94 if 'cover_class' in item:
95 cover_kwargs['cover_class'] = item['cover_class']
97 cover_kwargs['cover_logo'] = self.logo.path
98 cover = lambda *args, **kwargs: make_cover(*args, **kwargs, **cover_kwargs)
100 output = EpubBuilder(
103 fundraising=item.get('fundraising', []),
106 elif item['type'] == 'mobi':
107 output = MobiBuilder(
110 fundraising=item.get('fundraising', []),
113 fname = f'{slug}/{slug}.'
115 fname += item['slug'] + '.'
124 class LegimiBookPublish(models.Model):
125 book = models.ForeignKey('documents.Book', models.CASCADE)
126 user = models.ForeignKey(settings.AUTH_USER_MODEL, models.SET_NULL, null=True)
127 created_at = models.DateTimeField()
128 started_at = models.DateTimeField(null=True, blank=True)
129 finished_at = models.DateTimeField(null=True, blank=True)
130 status = models.PositiveSmallIntegerField(choices=[
136 error = models.TextField(blank=True)
139 def create_for(cls, book, user):
140 book.assert_publishable()
141 changes = book.get_current_changes(publishable=True)
142 me = cls.objects.create(book=book, user=user, created_at=now())
143 for change in changes:
144 me.legimichunkpublish_set.create(change=change)
149 self.started_at = now()
150 self.save(update_fields=['status', 'started_at'])
154 self.legimichunkpublish_set.order_by('change__chunk__number')
156 legimi.send_book(self.book, changes=changes)
159 self.error = traceback.format_exc()
163 self.finished_at = now()
164 self.save(update_fields=['status', 'finished_at', 'error'])
167 class LegimiChunkPublish(models.Model):
168 book_publish = models.ForeignKey(LegimiBookPublish, models.CASCADE)
169 change = models.ForeignKey('documents.ChunkChange', models.CASCADE)