Allow books with internationalized metadata.
authorRadek Czajka <radekczajka@nowoczesnapolska.org.pl>
Tue, 19 Nov 2013 12:09:12 +0000 (13:09 +0100)
committerRadek Czajka <radekczajka@nowoczesnapolska.org.pl>
Tue, 19 Nov 2013 12:35:56 +0000 (13:35 +0100)
apps/catalogue/constants.py
apps/catalogue/fields.py
apps/catalogue/models/book.py
apps/catalogue/models/tag.py
apps/catalogue/templatetags/catalogue_tags.py
apps/catalogue/tests/tags.py
apps/catalogue/utils.py
apps/funding/static/funding/funding.css
lib/librarian
requirements.txt

index bfad4b0..36f4f5d 100644 (file)
@@ -21,3 +21,18 @@ EBOOK_FORMATS_WITH_CHILDREN = ['pdf', 'epub', 'mobi']
 EBOOK_FORMATS_WITH_COVERS = ['pdf', 'epub', 'mobi']
 
 EBOOK_FORMATS = EBOOK_FORMATS_WITHOUT_CHILDREN + EBOOK_FORMATS_WITH_CHILDREN
+
+LANGUAGES_3TO2 = {
+    'deu': 'de',
+    'ger': 'de',
+    'eng': 'en',
+    'spa': 'es',
+    'fra': 'fr',
+    'fre': 'fr',
+    'ita': 'it',
+    'jpn': 'jp',
+    'lit': 'lt',
+    'pol': 'pl',
+    'rus': 'ru',
+    'ukr': 'uk',
+}
index bb9f26e..e2978e8 100644 (file)
@@ -7,6 +7,7 @@ from django.core.files import File
 from django.db import models
 from django.db.models.fields.files import FieldFile
 from catalogue import app_settings
+from catalogue.constants import LANGUAGES_3TO2
 from catalogue.utils import remove_zip, truncate_html_words
 from celery.task import Task, task
 from waiter.utils import clear_cache
@@ -140,6 +141,11 @@ class BuildHtml(BuildEbook):
         html_output = self.transform(
                         book.wldocument(parse_dublincore=False),
                         fieldfile)
+        lang = book.language
+        lang = LANGUAGES_3TO2.get(lang, lang)
+        if lang not in [ln[0] for ln in settings.LANGUAGES]:
+            lang = None
+
         if html_output:
             fieldfile.save(None, ContentFile(html_output.get_string()),
                     save=False)
@@ -167,14 +173,26 @@ class BuildHtml(BuildEbook):
                 for theme_name in theme_names:
                     if not theme_name:
                         continue
-                    tag, created = Tag.objects.get_or_create(
-                                        slug=slughifi(theme_name),
-                                        category='theme')
-                    if created:
-                        tag.name = theme_name
-                        tag.sort_key = sortify(theme_name.lower())
-                        tag.save()
-                    themes.append(tag)
+                    if lang == settings.LANGUAGE_CODE:
+                        # Allow creating themes if book in default language.
+                        tag, created = Tag.objects.get_or_create(
+                                            slug=slughifi(theme_name),
+                                            category='theme')
+                        if created:
+                            tag.name = theme_name
+                            setattr(tag, "name_%s" % lang, theme_name)
+                            tag.sort_key = sortify(theme_name.lower())
+                            tag.save()
+                        themes.append(tag)
+                    elif lang is not None:
+                        # Don't create unknown themes in non-default languages.
+                        try:
+                            tag = Tag.objects.get(category='theme',
+                                    **{"name_%s" % lang: theme_name})
+                        except Tag.DoesNotExist:
+                            pass
+                        else:
+                            themes.append(tag)
                 if not themes:
                     continue
 
index 0438afa..c865d1d 100644 (file)
@@ -495,7 +495,7 @@ class Book(models.Model):
         book = self
         rel_info = book.related_info()
         names = [(related_tag_name(tag), Tag.create_url('author', tag['slug']))
-                    for tag in rel_info['tags']['author']]
+                    for tag in rel_info['tags'].get('author', ())]
         if 'parents' in rel_info:
             books = [(name, Book.create_url(slug))
                         for name, slug in rel_info['parents']]
index b0c75fb..1309cbb 100644 (file)
@@ -2,6 +2,7 @@
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+from django.conf import settings
 from django.contrib.auth.models import User
 from django.core.exceptions import ValidationError
 from django.db import models
@@ -166,14 +167,26 @@ class Tag(TagBase):
                     # For instance, Pictures do not have 'genre' field.
                     continue
             for tag_name in tag_names:
+                lang = getattr(tag_name, 'lang', settings.LANGUAGE_CODE)
                 tag_sort_key = tag_name
                 if category == 'author':
                     tag_sort_key = tag_name.last_name
                     tag_name = tag_name.readable()
-                tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
-                if created:
-                    tag.name = tag_name
-                    tag.sort_key = sortify(tag_sort_key.lower())
-                    tag.save()
-                meta_tags.append(tag)
+                if lang == settings.LANGUAGE_CODE:
+                    # Allow creating new tag, if it's in default language.
+                    tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
+                    if created:
+                        tag.name = tag_name
+                        setattr(tag, "name_%s" % lang, tag_name)
+                        tag.sort_key = sortify(tag_sort_key.lower())
+                        tag.save()
+                    meta_tags.append(tag)
+                else:
+                    # Ignore unknown tags in non-default languages.
+                    try:
+                        tag = Tag.objects.get(category=category, **{"name_%s" % lang: tag_name})
+                    except Tag.DoesNotExist:
+                        pass
+                    else:
+                        meta_tags.append(tag)
         return meta_tags
index 8c073c1..d6a795f 100644 (file)
@@ -351,7 +351,7 @@ def book_short(context, book):
 @register.inclusion_tag('catalogue/book_mini_box.html')
 def book_mini(book):
     author_str = ", ".join(related_tag_name(tag)
-        for tag in book.related_info()['tags']['author'])
+        for tag in book.related_info()['tags'].get('author', ()))
     return {
         'book': book,
         'author_str': author_str,
index 17f9a77..99edebb 100644 (file)
@@ -271,9 +271,9 @@ class BookTagsTests(WLTestCase):
         related_themes = book.related_themes()
 
         self.assertEqual(related_info['tags']['author'],
-                         [{'pl': 'Common Man', 'slug': 'common-man'}])
+                         [{'name_pl': 'Common Man', 'slug': 'common-man'}])
         self.assertEqual(related_info['tags']['kind'],
-                         [{'pl': 'Kind', 'slug': 'kind'}])
+                         [{'name_pl': 'Kind', 'slug': 'kind'}])
         self.assertEqual([(tag.name, tag.count) for tag in related_themes],
                          [('ChildTheme', 1), ('ParentTheme', 1), ('Theme', 2)])
 
index b023f1a..edc287f 100644 (file)
@@ -317,7 +317,7 @@ This can sometimes occupy lots of memory, so trim it here a bit.
 
 def related_tag_name(tag_info, language=None):
     return tag_info.get("name_%s" % (language or get_language()),
-        tag_info.get("name_%s" % settings.LANGUAGE_CODE))
+        tag_info.get("name_%s" % settings.LANGUAGE_CODE, ""))
 
 
 def delete_from_cache_by_language(cache, key_template):
index 89dd7aa..a3e6546 100755 (executable)
@@ -1 +1 @@
-.funding,.funding-top-header{background:orange;margin:auto;padding:5px 0;position:relative}.funding a.call,.funding-top-header a.call{height:1.2em;width:9em;padding:.35em .5em;margin:.5em;display:inline-block;vertical-align:top;text-align:center;background:rgb(.465%, 59.936%, 63.653%)}.funding .description,.funding-top-header .description{display:inline-block;padding-left:.6em}.funding .description a,.funding-top-header .description a{display:block;color:#000;padding:0 3px}.funding strong,.funding-top-header strong{font-size:1.5em;padding:.2em 0 0}.funding .progress,.funding-top-header .progress{width:95.7em;margin:.3em 0 .4em;border-radius:2em;background-image:url(/static/img/progress-pixel.png);background-repeat:repeat-y;background-color:#f68900;box-shadow:.1em .1em .1em #888}.funding .progress .piece,.funding-top-header .progress .piece{font-size:1.3em;padding:.3em .5em}.funding .with-button .progress,.funding-top-header .with-button .progress{width:78em}.funding .funding .funding-title-strong,.funding .funding-title,.funding-top-header .funding-title,.funding-top-header .funding-top-header .funding-title-strong{font-size:1.1em}.funding .funding-title-strong,.funding-top-header .funding-title-strong{font-weight:bold;font-size:1.5em}.funding .close,.funding-top-header .close{position:absolute;top:0;right:5px}.funding-top-header{width:97.5em}.wlfund{width:100%;border-spacing:0}.wlfund td{padding:1em .5em 1em;text-align:center;border-top:1em solid rgba(255, 255, 255, 0);border-bottom:2px solid #DDD;background-clip:padding-box;-moz-background-clip:padding;-webkit-background-clip:padding}.wlfund td.oneline{white-space:nowrap}.wlfund td:last-child{text-align:right}.wlfund .funding-plus td{background-color:#cfe5e7}.wlfund .funding-minus td{background-color:#fff}.honking{background:#018189;font-size:1.5em;padding:.5em;color:#fff;border:0;box-shadow:.2em .2em .3em #888;position:relative}.honking:hover{box-shadow:none;top:.1em;left:.1em}.share a{margin-right:1.5em}.share a img{vertical-align:middle}.funding-handle{position:absolute;top:0;right:0;z-index:2;background:orange;cursor:pointer;padding:.2em 1em;border-radius:0 0 0 1em;font-size:13px}.funding-cover{float:left;margin-right:10px}.funding-details-intro{padding-top:1em}.funding-details-intro h3:first-of-type{margin-top:0}
\ No newline at end of file
+.funding,.funding-top-header{background:orange;margin:auto;padding:5px 0;position:relative}.funding a.call,.funding-top-header a.call{height:1.2em;width:9em;padding:.35em .5em;margin:.5em;display:inline-block;vertical-align:top;text-align:center;background:#018189}.funding .description,.funding-top-header .description{display:inline-block;padding-left:.6em}.funding .description a,.funding-top-header .description a{display:block;color:#000;padding:0 3px}.funding strong,.funding-top-header strong{font-size:1.5em;padding:.2em 0 0}.funding .progress,.funding-top-header .progress{width:95.7em;margin:.3em 0 .4em;border-radius:2em;background-image:url(/static/img/progress-pixel.png);background-repeat:repeat-y;background-color:#f68900;box-shadow:.1em .1em .1em #888}.funding .progress .piece,.funding-top-header .progress .piece{font-size:1.3em;padding:.3em .5em}.funding .with-button .progress,.funding-top-header .with-button .progress{width:78em}.funding .funding-title,.funding .funding-title-strong,.funding-top-header .funding-title,.funding-top-header .funding-title-strong{font-size:1.1em}.funding .funding-title-strong,.funding-top-header .funding-title-strong{font-weight:bold;font-size:1.5em}.funding .close,.funding-top-header .close{position:absolute;top:0;right:5px}.funding-top-header{width:97.5em}.wlfund{width:100%;border-spacing:0}.wlfund td{padding:1em .5em 1em;text-align:center;border-top:1em solid rgba(255,255,255,0);border-bottom:2px solid #ddd;background-clip:padding-box;-moz-background-clip:padding;-webkit-background-clip:padding}.wlfund td.oneline{white-space:nowrap}.wlfund td:last-child{text-align:right}.wlfund .funding-plus td{background-color:#cfe5e7}.wlfund .funding-minus td{background-color:#fff}.honking{background:#018189;font-size:1.5em;padding:.5em;color:#fff;border:0;box-shadow:.2em .2em .3em #888;position:relative}.honking:hover{box-shadow:none;top:.1em;left:.1em}.share a{margin-right:1.5em}.share a img{vertical-align:middle}.funding-handle{position:absolute;top:0;right:0;z-index:2;background:orange;cursor:pointer;padding:.2em 1em;border-radius:0 0 0 1em;font-size:13px}.funding-cover{float:left;margin-right:10px}.funding-details-intro{padding-top:1em}.funding-details-intro h3:first-of-type{margin-top:0}
\ No newline at end of file
index a7d898e..6b327e1 160000 (submodule)
@@ -1 +1 @@
-Subproject commit a7d898e7b37b773ec111aa395f9591d61b7a1d46
+Subproject commit 6b327e1a303fa8512cd5c56684bab2869198c01e
index cd319e2..3ed01b3 100644 (file)
@@ -3,7 +3,7 @@
 
 # django
 Django>=1.5,<1.6
-fnpdjango>=0.1.6,<0.2
+fnpdjango>=0.1.8,<0.2
 South>=0.7 # migrations for django
 django-pipeline>=1.2.24,<1.3
 django-pagination>=1.0