From: Radek Czajka Date: Fri, 30 Dec 2011 08:45:20 +0000 (+0100) Subject: Merge branch 'pretty' of github.com:fnp/wolnelektury into pretty X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/d52c76021eaf1621b4e55a6eba1df1c0404bd7dd?ds=inline;hp=-c Merge branch 'pretty' of github.com:fnp/wolnelektury into pretty Conflicts: apps/catalogue/management/commands/importbooks.py wolnelektury/templates/catalogue/book_detail.html wolnelektury/templates/catalogue/book_short.html --- d52c76021eaf1621b4e55a6eba1df1c0404bd7dd diff --combined apps/catalogue/management/commands/importbooks.py index 995132a82,b6ddc5540..5b4d499a4 --- a/apps/catalogue/management/commands/importbooks.py +++ b/apps/catalogue/management/commands/importbooks.py @@@ -29,11 -29,12 +29,12 @@@ class Command(BaseCommand) help='Don\'t build TXT file'), make_option('-P', '--no-build-pdf', action='store_false', dest='build_pdf', default=True, help='Don\'t build PDF file'), + make_option('-S', '--no-search-index', action='store_false', dest='search_index', 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)'), make_option('-p', '--picture', action='store_true', dest='import_picture', default=False, help='Import pictures'), - ) help = 'Imports books from the specified directories.' args = 'directory [directory ...]' @@@ -42,14 -43,16 +43,15 @@@ verbose = options.get('verbose') file_base, ext = os.path.splitext(file_path) book = Book.from_xml_file(file_path, overwrite=options.get('force'), - build_epub=options.get('build_epub'), - build_txt=options.get('build_txt'), - build_pdf=options.get('build_pdf'), - build_mobi=options.get('build_mobi')) + build_epub=options.get('build_epub'), + build_txt=options.get('build_txt'), + build_pdf=options.get('build_pdf'), + build_mobi=options.get('build_mobi'), + search_index=options.get('search_index')) - fileid = book.fileid() for ebook_format in Book.ebook_formats: if os.path.isfile(file_base + '.' + ebook_format): getattr(book, '%s_file' % ebook_format).save( - '%s.%s' % (fileid, ebook_format), + '%s.%s' % (book.slug, ebook_format), File(file(file_base + '.' + ebook_format))) if verbose: print "Importing %s.%s" % (file_base, ebook_format) diff --combined apps/catalogue/models.py index 43a9496d8,77efe6e98..78e6b0b3d --- a/apps/catalogue/models.py +++ b/apps/catalogue/models.py @@@ -24,13 -24,15 +24,15 @@@ from newtagging.models import TagBase, from newtagging import managers from catalogue.fields import JSONField, OverwritingFileField from catalogue.utils import create_zip, split_tags - from catalogue.tasks import touch_tag + from catalogue.tasks import touch_tag, index_book from shutil import copy from glob import glob import re from os import path + import search + TAG_CATEGORIES = ( ('author', _('author')), ('epoch', _('epoch')), @@@ -220,7 -222,7 +222,7 @@@ def get_customized_pdf_path(book, custo customizations.sort() h = hash(tuple(customizations)) - pdf_name = '%s-custom-%s' % (book.fileid(), h) + pdf_name = '%s-custom-%s' % (book.slug, h) pdf_file = get_dynamic_path(None, pdf_name, ext='pdf') return pdf_file @@@ -230,7 -232,7 +232,7 @@@ def get_existing_customized_pdf(book) """ Returns a list of paths to generated customized pdf of a book """ - pdf_glob = '%s-custom-' % (book.fileid(),) + pdf_glob = '%s-custom-' % (book.slug,) pdf_glob = get_dynamic_path(None, pdf_glob, ext='pdf') pdf_glob = re.sub(r"[.]([a-z0-9]+)$", "*.\\1", pdf_glob) return glob(path.join(settings.MEDIA_ROOT, pdf_glob)) @@@ -269,7 -271,7 +271,7 @@@ class BookMedia(models.Model) try: old = BookMedia.objects.get(pk=self.pk) except BookMedia.DoesNotExist, e: - pass + old = None else: # if name changed, change the file name, too if slughifi(self.name) != slughifi(old.name): @@@ -278,9 -280,7 +280,9 @@@ super(BookMedia, self).save(*args, **kwargs) # remove the zip package for book with modified media - remove_zip(self.book.fileid()) + if old: + remove_zip("%s_%s" % (old.book.slug, old.type)) + remove_zip("%s_%s" % (self.book.slug, self.type)) extra_info = self.get_extra_info_value() extra_info.update(self.read_meta()) @@@ -349,9 -349,7 +351,9 @@@ class Book(models.Model): title = models.CharField(_('title'), max_length=120) sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False) - slug = models.SlugField(_('slug'), max_length=120, db_index=True) + slug = models.SlugField(_('slug'), max_length=120, db_index=True, + unique=True) + common_slug = models.SlugField(_('slug'), max_length=120, db_index=True) language = models.CharField(_('language code'), max_length=3, db_index=True, default=settings.CATALOGUE_DEFAULT_LANGUAGE) description = models.TextField(_('description'), blank=True) @@@ -376,10 -374,14 +378,10 @@@ html_built = django.dispatch.Signal() published = django.dispatch.Signal() - URLID_RE = r'[a-z0-9-]+(?:/[a-z]{3})?' - FILEID_RE = r'[a-z0-9-]+(?:_[a-z]{3})?' - class AlreadyExists(Exception): pass class Meta: - unique_together = [['slug', 'language']] ordering = ('sort_key',) verbose_name = _('book') verbose_name_plural = _('books') @@@ -387,6 -389,42 +389,6 @@@ def __unicode__(self): return self.title - def urlid(self, sep='/'): - stem = self.slug - if self.language != settings.CATALOGUE_DEFAULT_LANGUAGE: - stem += sep + self.language - return stem - - def fileid(self): - return self.urlid('_') - - @staticmethod - def split_urlid(urlid, sep='/', default_lang=settings.CATALOGUE_DEFAULT_LANGUAGE): - """Splits a URL book id into slug and language code. - - Returns a dictionary usable i.e. for object lookup, or None. - - >>> Book.split_urlid("a-slug/pol", default_lang="eng") - {'slug': 'a-slug', 'language': 'pol'} - >>> Book.split_urlid("a-slug", default_lang="eng") - {'slug': 'a-slug', 'language': 'eng'} - >>> Book.split_urlid("a-slug_pol", "_", default_lang="eng") - {'slug': 'a-slug', 'language': 'pol'} - >>> Book.split_urlid("a-slug/eng", default_lang="eng") - - """ - parts = urlid.rsplit(sep, 1) - if len(parts) == 2: - if parts[1] == default_lang: - return None - return {'slug': parts[0], 'language': parts[1]} - else: - return {'slug': urlid, 'language': default_lang} - - @classmethod - def split_fileid(cls, fileid): - return cls.split_urlid(fileid, '_') - def save(self, force_insert=False, force_update=False, reset_short_html=True, **kwargs): from sortify import sortify @@@ -401,14 -439,18 +403,14 @@@ @permalink def get_absolute_url(self): - return ('catalogue.views.book_detail', [self.urlid()]) + return ('catalogue.views.book_detail', [self.slug]) @property def name(self): return self.title def book_tag_slug(self): - stem = 'l-' + self.slug - if self.language != settings.CATALOGUE_DEFAULT_LANGUAGE: - return stem[:116] + ' ' + self.language - else: - return stem[:120] + return ('l-' + self.slug)[:120] def book_tag(self): slug = self.book_tag_slug() @@@ -468,16 -510,12 +470,12 @@@ tags = self.tags.filter(category__in=('author', 'kind', 'genre', 'epoch')) tags = split_tags(tags) - formats = [] + formats = {} # files generated during publication for ebook_format in self.ebook_formats: if self.has_media(ebook_format): - formats.append(u'%s' % ( - self.get_media(ebook_format).url, - ebook_format.upper() - )) + formats[ebook_format] = self.get_media(ebook_format) - formats = [mark_safe(format) for format in formats] short_html = unicode(render_to_string('catalogue/book_short.html', {'book': self, 'tags': tags, 'formats': formats})) @@@ -525,7 -563,7 +523,7 @@@ has_daisy_file.boolean = True def wldocument(self, parse_dublincore=True): - from catalogue.utils import ORMDocProvider + from catalogue.import_utils import ORMDocProvider from librarian.parser import WLDocument return WLDocument.from_file(self.xml_file.path, @@@ -561,7 -599,7 +559,7 @@@ # we'd like to be sure not to overwrite changes happening while # (timely) pdf generation is taking place (async celery scenario) current_self = Book.objects.get(id=self.id) - current_self.pdf_file.save('%s.pdf' % self.fileid(), + current_self.pdf_file.save('%s.pdf' % self.slug, File(open(pdf.get_filename()))) self.pdf_file = current_self.pdf_file @@@ -584,7 -622,7 +582,7 @@@ mobi = self.wldocument().as_mobi() - self.mobi_file.save('%s.mobi' % self.fileid(), File(open(mobi.get_filename()))) + self.mobi_file.save('%s.mobi' % self.slug, File(open(mobi.get_filename()))) # remove zip with all mobi files remove_zip(settings.ALL_MOBI_ZIP) @@@ -596,7 -634,7 +594,7 @@@ epub = self.wldocument().as_epub() - self.epub_file.save('%s.epub' % self.fileid(), + self.epub_file.save('%s.epub' % self.slug, File(open(epub.get_filename()))) # remove zip package with all epub files @@@ -606,7 -644,7 +604,7 @@@ from django.core.files.base import ContentFile text = self.wldocument().as_text() - self.txt_file.save('%s.txt' % self.fileid(), ContentFile(text.get_string())) + self.txt_file.save('%s.txt' % self.slug, ContentFile(text.get_string())) def build_html(self): @@@ -621,7 -659,7 +619,7 @@@ html_output = self.wldocument(parse_dublincore=False).as_html() if html_output: - self.html_file.save('%s.html' % self.fileid(), + self.html_file.save('%s.html' % self.slug, ContentFile(html_output.get_string())) # get ancestor l-tags for adding to new fragments @@@ -672,7 -710,7 +670,7 @@@ def pretty_file_name(book): return "%s/%s.%s" % ( b.get_extra_info_value()['author'], - b.fileid(), + b.slug, format_) field_name = "%s_file" % format_ @@@ -686,9 -724,22 +684,22 @@@ def zip_audiobooks(self, format_): bm = BookMedia.objects.filter(book=self, type=format_) paths = map(lambda bm: (None, bm.file.path), bm) - result = create_zip.delay(paths, "%s_%s" % (self.fileid(), format_)) + result = create_zip.delay(paths, "%s_%s" % (self.slug, format_)) return result.wait() + def search_index(self, book_info=None): + if settings.CELERY_ALWAYS_EAGER: + idx = search.ReusableIndex() + else: + idx = search.Index() + + idx.open() + try: + idx.index_book(self, book_info) + idx.index_tags() + finally: + idx.close() + @classmethod def from_xml_file(cls, xml_file, **kwargs): from django.core.files import File @@@ -707,7 -758,8 +718,8 @@@ @classmethod def from_text_and_meta(cls, raw_file, book_info, overwrite=False, - build_epub=True, build_txt=True, build_pdf=True, build_mobi=True): + build_epub=True, build_txt=True, build_pdf=True, build_mobi=True, + search_index=True): import re from sortify import sortify @@@ -716,33 -768,30 +728,33 @@@ if hasattr(book_info, 'parts'): for part_url in book_info.parts: try: - children.append(Book.objects.get( - slug=part_url.slug, language=part_url.language)) + children.append(Book.objects.get(slug=part_url.slug)) except Book.DoesNotExist, e: - raise Book.DoesNotExist(_('Book "%s/%s" does not exist.') % - (part_url.slug, part_url.language)) + raise Book.DoesNotExist(_('Book "%s" does not exist.') % + part_url.slug) # Read book metadata book_slug = book_info.url.slug - language = book_info.language - if re.search(r'[^a-zA-Z0-9-]', book_slug): + if re.search(r'[^a-z0-9-]', book_slug): raise ValueError('Invalid characters in slug') - book, created = Book.objects.get_or_create(slug=book_slug, language=language) + book, created = Book.objects.get_or_create(slug=book_slug) if created: book_shelves = [] else: if not overwrite: - raise Book.AlreadyExists(_('Book %s/%s already exists') % ( - book_slug, language)) + raise Book.AlreadyExists(_('Book %s already exists') % ( + book_slug)) # Save shelves for this book book_shelves = list(book.tags.filter(category='set')) + book.language = book_info.language book.title = book_info.title + if book_info.variant_of: + book.common_slug = book_info.variant_of.slug + else: + book.common_slug = book.slug book.set_extra_info_value(book_info.to_dict()) book.save() @@@ -778,6 -827,9 +790,9 @@@ if not settings.NO_BUILD_MOBI and build_mobi: book.build_mobi() + if not settings.NO_SEARCH_INDEX and search_index: + index_book.delay(book.id, book_info) + book_descendants = list(book.children.all()) descendants_tags = set() # add l-tag to descendants and their fragments @@@ -903,7 -955,7 +918,7 @@@ books_by_parent = {} books = cls.objects.all().order_by('parent_number', 'sort_key').only( - 'title', 'parent', 'slug', 'language') + 'title', 'parent', 'slug') if filter: books = books.filter(filter).distinct() book_ids = set((book.pk for book in books)) @@@ -979,7 -1031,7 +994,7 @@@ class Fragment(models.Model) verbose_name_plural = _('fragments') def get_absolute_url(self): - return '%s#m%s' % (self.book.get_html_url(), self.anchor) + return '%s#m%s' % (reverse('book_text', args=[self.book.slug]), self.anchor) def reset_short_html(self): if self.id is None: diff --combined apps/catalogue/urls.py index e5087b6d0,4020592d2..b6fd3610f --- a/apps/catalogue/urls.py +++ b/apps/catalogue/urls.py @@@ -7,48 -7,45 +7,48 @@@ from catalogue.feeds import AudiobookFe from catalogue.models import Book from picture.models import Picture + +SLUG = r'[a-z0-9-]*' + urlpatterns = patterns('picture.views', # pictures - currently pictures are coupled with catalogue, hence the url is here url(r'^obraz/?$', 'picture_list'), - url(r'^obraz/(?P%s)/?$' % Picture.URLID_RE, 'picture_detail') + url(r'^obraz/(?P%s)/?$' % SLUG, 'picture_detail') ) + \ patterns('catalogue.views', url(r'^$', 'catalogue', name='catalogue'), url(r'^polki/(?P[a-zA-Z0-9-]+)/formaty/$', 'shelf_book_formats', name='shelf_book_formats'), - url(r'^polki/(?P[a-zA-Z0-9-]+)/(?P%s)/usun$' % Book.URLID_RE, 'remove_from_shelf', name='remove_from_shelf'), + url(r'^polki/(?P[a-zA-Z0-9-]+)/(?P%s)/usun$' % SLUG, 'remove_from_shelf', name='remove_from_shelf'), url(r'^polki/$', 'user_shelves', name='user_shelves'), url(r'^polki/(?P[a-zA-Z0-9-]+)/usun/$', 'delete_shelf', name='delete_shelf'), url(r'^polki/(?P[a-zA-Z0-9-]+)\.zip$', 'download_shelf', name='download_shelf'), url(r'^lektury/', 'book_list', name='book_list'), url(r'^audiobooki/$', 'audiobook_list', name='audiobook_list'), url(r'^daisy/$', 'daisy_list', name='daisy_list'), - url(r'^lektura/(?P%s)/polki/' % Book.URLID_RE, 'book_sets', name='book_shelves'), + url(r'^lektura/(?P%s)/polki/' % SLUG, 'book_sets', name='book_shelves'), url(r'^polki/nowa/$', 'new_set', name='new_set'), url(r'^tags/$', 'tags_starting_with', name='hint'), url(r'^jtags/$', 'json_tags_starting_with', name='jhint'), - url(r'^szukaj/$', 'search', name='search'), + url(r'^szukaj/$', 'search', name='old_search'), # zip url(r'^zip/pdf\.zip$', 'download_zip', {'format': 'pdf', 'slug': None}, 'download_zip_pdf'), url(r'^zip/epub\.zip$', 'download_zip', {'format': 'epub', 'slug': None}, 'download_zip_epub'), url(r'^zip/mobi\.zip$', 'download_zip', {'format': 'mobi', 'slug': None}, 'download_zip_mobi'), - url(r'^zip/mp3/(?P%s)\.zip' % Book.FILEID_RE, 'download_zip', {'format': 'mp3'}, 'download_zip_mp3'), - url(r'^zip/ogg/(?P%s)\.zip' % Book.FILEID_RE, 'download_zip', {'format': 'ogg'}, 'download_zip_ogg'), + url(r'^zip/mp3/(?P%s)\.zip' % SLUG, 'download_zip', {'format': 'mp3'}, 'download_zip_mp3'), + url(r'^zip/ogg/(?P%s)\.zip' % SLUG, 'download_zip', {'format': 'ogg'}, 'download_zip_ogg'), # Public interface. Do not change this URLs. - url(r'^lektura/(?P%s)\.html$' % Book.FILEID_RE, 'book_text', name='book_text'), - url(r'^lektura/(?P%s)/audiobook/$' % Book.URLID_RE, 'player', name='book_player'), - url(r'^lektura/(?P%s)/$' % Book.URLID_RE, 'book_detail', name='book_detail'), - url(r'^lektura/(?P%s)/motyw/(?P[a-zA-Z0-9-]+)/$' % Book.URLID_RE, + url(r'^lektura/(?P%s)\.html$' % SLUG, 'book_text', name='book_text'), + url(r'^lektura/(?P%s)/audiobook/$' % SLUG, 'player', name='book_player'), + url(r'^lektura/(?P%s)/$' % SLUG, 'book_detail', name='book_detail'), + url(r'^lektura/(?P%s)/motyw/(?P[a-zA-Z0-9-]+)/$' % SLUG, 'book_fragments', name='book_fragments'), url(r'^(?P[a-zA-Z0-9-/]*)/$', 'tagged_object_list', name='tagged_object_list'), url(r'^audiobooki/(?Pmp3|ogg|daisy|all).xml$', AudiobookFeed(), name='audiobook_feed'), - url(r'^custompdf/(?P%s).pdf' % Book.FILEID_RE, 'download_custom_pdf'), + url(r'^custompdf/(?P%s).pdf' % SLUG, 'download_custom_pdf'), ) diff --combined wolnelektury/settings.py index 20a3fc653,00792a814..55c845e60 --- a/wolnelektury/settings.py +++ b/wolnelektury/settings.py @@@ -60,6 -60,7 +60,7 @@@ USE_I18N = Tru # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = path.join(PROJECT_DIR, '../media') STATIC_ROOT = path.join(PROJECT_DIR, 'static') + SEARCH_INDEX = path.join(MEDIA_ROOT, 'search') # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash if there is a path component (optional in other cases). @@@ -153,11 -154,11 +154,12 @@@ INSTALLED_APPS = 'stats', 'suggest', 'picture', + 'search', ] #CACHE_BACKEND = 'locmem:///?max_entries=3000' -#CACHE_BACKEND = 'memcached://127.0.0.1:11211/' +CACHE_BACKEND = 'memcached://127.0.0.1:11211/' +#CACHE_BACKEND = None CACHE_MIDDLEWARE_ANONYMOUS_ONLY=True # CSS and JavaScript file groups @@@ -175,6 -176,8 +177,8 @@@ COMPRESS_CSS = 'css/book_box.css', 'css/catalogue.css', 'css/sponsors.css', + + 'css/ui-lightness/jquery-ui-1.8.16.custom.css', ], 'output_filename': 'css/all.min?.css', }, @@@ -205,12 -208,16 +209,16 @@@ COMPRESS_JS = 'js/jquery.countdown-es.js', 'js/jquery.countdown-lt.js', 'js/jquery.countdown-ru.js', 'js/jquery.countdown-fr.js', + 'js/jquery-ui-1.8.16.custom.min.js', + 'js/locale.js', 'js/dialogs.js', 'js/sponsors.js', 'js/base.js', 'js/pdcounter.js', + 'js/search.js', + #~ 'js/jquery.autocomplete.js', #~ 'js/jquery.labelify.js', 'js/catalogue.js', ), @@@ -265,7 -272,9 +273,9 @@@ MAX_TAG_LIST = NO_BUILD_EPUB = False NO_BUILD_TXT = False NO_BUILD_PDF = False - NO_BUILD_MOBI = False + NO_BUILD_MOBI = True + NO_SEARCH_INDEX = False + SEARCH_INDEX_PARALLEL = False ALL_EPUB_ZIP = 'wolnelektury_pl_epub' ALL_PDF_ZIP = 'wolnelektury_pl_pdf' @@@ -287,6 -296,7 +297,7 @@@ BROKER_PASSWORD = "guest BROKER_VHOST = "/" + # Load localsettings, if they exist try: from localsettings import * diff --combined wolnelektury/templates/catalogue/book_detail.html index 62b16f242,0a8b2236c..5645f68ec --- a/wolnelektury/templates/catalogue/book_detail.html +++ b/wolnelektury/templates/catalogue/book_detail.html @@@ -9,135 -9,8 +9,7 @@@ {% block bodyid %}book-detail{% endblock %} {% block body %} -

{% book_title book %}

-
- + {% book_wide book %} - {% if extra_info.license %} -

{% trans "Work is licensed under " %} {{ extra_info.license_description }}.

- {% endif %} -

{% trans "Based on" %}: {{ extra_info.source_name }}

- {% if book.has_description %} -
-
{{ book.description|safe }}
-
{{ book.description|safe|truncatewords_html:30 }}
-
-

- {% endif %} -
-

{% trans "Put a book" %} {% trans "on the shelf!" %}

-
-
- {% if book.has_html_file %} - {% trans "Read online" %} - {% endif %} -
- {% if book.pdf_file %} - {% trans - {% endif %} - {% if book.epub_file %} - {% trans - {% endif %} - {% if book.mobi_file %} - {% trans - {% endif %} - {% if book.txt_file %} - {% trans - {% endif %} - - {% if book.pdf_file %} -
{% trans "Dowload customized PDF" %}. - {% endif %} - -
-
-
- - {% if book_children %} - {% autopaginate book_children 10 %} -
-
    - {% for book in book_children %} -
  1. {{ book.short_html }}
  2. - {% endfor %} -
-
- {% paginate %} - {% endif %} - -
-- -
-
-

{% trans "Details" %}

-
    -
  • - {% trans "Author" %}: - {% for tag in categories.author %} - {{ tag }}{% if not forloop.last %}, {% endif %} - {% endfor %} -
  • -
  • - {% trans "Epoch" %}: - {% for tag in categories.epoch %} - {{ tag }}{% if not forloop.last %}, {% endif %} - {% endfor %} -
  • -
  • - {% trans "Kind" %}: - {% for tag in categories.kind %} - {{ tag }}{% if not forloop.last %}, {% endif %} - {% endfor %} -
  • -
  • - {% trans "Genre" %}: - {% for tag in categories.genre %} - {{ tag }}{% if not forloop.last %}, {% endif %} - {% endfor %} -
  • -
-

{% trans "Other resources" %}

- -

{% trans "View XML source" %}

-

Miksuj ten utwór

-
-
-

{% trans "Work's themes " %}

- -
-
-
{% endblock %} diff --combined wolnelektury/templates/catalogue/book_short.html index 9800a0cae,1c3a74253..7f262374d --- a/wolnelektury/templates/catalogue/book_short.html +++ b/wolnelektury/templates/catalogue/book_short.html @@@ -1,6 -1,6 +1,6 @@@ {% load i18n %} {% load thumbnail %} -
+
{% if book.cover %} @@@ -13,6 -13,8 +13,8 @@@ " alt="Cover" /> {% endif %} + {% block right-column %} + {% endblock %}
@@@ -49,20 -51,33 +51,33 @@@ + {% block box-append %} + {% endblock %}