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 .publishers.legimi import Legimi
+from .publishers.woblink import Woblink
class Package(models.Model):
self.set_status(self.get_status())
except:
pass
-
+
try:
self.set_definition(self.get_definition())
except:
pass
super().save(*args, **kwargs)
-
+
def get_status(self):
return json.loads(self.status_json)
def set_status(self, status):
- self.status_json = json.dumps(status, indent=4)
+ self.status_json = json.dumps(status, indent=4, ensure_ascii=False)
def get_definition(self):
return json.loads(self.definition_json)
def set_definition(self, definition):
- self.definition_json = json.dumps(definition, indent=4)
+ self.definition_json = json.dumps(definition, indent=4, ensure_ascii=False)
def build(self):
f = tempfile.NamedTemporaryFile(prefix='depot-', suffix='.zip', mode='wb', delete=False)
+ book_count = self.books.all().count()
with zipfile.ZipFile(f, 'w') as z:
- for book in self.books.all():
+ for i, book in enumerate(self.books.all()):
+ print(f'{i}/{book_count} {book.slug}')
self.build_for(book, z)
f.close()
with open(f.name, 'rb') as ff:
output = EpubBuilder(
cover=cover,
base_url=base_url,
-# fundraising=[]
+ fundraising=item.get('fundraising', []),
).build(wldoc2)
elif item['type'] == 'mobi':
output = MobiBuilder(
cover=cover,
base_url=base_url,
+ fundraising=item.get('fundraising', []),
).build(wldoc2)
- fname = f'{slug}/{slug}.{ext}'
+ fname = f'{slug}/{slug}.'
+ if 'slug' in item:
+ fname += item['slug'] + '.'
+ fname += ext
z.writestr(
fname,
output.get_bytes()
)
+
+
+class SiteBook(models.Model):
+ site = models.ForeignKey('Site', models.SET_NULL, null=True)
+ book = models.ForeignKey('documents.Book', models.CASCADE)
+ external_id = models.CharField(max_length=255, blank=True)
+ created_at = models.DateTimeField(auto_now_add=True)
+
+ class Meta:
+ unique_together = (('book', 'site'),)
+
+ def __str__(self):
+ return f'{self.site} : {self.book} : {self.external_id}'
+
+
+class SiteBookPublish(models.Model):
+ site_book = models.ForeignKey(SiteBook, models.PROTECT, null=True, blank=True)
+ 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, site):
+ book.assert_publishable()
+ changes = book.get_current_changes(publishable=True)
+ site_book, created = SiteBook.objects.get_or_create(
+ site=site, book=book
+ )
+ me = cls.objects.create(
+ site_book=site_book, user=user, created_at=now())
+ for change in changes:
+ me.sitechunkpublish_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.sitechunkpublish_set.order_by('change__tree__number')
+ ]
+
+ self.site_book.site.publish(self, 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 SiteChunkPublish(models.Model):
+ book_publish = models.ForeignKey(SiteBookPublish, models.CASCADE)
+ change = models.ForeignKey('documents.ChunkChange', models.CASCADE)
+
+
+class Site(models.Model):
+ name = models.CharField(max_length=255)
+ site_type = models.CharField(max_length=32, choices=[
+ ('legimi', 'Legimi'),
+ ('woblink', 'Woblink'),
+ ])
+ username = models.CharField(max_length=255)
+ password = models.CharField(max_length=255)
+ publisher_handle = models.CharField(max_length=255, blank=True)
+ description_add = models.TextField(blank=True)
+
+ def __str__(self):
+ return self.name
+
+ def get_texts(self):
+ return [t.text for t in self.mediainserttext_set.all()]
+
+ def get_price(self, words, pages):
+ price_obj = self.pricelevel_set.exclude(
+ min_pages__gt=pages
+ ).exclude(
+ min_words__gt=words
+ ).order_by('-price').first()
+ if price_obj is None:
+ return None
+ return price_obj.price
+
+ def get_publisher(self):
+ if self.site_type == 'legimi':
+ pub_class = Legimi
+ elif self.site_type == 'woblink':
+ pub_class = Woblink
+ return pub_class(self.username, self.password, self.publisher_handle)
+
+ def publish(self, site_book_publish, changes):
+ self.get_publisher().send_book(
+ site_book_publish,
+ changes=changes,
+ )
+
+ def can_publish(self, book):
+ return self.get_publisher().can_publish(self, book)
+
+ def get_last(self, book):
+ return SiteBookPublish.objects.filter(
+ site_book__site=self, site_book__book=book
+ ).order_by('-created_at').first()
+
+ def get_external_id_for_book(self, book):
+ site_book = self.sitebook_set.filter(book=book).first()
+ return (site_book and site_book.external_id) or ''
+
+class PriceLevel(models.Model):
+ site = models.ForeignKey(Site, models.CASCADE)
+ min_pages = models.IntegerField(null=True, blank=True)
+ min_words = models.IntegerField(null=True, blank=True)
+ price = models.IntegerField()
+
+ class Meta:
+ ordering = ('price',)
+
+
+class MediaInsertText(models.Model):
+ site = models.ForeignKey(Site, models.CASCADE)
+ ordering = models.IntegerField()
+ text = models.TextField()
+
+ class Meta:
+ ordering = ('ordering',)