From 46705d393ae76a1d8e0eb2dc8bf121269f6adb67 Mon Sep 17 00:00:00 2001 From: Marcin Koziej Date: Wed, 5 Oct 2011 16:54:25 +0200 Subject: [PATCH 1/1] #1242 generowanie pdf przy imporcie xml --- .../management/commands/importbooks.py | 5 +- apps/catalogue/models.py | 65 +++++++++++------- apps/catalogue/test_utils.py | 2 +- apps/catalogue/tests/book_import.py | 41 +++++++++++- .../but-w-butonierce-but-w-butonierce.xml | 67 +++++++++++++++++++ apps/catalogue/tests/fraszka-do-anusie.xml | 49 ++++++++++++++ apps/catalogue/utils.py | 15 +++++ 7 files changed, 217 insertions(+), 27 deletions(-) create mode 100755 apps/catalogue/tests/but-w-butonierce-but-w-butonierce.xml create mode 100755 apps/catalogue/tests/fraszka-do-anusie.xml diff --git a/apps/catalogue/management/commands/importbooks.py b/apps/catalogue/management/commands/importbooks.py index 72878fe25..ea3768ba7 100644 --- a/apps/catalogue/management/commands/importbooks.py +++ b/apps/catalogue/management/commands/importbooks.py @@ -24,6 +24,8 @@ class Command(BaseCommand): help='Don\'t build EPUB file'), make_option('-T', '--no-build-txt', action='store_false', dest='build_txt', default=True, help='Don\'t build TXT file'), + make_option('-T', '--no-build-pdf', action='store_false', dest='build_pdf', default=True, + help='Don\'t build PDF file'), make_option('-w', '--wait-until', dest='wait_until', metavar='TIME', help='Wait until specified time (Y-M-D h:m:s)'), ) @@ -81,7 +83,8 @@ class Command(BaseCommand): try: book = Book.from_xml_file(file_path, overwrite=force, build_epub=options.get('build_epub'), - build_txt=options.get('build_txt')) + build_txt=options.get('build_txt'), + build_pdf=options.get('build_pdf')) files_imported += 1 if os.path.isfile(file_base + '.pdf'): diff --git a/apps/catalogue/models.py b/apps/catalogue/models.py index ead5ba040..930f98d14 100644 --- a/apps/catalogue/models.py +++ b/apps/catalogue/models.py @@ -22,14 +22,14 @@ from django.conf import settings from newtagging.models import TagBase, tags_updated from newtagging import managers from catalogue.fields import JSONField, OverwritingFileField -from catalogue.utils import ExistingFile +from catalogue.utils import ExistingFile, BookImportDocProvider from librarian import dcparser, html, epub, NoDublinCore import mutagen from mutagen import id3 from slughifi import slughifi from sortify import sortify - +from os import unlink TAG_CATEGORIES = ( ('author', _('author')), @@ -51,6 +51,7 @@ MEDIA_FORMATS = ( # not quite, but Django wants you to set a timeout CACHE_FOREVER = 2419200 # 28 days + class TagSubcategoryManager(models.Manager): def __init__(self, subcategory): super(TagSubcategoryManager, self).__init__() @@ -295,9 +296,9 @@ class Book(models.Model): xml_file = models.FileField(_('XML file'), upload_to=book_upload_path('xml'), blank=True) html_file = models.FileField(_('HTML file'), upload_to=book_upload_path('html'), blank=True) pdf_file = models.FileField(_('PDF file'), upload_to=book_upload_path('pdf'), blank=True) - epub_file = models.FileField(_('EPUB file'), upload_to=book_upload_path('epub'), blank=True) - txt_file = models.FileField(_('TXT file'), upload_to=book_upload_path('txt'), blank=True) - + epub_file = models.FileField(_('EPUB file'), upload_to=book_upload_path('epub'), blank=True) + txt_file = models.FileField(_('TXT file'), upload_to=book_upload_path('txt'), blank=True) + parent = models.ForeignKey('self', blank=True, null=True, related_name='children') objects = models.Manager() tagged = managers.ModelTaggedItemManager(Tag) @@ -503,34 +504,49 @@ class Book(models.Model): return bool(self.has_media("ogg")) has_ogg_file.short_description = 'OGG' has_ogg_file.boolean = True - + def has_daisy_file(self): return bool(self.has_media("daisy")) has_daisy_file.short_description = 'DAISY' - has_daisy_file.boolean = True - + has_daisy_file.boolean = True + + def build_pdf(self): + """ (Re)builds the pdf file. + + """ + from librarian import pdf, ParseError + from tempfile import NamedTemporaryFile + import os + + try: + path, fname = os.path.realpath(self.xml_file.path).rsplit('/', 1) + try: + pdf_file = NamedTemporaryFile(delete=False) + + pdf.transform(BookImportDocProvider(self), + file_path=str(self.xml_file.path), + output_file=pdf_file, + ) + + self.pdf_file.save('%s.pdf' % self.slug, File(open(pdf_file.name))) + finally: + unlink(pdf_file.name) + + except ParseError, e: + print '%(file)s:%(name)s:%(message)s; use -v to see more output' % { + 'file': self.xml_file.path, + 'name': e.__class__.__name__, + 'message': e + } + def build_epub(self, remove_descendants=True): """ (Re)builds the epub file. If book has a parent, does nothing. Unless remove_descendants is False, descendants' epubs are removed. """ - from StringIO import StringIO from hashlib import sha1 from django.core.files.base import ContentFile - from librarian import DocProvider - - class BookImportDocProvider(DocProvider): - """ used for joined EPUBs """ - - def __init__(self, book): - self.book = book - - def by_slug(self, slug): - if slug == self.book.slug: - return self.book.xml_file - else: - return Book.objects.get(slug=slug).xml_file if self.parent: # don't need an epub @@ -633,7 +649,7 @@ class Book(models.Model): xml_file.close() @classmethod - def from_text_and_meta(cls, raw_file, book_info, overwrite=False, build_epub=True, build_txt=True): + def from_text_and_meta(cls, raw_file, book_info, overwrite=False, build_epub=True, build_txt=True, build_pdf=True): import re # check for parts before we do anything @@ -706,6 +722,9 @@ class Book(models.Model): if not settings.NO_BUILD_EPUB and build_epub: book.root_ancestor.build_epub() + if not settings.NO_BUILD_PDF and build_pdf: + book.root_ancestor.build_pdf() + book_descendants = list(book.children.all()) # add l-tag to descendants and their fragments # delete unnecessary EPUB files diff --git a/apps/catalogue/test_utils.py b/apps/catalogue/test_utils.py index 1dcd7266a..7905efbb3 100644 --- a/apps/catalogue/test_utils.py +++ b/apps/catalogue/test_utils.py @@ -10,7 +10,7 @@ class WLTestCase(TestCase): """ def setUp(self): self._MEDIA_ROOT, settings.MEDIA_ROOT = settings.MEDIA_ROOT, tempfile.mkdtemp(prefix='djangotest_') - settings.NO_BUILD_EPUB = settings.NO_BUILD_TXT = True + settings.NO_BUILD_PDF = settings.NO_BUILD_EPUB = settings.NO_BUILD_TXT = True def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT, True) diff --git a/apps/catalogue/tests/book_import.py b/apps/catalogue/tests/book_import.py index 57dbf27a3..29be73177 100644 --- a/apps/catalogue/tests/book_import.py +++ b/apps/catalogue/tests/book_import.py @@ -1,10 +1,13 @@ # -*- coding: utf-8 -*- -from django.core.files.base import ContentFile +from __future__ import with_statement + +from django.core.files.base import ContentFile, File from catalogue.test_utils import * from catalogue import models from nose.tools import raises - +import tempfile +from os import unlink,path class BookImportLogicTests(WLTestCase): @@ -228,3 +231,37 @@ class ChildImportTests(WLTestCase): self.assertEqual(['Kot'], [tag.name for tag in themes], 'wrong related theme list') + + +class BookImportGenerateTest(WLTestCase): + def setUp(self): + WLTestCase.setUp(self) + self.book_info = BookInfoStub( + url=u"http://wolnelektury.pl/example/default-book", + about=u"http://wolnelektury.pl/example/URI/default_book", + title=u"Default Book", + author=PersonStub(("Jim",), "Lazy"), + kind="X-Kind", + genre="X-Genre", + epoch="X-Epoch", + ) + + self.expected_tags = [ + ('author', 'jim-lazy'), + ('genre', 'x-genre'), + ('epoch', 'x-epoch'), + ('kind', 'x-kind'), + ] + self.expected_tags.sort() + + def test_gen_pdf(self): + input = open(path.dirname(__file__) + '/but-w-butonierce-but-w-butonierce.xml') + book = models.Book.from_text_and_meta(File(input), self.book_info, overwrite=True) + book.build_pdf() + self.assertTrue(path.exists(book.pdf_file.path)) + + def test_gen_pdf_child(self): + input = open(path.dirname(__file__) + "/fraszka-do-anusie.xml") + book = models.Book.from_text_and_meta(File(input), self.book_info, overwrite=True) + book.build_pdf() + self.assertTrue(path.exists(book.pdf_file.path)) diff --git a/apps/catalogue/tests/but-w-butonierce-but-w-butonierce.xml b/apps/catalogue/tests/but-w-butonierce-but-w-butonierce.xml new file mode 100755 index 000000000..8b52addfb --- /dev/null +++ b/apps/catalogue/tests/but-w-butonierce-but-w-butonierce.xml @@ -0,0 +1,67 @@ + + + +Jasieński, Bruno +But w butonierce +http://wolnelektury.pl/katalog/lektura/but-w-butonierce +Sekuła, Aleksandra +Sutkowska, Olga +Fundacja Nowoczesna Polska +Dwudziestolecie międzywojenne +Liryka +Wiersz sylabotoniczny +Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN. +http://wolnelektury.pl/katalog/lektura/but-w-butonierce-but-w-butonierce +http://www.polona.pl/Content/14667/27384_But_w_butoni.html +Jasieński, Bruno (1901-1938), But w butonierce, Klub Futurystów "Katarynka", Warszawa, 1921 +Domena publiczna - Bruno Jasieński zm. 1938 +1938 +xml +text +text +2009-02-23 +L +pol + + + +Bruno Jasieński + +But w butonierce + +But w butonierce + + + +Zmarnowałem podeszwy w całodziennych spieszeniach,/ +Teraz jestem słoneczny, siebiepewny i rad./ +Idę młody, genialny, trzymam ręce w kieszeniach,/ +Stawiam kroki milowe, zamaszyste, jak świat. + +Nie zatrzymam się nigdzie na rozstajach, na wiorstach,/ +Bo mnie niesie coś wiecznie, motorycznie i przed./ +Mijam strachy na wróble w eleganckich windhorstach,/ +Wszystkim kłaniam się grzecznie i poprawiam im pled. + +W parkocieniu krokietni --- jakiś meeting panieński./ +Dyskutują o sztuce, objawiając swój traf./ +One jeszcze nie wiedzą, że, gdy nastał Jasieński,/ +Bezpowrotnie umarli i Tetmajer i Staff. + +One jeszcze nie wiedzą, one jeszcze nie wierzą./ +Poezyjność, futuryzm --- niewiadoma i X./ +Chodźmy biegać, panienki, niech się główki oświeżą, ---/ +Będzie lepiej smakować poobiedni jour-fixe. + +Przeleciało gdzieś auto w białych kłębach benzyny,/ +Zafurkotał na wietrze trzepocący się szal./ +Pojechała mi bajka poza góry doliny/ +I nic jakoś mi nie żal, a powinno być żal... + +Tak mi dobrze, tak mojo, aż rechoce się serce./ +Same nogi mnie niosą gdzieś --- i po co mi, gdzie?/ +Idę młody, genialny, niosę BUT W BUTONIERCEwersaliki,/ +Tym co za mną nie zdążą echopowiem: --- Adieu! --- + + diff --git a/apps/catalogue/tests/fraszka-do-anusie.xml b/apps/catalogue/tests/fraszka-do-anusie.xml new file mode 100755 index 000000000..3bbda155e --- /dev/null +++ b/apps/catalogue/tests/fraszka-do-anusie.xml @@ -0,0 +1,49 @@ + + + + +Sęp Szarzyński, Mikołaj +Fraszka do Anusie +Sekuła, Aleksandra +Sutkowska, Olga +Fundacja Nowoczesna Polska +Barok +Liryka +Fraszka +Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN. +http://wolnelektury.pl/katalog/lektura/fraszka-do-anusie +http://www.polona.pl/Content/8759 +Szarzyński Sęp, Mikołaj (ca 1550-1581), Rytmy abo Wiersze polskie w wyborze, E. Wende, Warszawa, 1914 +Domena publiczna - Mikołaj Sęp Szarzyński zm. 1581 +1581 +xml +text +text +2008-12-29 +L +L +pol + + + + +Mikołaj Sęp Szarzyński + +Fraszka do Anusie + + + +Kochanek, Łzy, Miłość, Oko, Serce, WzrokJeśli oczu hamować swoich nie umiały/ +Leśnych krynic boginie, aby nie płakały,/ +Gdy baczyłybaczyły --- tu: zobaczyły, patrzyły na. przy studni Narcyza pięknego,/ +A on umarł prze miłość oblicza swojego;/ +Jeśli nieśmiertelnym stanom żałość rozkazuje,/ +Gdy niebaczna fortuna co niesłusznie psuje: + +Jakoż ja mam hamować, by na lice moje/ +Z oczu smutnych żałośne nie płynęły zdroje?/ +Jako serce powściągać, aby nie wzdychało/ +I od ciężkiej żałości omdlewać nie miało? + + + diff --git a/apps/catalogue/utils.py b/apps/catalogue/utils.py index 566eaf4cd..02e5b6d93 100644 --- a/apps/catalogue/utils.py +++ b/apps/catalogue/utils.py @@ -10,6 +10,8 @@ from django.core.files.uploadedfile import UploadedFile from django.utils.hashcompat import sha_constructor from django.conf import settings +from librarian import DocProvider + # Use the system (hardware-based) random number generator if it exists. if hasattr(random, 'SystemRandom'): @@ -44,3 +46,16 @@ class ExistingFile(UploadedFile): def close(self): pass + + +class BookImportDocProvider(DocProvider): + """ used for joined EPUBs """ + + def __init__(self, book): + self.book = book + + def by_slug(self, slug): + if slug == self.book.slug: + return self.book.xml_file + else: + return Book.objects.get(slug=slug).xml_file -- 2.20.1