Fix
[redakcja.git] / src / depot / publishers / woblink.py
index 00dca5e..0cd7a89 100644 (file)
@@ -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 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
 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(
 class NoPrice(WoblinkError):
     def as_html(self):
         return format_html(
-            'Brak <a href="/admin/depot/shop/{price}">określonej ceny</a>.',
-            price=self.args[0].id
+            'Brak <a href="/admin/depot/site/{site}">określonej ceny</a>.',
+            site=self.args[0].id
         )
 
 class NoIsbn(WoblinkError):
         )
 
 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'
     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'
 
     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
 
     ROLE_AUTHOR = 1
     ROLE_TRANSLATOR = 4
 
@@ -148,6 +152,28 @@ class Woblink(BasePublisher):
             data=data,
         )
 
             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:
     def get_isbn(self, meta, errors=None):
         if not meta.isbn_epub:
             if errors is not None:
@@ -194,6 +220,12 @@ class Woblink(BasePublisher):
             if errors is not None:
                 errors.append(NoMainThemaWarning())
         thema_codes.extend(meta.thema)
             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())
         if not thema_codes:
             if errors is not None:
                 errors.append(NoThema())
@@ -219,25 +251,35 @@ class Woblink(BasePublisher):
         return category_ids
 
     def get_series(self, meta, errors=None):
         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)
 
     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 = [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 = []
             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:
             if tag[1] == '/':
                 opened.pop()
             else:
@@ -253,26 +295,36 @@ class Woblink(BasePublisher):
     def get_lang2code(self, meta, errors=None):
         return lang_code_3to2(meta.language)
 
     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
         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:
         if price is None:
             if errors:
-                errors.append(NoPrice(shop))
+                errors.append(NoPrice(site))
             return 0
 
         return price
 
             return 0
 
         return price
 
-    def can_publish(self, shop, book):
-        wldoc = book.wldocument(librarian2=True)
+    def can_publish(self, site, book):
         d = {
             'warnings': [],
             'errors': [],
         d = {
             'warnings': [],
             'errors': [],
+            'info': [],
         }
         }
+        try:
+            wldoc = book.wldocument(librarian2=True)
+        except:
+            d['errors'].append('Nieprawidłowy dokument.')
+            return d
         errors = []
         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']
         for error in errors:
             if not isinstance(error, Warning):
                 errlist = d['errors']
@@ -281,11 +333,15 @@ class Woblink(BasePublisher):
             errlist.append(error.as_html())
 
         if book_data.get('genres'):
             errlist.append(error.as_html())
 
         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']),
                 '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(
+            '<strong>' + book_data['abstract']['header'] +
+            '</strong><br/>' + book_data['abstract']['rest']
+        ))
 
         return d
 
 
         return d
 
@@ -313,44 +369,54 @@ class Woblink(BasePublisher):
         if m is not None:
             return m.group(1)
 
         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
 
         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
             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
         )
             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
         )
             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(
         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),
             ),
             "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):
         }
 
     def with_form_name(self, data, name):
@@ -371,6 +437,14 @@ class Woblink(BasePublisher):
             for (author_type, author_id) in data['authors']
         ]
 
             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),
         d = {
             'pubTitle': book_data['title'],
             'npwAuthorHasPublications': json.dumps(authors_data),
@@ -378,8 +452,7 @@ class Woblink(BasePublisher):
             'pubNote': data['abstract']['rest'],
             'pubCulture': data['lang2code'],
             'npwPublicationHasAwards': '[]',
             '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']]
         }
         d = self.with_form_name(d, 'EditPublicationStep1')
         d['roles'] = [author_type for (author_type, author_id) in data['authors']]
@@ -402,8 +475,8 @@ class Woblink(BasePublisher):
                 if legacy is None:
                     legacy = WOBLINK_CATEGORIES[p].get('legacy')
             else:
                 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,
         gd = [
             {
                 "pubId": woblink_id,
@@ -423,15 +496,35 @@ class Woblink(BasePublisher):
     def edit_step3(self, woblink_id, book_data):
         d = {
             'pubBasePrice': book_data['price'],
     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': '',
             '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)
 
         }
         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(
     def wait_for_job(self, job_id):
         while True:
             response = self.session.post(
@@ -441,16 +534,21 @@ class Woblink(BasePublisher):
             data = response.json()[job_id]
             if data['ready']:
                 assert data['successful']
             data = response.json()[job_id]
             if data['ready']:
                 assert data['successful']
-                return
+                return data.get('returnValue')
             sleep(2)
 
             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)
         }
         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),
         response = self.session.post(
             self.UPLOAD_URL % field_name,
             data=self.with_form_name(data, form_name),
@@ -458,8 +556,10 @@ class Woblink(BasePublisher):
         )
         resp_data = response.json()
         assert resp_data['success'] is True
         )
         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'])
         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
 
     def generate_demo(self, woblink_id, file_format, check=True):
         percent = 10
@@ -468,7 +568,13 @@ class Woblink(BasePublisher):
                 self.GENERATE_DEMO_URL % (file_format, woblink_id, percent),
             ).json()['jobId']
             try:
                 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
             except AssertionError:
                 if percent < 50:
                     percent += 10
@@ -477,12 +583,7 @@ class Woblink(BasePublisher):
             else:
                 break
 
             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
 
     def send_epub(self, woblink_id, doc, gallery_path, fundraising=None):
         from librarian.builders import EpubBuilder
@@ -490,15 +591,15 @@ class Woblink(BasePublisher):
             base_url='file://' + gallery_path + '/',
             fundraising=fundraising or [],
         ).build(doc).get_file()
             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,
             woblink_id,
             doc.meta.url.slug + '.epub',
             content,
-            'UploadEpub',
-            'epub',
+            'Epub',
             'application/epub+zip'
         )
             '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
 
     def send_mobi(self, woblink_id, doc, gallery_path, fundraising=None):
         from librarian.builders import MobiBuilder
@@ -506,15 +607,15 @@ class Woblink(BasePublisher):
             base_url='file://' + gallery_path + '/',
             fundraising=fundraising or [],
         ).build(doc).get_file()
             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,
             woblink_id,
             doc.meta.url.slug + '.mobi',
             content,
-            'UploadMobi',
-            'mobi',
+            'Mobi',
             'application/x-mobipocket-ebook'
         )
             '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
 
     def send_cover(self, woblink_id, doc):
         from librarian.cover import make_cover
@@ -524,11 +625,11 @@ class Woblink(BasePublisher):
         content = io.BytesIO()
         cover.final_image().save(content, cover.format)
         content.seek(0)
         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,
             woblink_id,
             doc.meta.url.slug + '.jpeg',
             content,
-            'UploadCover',
-            'cover',
+            'Cover',
             cover.mime_type()
         )
             cover.mime_type()
         )
+        return file_id