X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/2d538a50605add2666172861744229599487f1b2..049c5959e33d29e4c0044c235d291144d49d3c45:/src/depot/publishers/woblink.py diff --git a/src/depot/publishers/woblink.py b/src/depot/publishers/woblink.py index 00dca5ec..3e1c78c8 100644 --- a/src/depot/publishers/woblink.py +++ b/src/depot/publishers/woblink.py @@ -8,7 +8,7 @@ from django.utils.html import escape, format_html from django.utils.safestring import mark_safe from librarian.builders.html import SnippetHtmlBuilder from librarian.functions import lang_code_3to2 -from catalogue.models import Author, Thema +from catalogue.models import Audience, Author, Thema from .. import models from .base import BasePublisher from .woblink_constants import WOBLINK_CATEGORIES @@ -20,8 +20,8 @@ class WoblinkError(ValueError): class NoPrice(WoblinkError): def as_html(self): return format_html( - 'Brak określonej ceny.', - price=self.args[0].id + 'Brak określonej ceny.', + site=self.args[0].id ) class NoIsbn(WoblinkError): @@ -124,11 +124,15 @@ class Woblink(BasePublisher): STEP1_URL = BASE_URL + 'catalog/edit/%s' STEP2_URL = BASE_URL + 'catalog/edit/%s/2' STEP3_URL = BASE_URL + 'catalog/edit/%s/3' + STEP4_URL = BASE_URL + 'catalog/edit/%s/4' + STEP5_URL = BASE_URL + 'catalog/edit/%s/5' UPLOAD_URL = BASE_URL + 'file/upload-%s' JOB_STATUS_URL = BASE_URL + 'task/status' GENERATE_DEMO_URL = BASE_URL + 'task/run/generate-%s-demo/%s/%d' CHECK_DEMO_URL = BASE_URL + 'task/run/check-%s-demo/%s' + SEARCH_CATALOGUE_URL = BASE_URL + '{category}/autocomplete/{term}' + ROLE_AUTHOR = 1 ROLE_TRANSLATOR = 4 @@ -148,6 +152,28 @@ class Woblink(BasePublisher): data=data, ) + def search_catalogue(self, category, term): + return self.session.get( + self.SEARCH_CATALOGUE_URL.format(category=category, term=term) + ).json() + + def search_author_catalogue(self, term): + return [ + { + 'id': item['autId'], + 'text': item['autFullname'] + } + for item in self.search_catalogue('author', term) + ] + def search_series_catalogue(self, term): + return [ + { + 'id': item['id'], + 'text': item['name'] + } + for item in self.search_catalogue('series', term) + ] + def get_isbn(self, meta, errors=None): if not meta.isbn_epub: if errors is not None: @@ -161,6 +187,7 @@ class Woblink(BasePublisher): (self.ROLE_TRANSLATOR, meta.translators, False) ]: for person_literal in items: + if person_literal is None: continue if person_literal.lang != 'pl': if errors is not None: if obligatory: @@ -194,6 +221,12 @@ class Woblink(BasePublisher): if errors is not None: errors.append(NoMainThemaWarning()) thema_codes.extend(meta.thema) + + thema_codes.extend( + Audience.objects.filter(code__in=meta.audiences).exclude( + thema='').values_list('thema', flat=True) + ) + if not thema_codes: if errors is not None: errors.append(NoThema()) @@ -219,25 +252,35 @@ class Woblink(BasePublisher): return category_ids def get_series(self, meta, errors=None): - pass + return list(Audience.objects.filter(code__in=meta.audiences).exclude( + woblink=None).values_list('woblink', flat=True)) def get_abstract(self, wldoc, errors=None, description_add=None): description = self.get_description(wldoc, description_add) parts = description.split('\n', 1) - if len(parts) == 1 or len(parts[0]) > 200: - p1 = description[:200].rsplit(' ', 1)[0] - p2 = description[len(p1):] - p1 += '…' - p2 = '…' + p2 + if len(parts) == 1 or len(parts[0]) > 240: + # No newline found here. + # Try to find last sentence end.. + parts = re.split(r' \.', description[240::-1], 1) + if len(parts) == 2: + p1 = parts[1][::-1] + '.' + p2 = description[len(p1) + 1:] + else: + # No sentence end found. + # Just find a space. + p1 = description[:240].rsplit(' ', 1)[0] + p2 = description[len(p1) + 1:] + p1 += '…' + p2 = '…' + p2 parts = [p1, p2] m = re.search(r'<[^>]+$', parts[0]) if m is not None: - parts[0] = parts[:-len(m.group(0))] + parts[0] = parts[0][:-len(m.group(0))] parts[1] = m.group(0) + parts[1] opened = [] - for tag in re.findall(r'<[^>]+[^/>]>', parts[0]): + for tag in re.findall(r'<[^>]*[^/>]>', parts[0]): if tag[1] == '/': opened.pop() else: @@ -253,26 +296,36 @@ class Woblink(BasePublisher): def get_lang2code(self, meta, errors=None): return lang_code_3to2(meta.language) - def get_price(self, shop, wldoc, errors=None): - stats = wldoc.get_statistics()['total'] + def get_price(self, site, wldoc, errors=None): + try: + stats = wldoc.get_statistics()['total'] + except: + if errors: + errors.append(NoPrice(site)) + return 0 words = stats['words_with_fn'] pages = stats['chars_with_fn'] / 1800 - price = shop.get_price(words, pages) + price = site.get_price(words, pages) if price is None: if errors: - errors.append(NoPrice(shop)) + errors.append(NoPrice(site)) return 0 return price - def can_publish(self, shop, book): - wldoc = book.wldocument(librarian2=True) + def can_publish(self, site, book): d = { 'warnings': [], 'errors': [], + 'info': [], } + try: + wldoc = book.wldocument(librarian2=True) + except: + d['errors'].append('Nieprawidłowy dokument.') + return d errors = [] - book_data = self.get_book_data(shop, wldoc, errors) + book_data = self.get_book_data(site, wldoc, errors) for error in errors: if not isinstance(error, Warning): errlist = d['errors'] @@ -280,12 +333,22 @@ class Woblink(BasePublisher): errlist = d['warnings'] errlist.append(error.as_html()) + if book_data.get('isbn'): + d['info'].append(format_html( + 'ISBN: {isbn}', + isbn=book_data['isbn'], + )) + if book_data.get('genres'): - d['comment'] = format_html( + d['info'].append(format_html( 'W kategoriach: {cat} ({price} zł)', cat=', '.join(self.describe_category(g) for g in book_data['genres']), - price=book_data['price'] - ) + price=book_data['price'], + )) + d['info'].append(mark_safe( + '' + book_data['abstract']['header'] + + '
' + book_data['abstract']['rest'] + )) return d @@ -313,44 +376,54 @@ class Woblink(BasePublisher): if m is not None: return m.group(1) - def send_book(self, shop, book, changes=None): + def send_book(self, site_book_publish, changes=None): + site_book = site_book_publish.site_book + book = site_book.book + site = site_book.site wldoc = book.wldocument(librarian2=True, changes=changes, publishable=False) # TODO pub meta = wldoc.meta - book_data = self.get_book_data(shop, wldoc) + book_data = self.get_book_data(site, wldoc) - if not book.woblink_id: - #book.woblink_id = 2959868 + if not site_book.external_id: woblink_id = self.create_book(book_data['isbn']) assert woblink_id - book.woblink_id = woblink_id - book.save(update_fields=['woblink_id']) - - self.edit_step1(book.woblink_id, book_data) - self.edit_step2(book.woblink_id, book_data) - self.edit_step3(book.woblink_id, book_data) - self.send_cover(book.woblink_id, wldoc) - texts = shop.get_texts() - self.send_epub( - book.woblink_id, wldoc, book.gallery_path(), + site_book.external_id = woblink_id + site_book.save(update_fields=['external_id']) + woblink_id = site_book.external_id + + self.edit_step1(woblink_id, book_data) + self.edit_step2(woblink_id, book_data) + self.edit_step3(woblink_id, book_data) + cover_id = self.send_cover(woblink_id, wldoc) + + texts = site.get_texts() + epub_id, epub_demo = self.send_epub( + woblink_id, wldoc, book.gallery_path(), fundraising=texts ) - self.send_mobi( - book.woblink_id, wldoc, book.gallery_path(), + mobi_id, mobi_demo = self.send_mobi( + woblink_id, wldoc, book.gallery_path(), fundraising=texts ) + self.edit_step4( + woblink_id, book_data, + cover_id, epub_id, epub_demo, mobi_id, mobi_demo, + ) + self.edit_step5(woblink_id, book_data) - def get_book_data(self, shop, wldoc, errors=None): + def get_book_data(self, site, wldoc, errors=None): return { "title": wldoc.meta.title, "isbn": self.get_isbn(wldoc.meta, errors=errors), "authors": self.get_authors_data(wldoc.meta, errors=errors), "abstract": self.get_abstract( - wldoc, errors=errors, description_add=shop.description_add + wldoc, errors=errors, description_add=site.description_add ), "lang2code": self.get_lang2code(wldoc.meta, errors=errors), "genres": self.get_genres(wldoc.meta, errors=errors), - "price": self.get_price(shop, wldoc, errors=errors), + "price": self.get_price(site, wldoc, errors=errors), + "series": self.get_series(wldoc.meta, errors=errors), } def with_form_name(self, data, name): @@ -371,6 +444,14 @@ class Woblink(BasePublisher): for (author_type, author_id) in data['authors'] ] + series_data = [ + { + 'PublicationId': woblink_id, + 'SeriesId': series_id, + } + for series_id in data['series'] + ] + d = { 'pubTitle': book_data['title'], 'npwAuthorHasPublications': json.dumps(authors_data), @@ -378,8 +459,7 @@ class Woblink(BasePublisher): 'pubNote': data['abstract']['rest'], 'pubCulture': data['lang2code'], 'npwPublicationHasAwards': '[]', - 'npwPublicationHasSeriess': '[]', # TODO - # "[{\"Id\":6153,\"PublicationId\":73876,\"SeriesId\":1615,\"Tome\":null}]" + 'npwPublicationHasSeriess': json.dumps(series_data), } d = self.with_form_name(d, 'EditPublicationStep1') d['roles'] = [author_type for (author_type, author_id) in data['authors']] @@ -402,8 +482,8 @@ class Woblink(BasePublisher): if legacy is None: legacy = WOBLINK_CATEGORIES[p].get('legacy') else: - gd.setdefault(p, {}) - ds[p]['isMain'] = True + gd.setdefault(g, {}) + gd[g]['isMain'] = True gd = [ { "pubId": woblink_id, @@ -423,15 +503,35 @@ class Woblink(BasePublisher): def edit_step3(self, woblink_id, book_data): d = { 'pubBasePrice': book_data['price'], - 'pubPremiereDate': '2023-08-09', #date.today().isoformat(), + 'pubPremiereDate': date.today().isoformat(), 'pubIsLicenseIndefinite': '1', 'pubFileFormat': 'epub+mobi', 'pubIsAcs': '0', 'pubPublisherIndex': '', + 'save_and_continue': '', } d = self.with_form_name(d, 'EditPublicationStep3') return self.session.post(self.STEP3_URL % woblink_id, data=d) + def edit_step4(self, woblink_id, book_data, cover_id, epub_id, epub_demo, mobi_id, mobi_demo): + d = { + 'pubCoverResId': cover_id, + 'pubEpubResId': epub_id, + 'pubEpubDemoResId': epub_demo, + 'pubMobiResId': mobi_id, + 'pubMobiDemoResId': mobi_demo, + 'pubFileFormat': 'epub+mobi', + 'pubId': woblink_id, + 'save_and_continue': '', + } + d = self.with_form_name(d, 'EditPublicationStep4') + return self.session.post(self.STEP4_URL % woblink_id, data=d) + + def edit_step5(self, woblink_id, book_data): + d = {'save': ''} + d = self.with_form_name(d, 'EditPublicationStep5') + return self.session.post(self.STEP5_URL % woblink_id, data=d) + def wait_for_job(self, job_id): while True: response = self.session.post( @@ -441,16 +541,21 @@ class Woblink(BasePublisher): data = response.json()[job_id] if data['ready']: assert data['successful'] - return + return data.get('returnValue') sleep(2) - def upload_file(self, woblink_id, filename, content, form_name, field_name, mime_type): + def upload_file(self, woblink_id, filename, content, field_name, mime_type): + form_name = f'Upload{field_name}' + id_field = f'pub{field_name}ResId' + field_name = field_name.lower() + data = { 'pubId': woblink_id, } files = { field_name: (filename, content, mime_type) } + response = self.session.post( self.UPLOAD_URL % field_name, data=self.with_form_name(data, form_name), @@ -458,8 +563,10 @@ class Woblink(BasePublisher): ) resp_data = response.json() assert resp_data['success'] is True + file_id = resp_data[id_field] if 'jobId' in resp_data: self.wait_for_job(resp_data['jobId']) + return file_id def generate_demo(self, woblink_id, file_format, check=True): percent = 10 @@ -468,7 +575,13 @@ class Woblink(BasePublisher): self.GENERATE_DEMO_URL % (file_format, woblink_id, percent), ).json()['jobId'] try: - self.wait_for_job(job_id) + file_id = self.wait_for_job(job_id) + if check: + self.wait_for_job( + self.session.get( + self.CHECK_DEMO_URL % (file_format, woblink_id) + ).json()['jobId'] + ) except AssertionError: if percent < 50: percent += 10 @@ -477,12 +590,7 @@ class Woblink(BasePublisher): else: break - if check: - self.wait_for_job( - self.session.get( - self.CHECK_DEMO_URL % (file_format, woblink_id) - ).json()['jobId'] - ) + return file_id def send_epub(self, woblink_id, doc, gallery_path, fundraising=None): from librarian.builders import EpubBuilder @@ -490,15 +598,15 @@ class Woblink(BasePublisher): base_url='file://' + gallery_path + '/', fundraising=fundraising or [], ).build(doc).get_file() - self.upload_file( + file_id = self.upload_file( woblink_id, doc.meta.url.slug + '.epub', content, - 'UploadEpub', - 'epub', + 'Epub', 'application/epub+zip' ) - self.generate_demo(woblink_id, 'epub') + demo_id = self.generate_demo(woblink_id, 'epub') + return file_id, demo_id def send_mobi(self, woblink_id, doc, gallery_path, fundraising=None): from librarian.builders import MobiBuilder @@ -506,15 +614,15 @@ class Woblink(BasePublisher): base_url='file://' + gallery_path + '/', fundraising=fundraising or [], ).build(doc).get_file() - self.upload_file( + file_id = self.upload_file( woblink_id, doc.meta.url.slug + '.mobi', content, - 'UploadMobi', - 'mobi', + 'Mobi', 'application/x-mobipocket-ebook' ) - self.generate_demo(woblink_id, 'mobi', check=False) + demo_id = self.generate_demo(woblink_id, 'mobi', check=False) + return file_id, demo_id def send_cover(self, woblink_id, doc): from librarian.cover import make_cover @@ -524,11 +632,11 @@ class Woblink(BasePublisher): content = io.BytesIO() cover.final_image().save(content, cover.format) content.seek(0) - self.upload_file( + file_id = self.upload_file( woblink_id, doc.meta.url.slug + '.jpeg', content, - 'UploadCover', - 'cover', + 'Cover', cover.mime_type() ) + return file_id