From 08bd2b6b6557481ecbb64b2f07db0d3d9784d9bb Mon Sep 17 00:00:00 2001 From: Marcin Koziej Date: Mon, 16 Dec 2013 16:31:42 +0100 Subject: [PATCH] going production #21 --- .../catalogue/locale/pl/LC_MESSAGES/django.po | 4 +- ...12_auto__add_field_book_sort_key_author.py | 184 ++++++++++++++++++ apps/catalogue/models/book.py | 13 +- apps/catalogue/templates/catalogue/menu.html | 5 + .../catalogue/tagged_object_list.html | 12 +- apps/catalogue/urls.py | 4 +- apps/catalogue/utils.py | 46 +++++ apps/catalogue/views.py | 67 ++++--- ...picture_width__add_field_picture_height.py | 51 +++++ ...d_picture_areas__add_field_picture_area.py | 70 +++++++ ...to__add_field_picturearea__related_info.py | 51 +++++ ...auto__add_field_picture_sort_key_author.py | 52 +++++ apps/picture/models.py | 150 ++++++++++++-- .../templates/picture/picture_wide.html | 3 - apps/picture/views.py | 13 +- apps/wolnelektury_core/static/css/header.css | 4 +- .../static/img/logo_nck_200horiz_trans.png | Bin 0 -> 7972 bytes .../static/img/logo_nck_200trans.png | Bin 0 -> 20409 bytes .../locale-contrib/de/LC_MESSAGES/django.mo | Bin 10077 -> 7777 bytes .../locale-contrib/en/LC_MESSAGES/django.mo | Bin 9771 -> 7471 bytes .../locale-contrib/es/LC_MESSAGES/django.mo | Bin 10329 -> 8021 bytes .../locale-contrib/lt/LC_MESSAGES/django.mo | Bin 10265 -> 7965 bytes .../locale-contrib/pl/LC_MESSAGES/django.mo | Bin 13368 -> 13540 bytes .../locale-contrib/uk/LC_MESSAGES/django.mo | Bin 12141 -> 9261 bytes 24 files changed, 670 insertions(+), 59 deletions(-) create mode 100644 apps/catalogue/migrations/0012_auto__add_field_book_sort_key_author.py create mode 100644 apps/picture/migrations/0006_auto__add_field_picture_width__add_field_picture_height.py create mode 100644 apps/picture/migrations/0007_auto__add_picturearea__del_field_picture_areas__add_field_picture_area.py create mode 100644 apps/picture/migrations/0008_auto__add_field_picturearea__related_info.py create mode 100644 apps/picture/migrations/0009_auto__add_field_picture_sort_key_author.py create mode 100644 apps/wolnelektury_core/static/img/logo_nck_200horiz_trans.png create mode 100644 apps/wolnelektury_core/static/img/logo_nck_200trans.png diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.po b/apps/catalogue/locale/pl/LC_MESSAGES/django.po index 02d36e089..76b1aa73c 100644 --- a/apps/catalogue/locale/pl/LC_MESSAGES/django.po +++ b/apps/catalogue/locale/pl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-12-16 09:19+0100\n" -"PO-Revision-Date: 2013-12-16 09:29+0100\n" +"PO-Revision-Date: 2013-12-16 15:51+0100\n" "Last-Translator: Marcin Koziej \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" @@ -321,7 +321,7 @@ msgstr "" #: templates/catalogue/book_info.html:20 msgid "Resource prepared based on:" -msgstr "Tekst opracowany na podstawie:" +msgstr "Zasób opracowany na podstawie:" #: templates/catalogue/book_info.html:28 msgid "Edited and annotated by:" diff --git a/apps/catalogue/migrations/0012_auto__add_field_book_sort_key_author.py b/apps/catalogue/migrations/0012_auto__add_field_book_sort_key_author.py new file mode 100644 index 000000000..076f668e3 --- /dev/null +++ b/apps/catalogue/migrations/0012_auto__add_field_book_sort_key_author.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Book.sort_key_author' + db.add_column(u'catalogue_book', 'sort_key_author', + self.gf('django.db.models.fields.CharField')(default=u'', max_length=120, db_index=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Book.sort_key_author' + db.delete_column(u'catalogue_book', 'sort_key_author') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'catalogue.book': { + 'Meta': {'ordering': "('sort_key',)", 'object_name': 'Book'}, + '_related_info': ('jsonfield.fields.JSONField', [], {'null': 'True', 'blank': 'True'}), + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'common_slug': ('django.db.models.fields.SlugField', [], {'max_length': '120'}), + 'cover': ('catalogue.fields.EbookField', [], {'max_length': '100', 'null': 'True', 'format_name': "'cover'", 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'epub_file': ('catalogue.fields.EbookField', [], {'default': "''", 'max_length': '100', 'format_name': "'epub'", 'blank': 'True'}), + 'extra_info': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'fb2_file': ('catalogue.fields.EbookField', [], {'default': "''", 'max_length': '100', 'format_name': "'fb2'", 'blank': 'True'}), + 'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'html_file': ('catalogue.fields.EbookField', [], {'default': "''", 'max_length': '100', 'format_name': "'html'", 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'default': "'pol'", 'max_length': '3', 'db_index': 'True'}), + 'mobi_file': ('catalogue.fields.EbookField', [], {'default': "''", 'max_length': '100', 'format_name': "'mobi'", 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['catalogue.Book']"}), + 'parent_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'pdf_file': ('catalogue.fields.EbookField', [], {'default': "''", 'max_length': '100', 'format_name': "'pdf'", 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'sort_key_author': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '120', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'txt_file': ('catalogue.fields.EbookField', [], {'default': "''", 'max_length': '100', 'format_name': "'txt'", 'blank': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'xml_file': ('catalogue.fields.EbookField', [], {'default': "''", 'max_length': '100', 'format_name': "'xml'", 'blank': 'True'}) + }, + 'catalogue.bookmedia': { + 'Meta': {'ordering': "('type', 'name')", 'object_name': 'BookMedia'}, + 'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'media'", 'to': "orm['catalogue.Book']"}), + 'extra_info': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'file': ('catalogue.fields.OverwritingFileField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': "'100'"}), + 'source_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': "'100'", 'db_index': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}) + }, + 'catalogue.collection': { + 'Meta': {'ordering': "('title',)", 'object_name': 'Collection'}, + 'book_slugs': ('django.db.models.fields.TextField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'description_de': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_en': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_es': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_fr': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_it': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_lt': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_pl': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_ru': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_uk': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'kind': ('django.db.models.fields.CharField', [], {'default': "'book'", 'max_length': '10', 'db_index': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '120', 'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'title_de': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '120', 'null': True, 'db_index': 'True'}), + 'title_en': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '120', 'null': True, 'db_index': 'True'}), + 'title_es': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '120', 'null': True, 'db_index': 'True'}), + 'title_fr': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '120', 'null': True, 'db_index': 'True'}), + 'title_it': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '120', 'null': True, 'db_index': 'True'}), + 'title_lt': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '120', 'null': True, 'db_index': 'True'}), + 'title_pl': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '120', 'null': True, 'db_index': 'True'}), + 'title_ru': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '120', 'null': True, 'db_index': 'True'}), + 'title_uk': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '120', 'null': True, 'db_index': 'True'}) + }, + 'catalogue.fragment': { + 'Meta': {'ordering': "('book', 'anchor')", 'object_name': 'Fragment'}, + 'anchor': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'fragments'", 'to': "orm['catalogue.Book']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'short_text': ('django.db.models.fields.TextField', [], {}), + 'text': ('django.db.models.fields.TextField', [], {}) + }, + 'catalogue.tag': { + 'Meta': {'ordering': "('sort_key',)", 'unique_together': "(('slug', 'category'),)", 'object_name': 'Tag'}, + 'book_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'category': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'culturepl_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'description_de': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_en': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_es': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_fr': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_it': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_lt': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_pl': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_ru': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'description_uk': ('django.db.models.fields.TextField', [], {'null': True, 'blank': True}), + 'gazeta_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'name_de': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '50', 'null': True, 'db_index': 'True'}), + 'name_en': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '50', 'null': True, 'db_index': 'True'}), + 'name_es': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '50', 'null': True, 'db_index': 'True'}), + 'name_fr': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '50', 'null': True, 'db_index': 'True'}), + 'name_it': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '50', 'null': True, 'db_index': 'True'}), + 'name_lt': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '50', 'null': True, 'db_index': 'True'}), + 'name_pl': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '50', 'null': True, 'db_index': 'True'}), + 'name_ru': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '50', 'null': True, 'db_index': 'True'}), + 'name_uk': ('django.db.models.fields.CharField', [], {'blank': True, 'max_length': '50', 'null': True, 'db_index': 'True'}), + 'picture_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '120'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'wiki_link_de': ('django.db.models.fields.CharField', [], {'max_length': '240', 'null': True, 'blank': True}), + 'wiki_link_en': ('django.db.models.fields.CharField', [], {'max_length': '240', 'null': True, 'blank': True}), + 'wiki_link_es': ('django.db.models.fields.CharField', [], {'max_length': '240', 'null': True, 'blank': True}), + 'wiki_link_fr': ('django.db.models.fields.CharField', [], {'max_length': '240', 'null': True, 'blank': True}), + 'wiki_link_it': ('django.db.models.fields.CharField', [], {'max_length': '240', 'null': True, 'blank': True}), + 'wiki_link_lt': ('django.db.models.fields.CharField', [], {'max_length': '240', 'null': True, 'blank': True}), + 'wiki_link_pl': ('django.db.models.fields.CharField', [], {'max_length': '240', 'null': True, 'blank': True}), + 'wiki_link_ru': ('django.db.models.fields.CharField', [], {'max_length': '240', 'null': True, 'blank': True}), + 'wiki_link_uk': ('django.db.models.fields.CharField', [], {'max_length': '240', 'null': True, 'blank': True}) + }, + 'catalogue.tagrelation': { + 'Meta': {'unique_together': "(('tag', 'content_type', 'object_id'),)", 'object_name': 'TagRelation', 'db_table': "u'catalogue_tag_relation'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'items'", 'to': "orm['catalogue.Tag']"}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['catalogue'] \ No newline at end of file diff --git a/apps/catalogue/models/book.py b/apps/catalogue/models/book.py index 30b447197..9f7803dcd 100644 --- a/apps/catalogue/models/book.py +++ b/apps/catalogue/models/book.py @@ -27,6 +27,7 @@ class Book(models.Model): """Represents a book imported from WL-XML.""" title = models.CharField(_('title'), max_length=120) sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False) + sort_key_author = models.CharField(_('sort key by author'), max_length=120, db_index=True, editable=False, default=u'') slug = models.SlugField(_('slug'), max_length=120, db_index=True, unique=True) common_slug = models.SlugField(_('slug'), max_length=120, db_index=True) @@ -141,6 +142,14 @@ class Book(models.Model): for fragm in self.fragments.all().iterator(): fragm.reset_short_html() + try: + author = self.tags.filter(category='author')[0].sort_key + except IndexError: + author = u'' + type(self).objects.filter(pk=self.pk).update(sort_key_author=author) + + + def has_description(self): return len(self.description) > 0 has_description.short_description = _('description') @@ -405,7 +414,7 @@ class Book(models.Model): for category in tags: cat = [] for tag in tags[category]: - tag_info = {'slug': tag.slug} + tag_info = {'slug': tag.slug, 'name': tag.name} for lc, ln in settings.LANGUAGES: tag_name = getattr(tag, "name_%s" % lc) if tag_name: @@ -497,6 +506,8 @@ class Book(models.Model): rel_info = book.related_info() names = [(related_tag_name(tag), Tag.create_url('author', tag['slug'])) for tag in rel_info['tags'].get('author', ())] + import logging + logging.info("%s, %s" % (book.slug, unicode(rel_info['tags'].get('author', ())))) if 'parents' in rel_info: books = [(name, Book.create_url(slug)) for name, slug in rel_info['parents']] diff --git a/apps/catalogue/templates/catalogue/menu.html b/apps/catalogue/templates/catalogue/menu.html index 4fe776925..55a9e1851 100644 --- a/apps/catalogue/templates/catalogue/menu.html +++ b/apps/catalogue/templates/catalogue/menu.html @@ -31,4 +31,9 @@ {% trans "DAISY" %} + + diff --git a/apps/catalogue/templates/catalogue/tagged_object_list.html b/apps/catalogue/templates/catalogue/tagged_object_list.html index 96b628d59..ee207ff57 100644 --- a/apps/catalogue/templates/catalogue/tagged_object_list.html +++ b/apps/catalogue/templates/catalogue/tagged_object_list.html @@ -110,17 +110,19 @@ {% endif %} diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py index 6d5ff795a..7c1109008 100644 --- a/apps/catalogue/urls.py +++ b/apps/catalogue/urls.py @@ -14,7 +14,7 @@ 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_thumb'), + url(r'^obraz/?$', 'picture_list_thumb', name='picture_list_thumb'), url(r'^obraz/(?P%s).html$' % SLUG, 'picture_viewer', name='picture_viewer'), url(r'^obraz/(?P%s)/?$' % SLUG, 'picture_detail'), @@ -65,7 +65,5 @@ urlpatterns += patterns('catalogue.views', 'book_fragments', name='book_fragments'), # This should be the last pattern. - url(r'^literatura/(?P[a-zA-Z0-9-/]*)/$', 'tagged_object_list', {'literature': True, 'gallery': False}, name='tagged_object_list'), - url(r'^galeria/(?P[a-zA-Z0-9-/]*)/$', 'tagged_object_list', {'literature': False, 'gallery': True}, name='tagged_object_list'), url(r'^(?P[a-zA-Z0-9-/]*)/$', 'tagged_object_list', name='tagged_object_list'), ) diff --git a/apps/catalogue/utils.py b/apps/catalogue/utils.py index 33b0830e7..fd74c9498 100644 --- a/apps/catalogue/utils.py +++ b/apps/catalogue/utils.py @@ -192,6 +192,52 @@ class MultiQuerySet(object): stop = total_len - len(items) continue +class SortedMultiQuerySet(MultiQuerySet): + def __init__(self, *args, **kwargs): + self.order_by = kwargs.pop('order_by', None) + self.sortfn = kwargs.pop('sortfn', None) + if self.order_by is not None: + self.sortfn = lambda a, b: cmp(getattr(a, self.order_by), + getattr(b, self.order_by)) + super(SortedMultiQuerySet, self).__init__(*args, **kwargs) + + def __getitem__(self, item): + sort_heads = [0] * len(self.querysets) + try: + indices = (offset, stop, step) = item.indices(self.count()) + except AttributeError: + # it's not a slice - make it one + return self[item : item + 1][0] + items = [] + total_len = stop - offset + skipped = 0 + i_s = range(len(sort_heads)) + + while len(items) < total_len: + candidate = None + for i in i_s: + def get_next(): + return self.querysets[i][sort_heads[i]] + try: + if candidate is None: + candidate = get_next() + else: + competitor = get_next() + if self.sortfn(candidate, competitor) > 0: + candidate = competitor + except IndexError: + continue # continue next sort_head + sort_heads[i] += 1 + # we have no more elements: + if candidate is None: + break + if skipped < offset: + skipped += 1 + continue # continue next item + items.append(candidate) + + return items + def truncate_html_words(s, num, end_text='...'): """Truncates HTML to a certain number of words (not counting tags and diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index deab25967..fd66da9dd 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -23,7 +23,7 @@ from django.views.decorators.vary import vary_on_headers from ajaxable.utils import JSONResponse, AjaxableFormView from catalogue import models from catalogue import forms -from catalogue.utils import split_tags, MultiQuerySet +from catalogue.utils import split_tags, MultiQuerySet, SortedMultiQuerySet from catalogue.templatetags.catalogue_tags import tag_list, collection_list from pdcounter import models as pdcounter_models from pdcounter import views as pdcounter_views @@ -65,9 +65,13 @@ def catalogue(request): return render_to_string('catalogue/tag_list_split.html', ctx) output = {'theme': {}} - output['theme'] = render_split(fragment_tags) + output['theme'] = render_tag_list(fragment_tags) for category, tags in categories.items(): - output[category] = render_split(tags) + if category in ('author', 'theme'): + output[category] = render_tag_list(tags) + else: + output[category] = render_split(tags) + output['collections'] = render_to_string( 'catalogue/collection_list.html', collection_list(collections)) @@ -150,7 +154,7 @@ def differentiate_tags(request, tags, ambiguous_slugs): # TODO: Rewrite this hellish piece of code which tries to do everything -def tagged_object_list(request, tags='', gallery=True, literature=True): +def tagged_object_list(request, tags=''): # preliminary tests and conditions try: tags = models.Tag.get_tag_list(tags) @@ -178,7 +182,6 @@ def tagged_object_list(request, tags='', gallery=True, literature=True): raise Http404 # beginning of digestion - theme_is_set = [tag for tag in tags if tag.category == 'theme'] shelf_is_set = [tag for tag in tags if tag.category == 'set'] only_shelf = shelf_is_set and len(tags) == 1 @@ -192,16 +195,10 @@ def tagged_object_list(request, tags='', gallery=True, literature=True): if theme_is_set: shelf_tags = [tag for tag in tags if tag.category == 'set'] fragment_tags = [tag for tag in tags if tag.category != 'set'] - if literature: - fragments = models.Fragment.tagged.with_all(fragment_tags) - else: - fragments = models.Fragment.objects.none() - if gallery: - areas = PictureArea.tagged.with_all(fragment_tags) - else: - areas = PictureArea.objects.none() + fragments = models.Fragment.tagged.with_all(fragment_tags) + areas = PictureArea.tagged.with_all(fragment_tags) - if shelf_tags and literature: + if shelf_tags: books = models.Book.tagged.with_all(shelf_tags).order_by() l_tags = models.Tag.objects.filter(category='book', slug__in=[book.book_tag_slug() for book in books.iterator()]) @@ -230,23 +227,19 @@ def tagged_object_list(request, tags='', gallery=True, literature=True): related_tags = (tag for tag in related_tags if tag not in fragment_tags) categories = split_tags(related_tags, categories) - object_queries.append(areas) + + # we want the Pictures to go first + object_queries.insert(0, areas) objects = MultiQuerySet(*object_queries) else: - if literature: - if shelf_is_set: - books = models.Book.tagged.with_all(tags) - else: - books = models.Book.tagged_top_level(tags) + if shelf_is_set: + books = models.Book.tagged.with_all(tags).order_by('sort_key_author') else: - books = models.Book.objects.none() + books = models.Book.tagged_top_level(tags).order_by('sort_key_author') - if gallery: - pictures = Picture.tagged.with_all(tags) - else: - pictures = Picture.objects.none() + pictures = Picture.tagged.with_all(tags).order_by('sort_key_author') - if literature and books.count() > 0: + if books.count() > 0: # get related tags from `tag_counter` and `theme_counter` related_counts = {} tags_pks = [tag.pk for tag in tags] @@ -263,7 +256,27 @@ def tagged_object_list(request, tags='', gallery=True, literature=True): categories = split_tags(related_tags) del related_tags - objects = MultiQuerySet(pictures, books) + if pictures.count() > 0: + related_counts = {} + tags_pks = [tag.pk for tag in tags] + for picture in pictures: + for tag_pk, value in itertools.chain(picture.tag_counter.iteritems(), picture.theme_counter.iteritems()): + if tag_pk in tags_pks: + continue + logging.info("counting tag not in tags_pks: %d", tag_pk) + related_counts[tag_pk] = related_counts.get(tag_pk, 0) + value + related_tags = models.Tag.objects.filter(pk__in=related_counts.keys()) + related_tags = [tag for tag in related_tags if tag not in tags] + for tag in related_tags: + tag.count = related_counts[tag.pk] + + categories = split_tags(related_tags) + del related_tags + + logging.info("Returning %d picutres and %d books" % (pictures.count(), books.count())) + objects = SortedMultiQuerySet(pictures, books, order_by='sort_key_author') + + if not objects: only_author = len(tags) == 1 and tags[0].category == 'author' diff --git a/apps/picture/migrations/0006_auto__add_field_picture_width__add_field_picture_height.py b/apps/picture/migrations/0006_auto__add_field_picture_width__add_field_picture_height.py new file mode 100644 index 000000000..1b1d3b198 --- /dev/null +++ b/apps/picture/migrations/0006_auto__add_field_picture_width__add_field_picture_height.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Picture.width' + db.add_column(u'picture_picture', 'width', + self.gf('django.db.models.fields.IntegerField')(null=True), + keep_default=False) + + # Adding field 'Picture.height' + db.add_column(u'picture_picture', 'height', + self.gf('django.db.models.fields.IntegerField')(null=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Picture.width' + db.delete_column(u'picture_picture', 'width') + + # Deleting field 'Picture.height' + db.delete_column(u'picture_picture', 'height') + + + models = { + u'picture.picture': { + 'Meta': {'ordering': "('sort_key',)", 'object_name': 'Picture'}, + 'areas': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'culturepl_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'extra_info': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'html_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image_file': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'xml_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}) + } + } + + complete_apps = ['picture'] \ No newline at end of file diff --git a/apps/picture/migrations/0007_auto__add_picturearea__del_field_picture_areas__add_field_picture_area.py b/apps/picture/migrations/0007_auto__add_picturearea__del_field_picture_areas__add_field_picture_area.py new file mode 100644 index 000000000..91652cbea --- /dev/null +++ b/apps/picture/migrations/0007_auto__add_picturearea__del_field_picture_areas__add_field_picture_area.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'PictureArea' + db.create_table(u'picture_picturearea', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('picture', self.gf('django.db.models.fields.related.ForeignKey')(related_name='areas', to=orm['picture.Picture'])), + ('area', self.gf('jsonfield.fields.JSONField')(default={})), + ('kind', self.gf('django.db.models.fields.CharField')(max_length=10, db_index=True)), + )) + db.send_create_signal(u'picture', ['PictureArea']) + + # Deleting field 'Picture.areas' + db.delete_column(u'picture_picture', 'areas') + + # Adding field 'Picture.areas_json' + db.add_column(u'picture_picture', 'areas_json', + self.gf('jsonfield.fields.JSONField')(default={}), + keep_default=False) + + + def backwards(self, orm): + # Deleting model 'PictureArea' + db.delete_table(u'picture_picturearea') + + # Adding field 'Picture.areas' + db.add_column(u'picture_picture', 'areas', + self.gf('jsonfield.fields.JSONField')(default={}), + keep_default=False) + + # Deleting field 'Picture.areas_json' + db.delete_column(u'picture_picture', 'areas_json') + + + models = { + u'picture.picture': { + 'Meta': {'ordering': "('sort_key',)", 'object_name': 'Picture'}, + 'areas_json': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'culturepl_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'extra_info': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'html_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image_file': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'xml_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}) + }, + u'picture.picturearea': { + 'Meta': {'object_name': 'PictureArea'}, + 'area': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'kind': ('django.db.models.fields.CharField', [], {'max_length': '10', 'db_index': 'True'}), + 'picture': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'areas'", 'to': u"orm['picture.Picture']"}) + } + } + + complete_apps = ['picture'] \ No newline at end of file diff --git a/apps/picture/migrations/0008_auto__add_field_picturearea__related_info.py b/apps/picture/migrations/0008_auto__add_field_picturearea__related_info.py new file mode 100644 index 000000000..60411fcc9 --- /dev/null +++ b/apps/picture/migrations/0008_auto__add_field_picturearea__related_info.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Picture._related_info' + db.add_column(u'picture_picture', '_related_info', + self.gf('jsonfield.fields.JSONField')(null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Picture._related_info' + db.delete_column(u'picture_picture', '_related_info') + + + models = { + u'picture.picture': { + 'Meta': {'ordering': "('sort_key',)", 'object_name': 'Picture'}, + '_related_info': ('jsonfield.fields.JSONField', [], {'null': 'True', 'blank': 'True'}), + 'areas_json': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'culturepl_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'extra_info': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'html_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image_file': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'xml_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}) + }, + u'picture.picturearea': { + 'Meta': {'object_name': 'PictureArea'}, + 'area': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'kind': ('django.db.models.fields.CharField', [], {'max_length': '10', 'db_index': 'True'}), + 'picture': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'areas'", 'to': u"orm['picture.Picture']"}) + } + } + + complete_apps = ['picture'] \ No newline at end of file diff --git a/apps/picture/migrations/0009_auto__add_field_picture_sort_key_author.py b/apps/picture/migrations/0009_auto__add_field_picture_sort_key_author.py new file mode 100644 index 000000000..013e345fa --- /dev/null +++ b/apps/picture/migrations/0009_auto__add_field_picture_sort_key_author.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Picture.sort_key_author' + db.add_column(u'picture_picture', 'sort_key_author', + self.gf('django.db.models.fields.CharField')(default=u'', max_length=120, db_index=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Picture.sort_key_author' + db.delete_column(u'picture_picture', 'sort_key_author') + + + models = { + u'picture.picture': { + 'Meta': {'ordering': "('sort_key',)", 'object_name': 'Picture'}, + '_related_info': ('jsonfield.fields.JSONField', [], {'null': 'True', 'blank': 'True'}), + 'areas_json': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'culturepl_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'extra_info': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'html_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image_file': ('sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '120'}), + 'sort_key': ('django.db.models.fields.CharField', [], {'max_length': '120', 'db_index': 'True'}), + 'sort_key_author': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '120', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'wiki_link': ('django.db.models.fields.CharField', [], {'max_length': '240', 'blank': 'True'}), + 'xml_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}) + }, + u'picture.picturearea': { + 'Meta': {'object_name': 'PictureArea'}, + 'area': ('jsonfield.fields.JSONField', [], {'default': '{}'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'kind': ('django.db.models.fields.CharField', [], {'max_length': '10', 'db_index': 'True'}), + 'picture': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'areas'", 'to': u"orm['picture.Picture']"}) + } + } + + complete_apps = ['picture'] \ No newline at end of file diff --git a/apps/picture/models.py b/apps/picture/models.py index 2703c8091..705025a7f 100644 --- a/apps/picture/models.py +++ b/apps/picture/models.py @@ -16,17 +16,18 @@ from StringIO import StringIO import jsonfield import itertools import logging -logging.basicConfig(level=logging.DEBUG) from sorl.thumbnail import get_thumbnail, default from .engine import CustomCroppingEngine from PIL import Image -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import get_language, ugettext_lazy as _ from newtagging import managers from os import path +permanent_cache = get_cache('permanent') + picture_storage = FileSystemStorage(location=path.join( settings.MEDIA_ROOT, 'pictures'), base_url=settings.MEDIA_URL + "pictures/") @@ -52,10 +53,35 @@ class PictureArea(models.Model): pa.area = coords return pa + def reset_short_html(self): + if self.id is None: + return + + cache_key = "PictureArea.short_html/%d/%s" + for lang, langname in settings.LANGUAGES: + permanent_cache.delete(cache_key % (self.id, lang)) + + def short_html(self): - short_html = unicode(render_to_string( - 'picture/picturearea_short.html', {'area': self})) - return mark_safe(short_html) + if self.id: + cache_key = "PictureArea.short_html/%d/%s" % (self.id, get_language()) + short_html = permanent_cache.get(cache_key) + else: + short_html = None + + if short_html is not None: + return mark_safe(short_html) + else: + theme = self.tags.filter(category='theme') + theme = theme and theme[0] or None + thing = self.tags.filter(category='thing') + thing = thing and thing[0] or None + area = self + short_html = unicode(render_to_string( + 'picture/picturearea_short.html', locals())) + if self.id: + permanent_cache.set(cache_key, short_html) + return mark_safe(short_html) class Picture(models.Model): @@ -66,6 +92,7 @@ class Picture(models.Model): title = models.CharField(_('title'), max_length=120) slug = models.SlugField(_('slug'), max_length=120, db_index=True, unique=True) sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False) + sort_key_author = models.CharField(_('sort key by author'), max_length=120, db_index=True, editable=False, default=u'') created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True) changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True) xml_file = models.FileField('xml_file', upload_to="xml", storage=picture_storage) @@ -76,6 +103,8 @@ class Picture(models.Model): culturepl_link = models.CharField(blank=True, max_length=240) wiki_link = models.CharField(blank=True, max_length=240) + _related_info = jsonfield.JSONField(blank=True, null=True, editable=False) + width = models.IntegerField(null=True) height = models.IntegerField(null=True) @@ -148,8 +177,10 @@ class Picture(models.Model): picture.title = unicode(picture_xml.picture_info.title) picture.extra_info = picture_xml.picture_info.to_dict() + picture_tags = set(catalogue.models.Tag.tags_from_info(picture_xml.picture_info)) motif_tags = set() thing_tags = set() + area_data = {'themes':{}, 'things':{}} for part in picture_xml.partiter(): @@ -191,10 +222,9 @@ class Picture(models.Model): logging.debug("coords for theme: %s" % part['coords']) area = PictureArea.rectangle(picture, 'theme', part['coords']) area.save() - area.tags = _tags + area.tags = _tags.union(picture_tags) - picture.tags = catalogue.models.Tag.tags_from_info(picture_xml.picture_info) + \ - list(motif_tags) + list(thing_tags) + picture.tags = picture_tags.union(motif_tags).union(thing_tags) picture.areas_json = area_data if image_file is not None: @@ -276,13 +306,24 @@ class Picture(models.Model): def reset_short_html(self): if self.id is None: return + + type(self).objects.filter(pk=self.pk).update(_related_info=None) + for area in self.areas.all().iterator(): + area.reset_short_html() + + try: + author = self.tags.filter(category='author')[0].sort_key + except IndexError: + author = u'' + type(self).objects.filter(pk=self.pk).update(sort_key_author=author) - cache_key = "Picture.short_html/%d" % (self.id) - get_cache('permanent').delete(cache_key) + cache_key = "Picture.short_html/%d/%s" + for lang, langname in settings.LANGUAGES: + permanent_cache.delete(cache_key % (self.id, lang)) def short_html(self): if self.id: - cache_key = "Picture.short_html/%d" % (self.id) + cache_key = "Picture.short_html/%d/%s" % (self.id, get_language()) short_html = get_cache('permanent').get(cache_key) else: short_html = None @@ -314,3 +355,90 @@ class Picture(models.Model): else: names = [tag[0] for tag in names] return ', '.join(names) + + def related_info(self): + """Keeps info about related objects (tags) in cache field.""" + if self._related_info is not None: + return self._related_info + else: + rel = {'tags': {}} + + tags = self.tags.filter(category__in=( + 'author', 'kind', 'genre', 'epoch')) + tags = split_tags(tags) + for category in tags: + cat = [] + for tag in tags[category]: + tag_info = {'slug': tag.slug, 'name': tag.name} + for lc, ln in settings.LANGUAGES: + tag_name = getattr(tag, "name_%s" % lc) + if tag_name: + tag_info["name_%s" % lc] = tag_name + cat.append(tag_info) + rel['tags'][category] = cat + + + if self.pk: + type(self).objects.filter(pk=self.pk).update(_related_info=rel) + return rel + + # copied from book.py, figure out + def related_themes(self): + # self.theme_counter hides a computation, so a line below actually makes sense + theme_counter = self.theme_counter + picture_themes = list(catalogue.models.Tag.objects.filter(pk__in=theme_counter.keys())) + for tag in picture_themes: + tag.count = theme_counter[tag.pk] + return picture_themes + + def reset_tag_counter(self): + if self.id is None: + return + + cache_key = "Picture.tag_counter/%d" % self.id + permanent_cache.delete(cache_key) + if self.parent: + self.parent.reset_tag_counter() + + @property + def tag_counter(self): + if self.id: + cache_key = "Picture.tag_counter/%d" % self.id + tags = permanent_cache.get(cache_key) + else: + tags = None + + if tags is None: + tags = {} + # do we need to do this? there are no children here. + for tag in self.tags.exclude(category__in=('book', 'theme', 'thing', 'set')).order_by().iterator(): + tags[tag.pk] = 1 + + if self.id: + permanent_cache.set(cache_key, tags) + return tags + + def reset_theme_counter(self): + if self.id is None: + return + + cache_key = "Picture.theme_counter/%d" % self.id + permanent_cache.delete(cache_key) + + @property + def theme_counter(self): + if self.id: + cache_key = "Picture.theme_counter/%d" % self.id + tags = permanent_cache.get(cache_key) + else: + tags = None + + if tags is None: + tags = {} + for area in PictureArea.objects.filter(picture=self).order_by().iterator(): + for tag in area.tags.filter(category__in=('theme','thing')).order_by().iterator(): + tags[tag.pk] = tags.get(tag.pk, 0) + 1 + + if self.id: + permanent_cache.set(cache_key, tags) + return tags diff --git a/apps/picture/templates/picture/picture_wide.html b/apps/picture/templates/picture/picture_wide.html index 46e37c9af..3125b0f8f 100644 --- a/apps/picture/templates/picture/picture_wide.html +++ b/apps/picture/templates/picture/picture_wide.html @@ -55,9 +55,6 @@ {% if extra_info.about and not hide_about %}
  • {% trans "Picture on" %} {% trans "Editor's Platform" %}
  • {% endif %} - {% if picture.culturepl_link %} -
  • {% trans "Picture description on Lektury.Gazeta.pl" %}
  • - {% endif %} {% if picture.wiki_link %}
  • {% trans "Picture description on Wikipedia" %}
  • {% endif %} diff --git a/apps/picture/views.py b/apps/picture/views.py index 6529e8db7..f98d749fb 100644 --- a/apps/picture/views.py +++ b/apps/picture/views.py @@ -5,6 +5,7 @@ from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from django.core.paginator import Paginator from picture.models import Picture +from catalogue.utils import split_tags # was picture/picture_list.html list (without thumbs) def picture_list(request, filter=None, get_filter=None, template_name='catalogue/picture_list.html', cache_key=None, context=None): @@ -36,12 +37,14 @@ def picture_list_thumb(request, filter=None, get_filter=None, template_name='pic def picture_detail(request, slug): picture = get_object_or_404(Picture, slug=slug) - categories = SortedDict() - for tag in picture.tags.iterator(): - categories.setdefault(tag.category, []).append(tag) + theme_things = split_tags(picture.related_themes_things()) - themes = categories.get('theme', []) - things = categories.get('thing', []) + # categories = SortedDict() + # for tag in picture.tags.iterator(): + # categories.setdefault(tag.category, []).append(tag) + + themes = theme_things.get('theme', []) + things = theme_things.get('thing', []) extra_info = picture.extra_info diff --git a/apps/wolnelektury_core/static/css/header.css b/apps/wolnelektury_core/static/css/header.css index f1bd0df94..10d4956a1 100755 --- a/apps/wolnelektury_core/static/css/header.css +++ b/apps/wolnelektury_core/static/css/header.css @@ -150,8 +150,8 @@ li.menu { } a.menu { display: block; - padding-left: 1.4em; - padding-right: 1.4em; + padding-left: 1.2em; + padding-right: 1.2em; /* must match grid-line */ height: 3.1em; padding-top: 1.8em; diff --git a/apps/wolnelektury_core/static/img/logo_nck_200horiz_trans.png b/apps/wolnelektury_core/static/img/logo_nck_200horiz_trans.png new file mode 100644 index 0000000000000000000000000000000000000000..897a8352b8d34b8cd3c8e65704034055ffe33460 GIT binary patch literal 7972 zcmX|G1yCGKx1C*_;1-+(774Dw0)aqaad%tX-GgLtNN|D$2pU`xT*KlLT!RG&?ye#5 z`0B5EGu3mex@Nk&s_)!$`kZJr6**jNN^AfCaOGh#8c%WHslhPOpPsclEwWDm-CR*l z26+7M&TTJFe(J$;h3R`d1?B%5C_-Ek{M3ozDX;t%V;KhvM*znRTgn^&Xur$LNNM>l z92)!iQq9yqW>XA$6WM&Xj3i)5lud=P#%)Ss_o%>>^tudK2{1Ve5AbdCkOi+&H4KI{ zd^v+BYq*`}l_BGAVDn2o?g;DTWL+YtLo^L$^kHEz!Q$l7`NKGjR;)%q!|^(>+T+LA zgTT+`>(TaYpRwjek&RmI>=EQ8t`J*fzn&`ZFGxVogE=arull6lkhkt12YTO2YChfD?!WTv&8gVUl=#*o_6mCzQ~zrH+eTYg7` z^abGK{Iz2L((o`~05i%eSI+HiBpfpoWr1C_^*p^4t% zQufPYgMit2~3a{K=W68d` zX4zZ1H3o~L!KryUab%<~7ydT#>~JAw(HZkH{sxBT2Y~y`0NE>$_zvF-thgTB9|Jb$ z5@-x|C7tTdi)Y0_s{len>Y|fv@uDmbT~1Seh8M!28p1g7>`!i|LQa%L3;W06Cb?^0 zC;%%fvsRE!>gl~xj4OR^^Uf5L7aQ?Rk}%YiscQ^$0Jad7R9tqx-tY{djof!cNQ@&- zNs;&{_q38^U{jh}z1Jtcufpy4t*RiBAT^-xC(w>|!U~QvA3tWJ9%_|=7LWK0eZo#D z#D!+&?cH7cN=I9t>-qs*#k9TYRgH3}mo^eoevRo-RC zBd&Y73$j2%G=d&9vu&*?etO_=P_CwEL>J;yDzddpW8tOl zH~|F_QtC{~ef&!*Hp->|%%0h6EQ}5+%H=o<1n^qfKWCWzcZJrqCYF9$;%$Rky0>Ph zl;WmzZ>wRU7beO{9(~VB9!-tkmAT-cozL=MIxnG>|6MHCDqAr0rXC{oXYC+RH?1_eTa$LsxSjX ze7`38Y6&Xd&h5ozL}70oJ2@^lrfgR+Dr4fa?zNMnU^f5n)rET*o#^u7oS#@08eXX) z{q#-hlc622Ng(>#8iGFLogl;t3$i+TJWyV0xKD7>-nj5GLLKx&yH)&a*P8G|6B#4; zyIkwi9AnSW!|}jyE%2-UEUJ?xqoTHO&Txkw$01JxQ;gxS=xBAHG%?kjESnM+FKDK%BF3(VX z6!xH;{i^wuv`g5tOog&9G#m*6YS~u#PUp{nQNaOo7Wtr#QcKxOc(lF6e2m}Zw-6LS z9h81F`MG|!fYk$Z$IMZ@HH87}@JY|}IhVQ$UlZ*(0%8;aQUa9A^&Pp1 zy8Rpb=V~cbp1M=+cg|*5J5^Ug#uneZJ!=|dUndgSKC1)S%G>{#m9PApp)Y=VL)A`$ zJI1n=TvvC5@r?>Vox^SgZ z)pNigov}Qd>GWYQX^#Tq6@bIb+ZIn%HeUw6s{i>muj4DPmC%~YZwgBspv z*`{%=D75dUlh?WuX)VPbIsLmeG&B^;*hS&|XF>0(VIE+9%a6$}Sn0zSstO?a&iQ5c zVzB0T*F-xf2NPi%9}gCScmHlSjKtGL#poSozlWcQ_|+_i^&H<0wP~KuQh)tj0J-6w zM|MUB|H<7auqKn|kCrM1$TPoFXJ!2Z13yS(p8JEO$uf2)4@M$ZLcZz!YD1CHR@7qokESrEjc)tjEkbI zNxY+dXhT65X9Jhc*^U?SeMHZ?;>a;5 zo2qkR@fTn=!jR-2=^@>6*MIf+NNJsm}on|VBg*>*3X z_bNH{BlzmJ!O)?&{e9Z}>kR7_QmgNarEMdtwE}DUge*Z^%KF-fpwAk0F+xw9Y8*c0 z#~W(0PjfRBkKtGi_M-smeTb5NgMrlD?jU&J0=zudQ_LvMkGjfZ>4UpTcs>W8=nu#X zF6Ip;7AVat)-&{>*EC(7z)N3HebS;hM+`1dW6@97mF}_0xrve&&A+wJO&{2sWW?uAd)}>F&X;|j zumQ2fKcwFX-NvKb%t78c3xgO4=V~DxG7MHGVQ=U=7I$CK$|QpVnj4F^YyW*OYVJly>|jI!@B9Tl}bxM(FR!4rANQT zDJ65>zIGx!xtAu{l(7ChNT3VOH_EhU{){ac-#S_@*e_D1 z&Z(lk?gg=FHCZP&8ynk~aDsY*bQPHug44dLrk1jL!O4vFFK)F20;Fn&;E5ekPU9;j zH)94%TBdU(vq2g48v|BCF}*+LJfxu$xY5@MZ%t%l8_FELuT#@2Ml%~NrmosBcGuZi z^t5lgvuxfZU1GL)diO^Z!Rh1u}a0kmXe-+IhzQ{uwKzKN@Kpc*zC{B9A|t!;=URN~78X zIw^kmX;u|XMPg`$eX{W|W0!_-W-6W$Msygoj;ubHeNX!H-?(shhTYhj9pe}SB)d&G(|Z_KICFmk_)0Q{$rA}{Eu|u zylHQY>77Em&^C6Q76{=G(6F~ciQ4a|iK@hhM<5P!oGC7{uw~0Wb;8^=hK@ZPsZvNy z>gfbVscm-9yA8Y+=RKl@C6g~Syt$Xosk~eBjGtYwj;k*_6Jr)u8p4OXNI034&AhIY z5PxrXnK;ZqEPRJ395nG#Hyv5eR(K--S@U9KE#Bpf7zxZNXe_S;#scU)!;3_7G>!lMva>a@3G(+}>)7S_^yJfsfe>P_2vLhrcWrI`CzF-^YQ7yQ-t z!$m?bOc6YxT$Q`FD*I^pMaz#;-U%m{_1O=9r^GXAr#eP~{|36LR?K`nish_H*s?~A zMvwEjN%==zJ92BkJepoX+ux42ti(6SBZ=e0KL$>s`s)x!EyV{BPV&5{x+K)@ZL8bT zL3f4P{f<*!tRz#nu_sD)M;o}5ZOoXf`}-P_N_Lf^e5dUc`PdPAGB&?!iXoo`v=gKB z(bWbs%J&~!MGAV9`^2DD44}tBX)ZVWxZlPbVZbwApflsjyer22{wrNPeyhu9s`-I4 zXL%7D&Ib^jEE?T7_&6J_*ZS)+HgnN`%(`*#c5$?;^G_poZ9pIIk3~qkI14VcD`GWS zm#{UpwFPWMg@l9ycxV=UD2t%q&?u7uk~>7ye-I*u`iooxi@*3)>NlmIf5kj^RGr^8 zhoDhI&7iza*fMWa!N@whDcSxjuN9@9f+tL-bBH5G=W3mK$Issjp?A{5G=C5#j}8!4 zqx_IhGC1soT580j3dMSf-auU2!9;0nhFqjyQrVUV_9V6R1nj3{Ghbdtl$XtOT-))J_u z*nGs$CQDEjb>>+%yR^f3+iV+FziRUBcWBU`j$um_p4^7)NIwmBhU)J{RHcnk9Olhs(KE4+Q^WWzG+lPnL&W%aPR&|Ikq`I@K2L^LVgjlQ7j2WBR( zAmNin3EIM+>VIPqZ{0PztYr3%XW*aU!r?2rfA>HFSEJ0Q+NfCrO6(v@G)(;-reRM` zLS%0H=H}*lGOsk0K4>I&Jqlpp#X_=`9#5y-g^V4EFTmETsb6<9Mx{!#7%S=hVL$)k z9QIy&emE5E{O``XOswn%whV9fW#OYoL5XpwTG4>f%QF<-F28-C5*2jJcd(SLRwzO1%IvYCJi2)(_Z{NvPLi*2AYt zZejp}bCQo>6m?Ar@i<*B=*o?0L)65qIZOzsaoQjIJPlZGMV%zkgp>==6mVC+!WuNO zSxJI&ptM@CLB?d66!?XBLr5* zLpM&D{b#J6inDJ1kv&Ic3+Hmd=EjR9t7Ow{deq3N-B;u=Egr1*5dhbFD*MjIu4pEd1zl@~t&9NT}ed~h z=gX=GD5y(OcSavFY0{F5U1OX!_U8|_q|$HIJlh}VLK8Oh2vktlWB@dgK7fNkO8z}u zFsWzekq?1THT(0L!3}og@llQ4(7@m!Bjuxiffp9N{@o=l+Csp=7suV0I)h^7J1Xb2 z_1U?4is?7c*5`m`&;yll^AA}vpJspaqDtlhCZc13R2>=&K$(fZjD9?!+qxSSb};iS zxQBv(ejJJDZ%XNk7f5+m4NAN?m72om!^Sh7d1xCn3A90((Wg|fj-j%kYQ}*z4bmxS zL_g&1|0{M-k2^c|rN}7>Bebk1c{*F)is%c<%&yL!XxKVPD7_my?Z(0HGX+7FYopop zaSqF7(Rzbr_7Yn%Bt!%UyW5yr!_#=GG3z)9zhcul?3z~8&+tuFj$r8v*E;ENFc#f8)}3QN^aW%&h$`}R+@n_<s>&o%}bvDNh?j&j}98Dr;j_DV)4U7VmF(dk8&*+dA{8!qEx*fPr#ZF>I1fr26sZ$&~ZJ(P6z9t}@`CVv1TF0#$q<#htS+MPlBEEd8gs8(GYcuZub>?A2L$c!Xux=eE zJEGC`Kc5@O|MU(Q(R6re^~<3+)38#if5V9$s6?^rNXh8x8`-se zAPDoWy;UB122r~I8Lwb1JSA@EkSUTHe$mRY`@3%FrMQ#UBKq4GH6>@Y8HfCLd1E@+ zv*%_{Vpwa7g(~pPIz2+TxU_Ch3`N|zM*CmU%1ggwr6KuHVJ-RlN;jjF@_5&@Ve+}R z;>Sd;mZ_eKOi2>99HkdW7;)kjsf4cPWDn*9+{gGy?+jS!92d{1$+vCRxZy>2eUp3q ze_x->m(iN98r1G8i++<5cp|}5Lm}dChQRNK& zU+qH8vPoD`80Hq5Z@00tetzpXgCDvhXI-0V`6;HN&;?~(ihxTdC=V=}aVxQ`pgchjMs_?Tw+(4p=HFEJWtSlTB9P4?o} z(@<5ZqUD-xEyQh=OX<1r9H!$@1BiuD+-ss_v-kk$#*~0709gAt`cf8(-1C76I#+V) zEj0H?Ob=BaGsEujf-)?@g*l#+gyl}!Q9_n5RSDx8oe+FDSeV&o`1m zf_Xj++TS`)7N;1h2LXL=c=9`Kh0B}wbUrb`eF0}IS%$#2@BUdsn!~ItbOq$eWWWHy%RTnwK(Iey%C(|W+hGDA;Av&7Eqm6XO zCoTiMdrgjI70bKsE$RU5>hYvWcgdKilHY{r-Fdh=JV|+@*{w!g7+6b~QK3<+(?rqt zx=H=(uExmKT3Eg!i9@)tr#CP}x4B)9ub(T-0x zp3X>-p6P&r3|kRQdB@JO@C#I=8Bw`yRt2)92TICBaSyRa&ZsGVx)||R~HCXn2j#IR3f$aGl>c!oYfnw#N z8u1pOhQHKd&JTzGAd61GCPb$}g`*Oi>a2N6V6OIO{F2U7tLw`1joLxwS@jmq2ZD}I zefxN#R{$+G)LE;XEvru;Pcd>*zaa%)Lgx__>UIdo*>aSET2#MR8+j5e?Ql9B%>RsE zTT3g(N-Ypf?aM@twQN^%(r)tWMK?xYj9F!)l8mLV>^I5SKwTzQHYqd6W|SdJK$(fv zvR;c~LiX-HBLn>=J+Rsh_w%El-f#jb zM628{YF8|(LUHEcs++}t7NFNrUA&#&*~?HWa!n}$Z2V{ZxC&EG{7LfM4|_$47s=S% zBqONCBS`n;p0#C4aV}Uz5`KBYQ`}FYR9b!Ce{~Nxk*x|NpI7SFGFyW!$zhmAKUZr3 z&rXK-EEE&ACwv2efl)0xj~iUhL+gg9u8IA-@8XEo4bgXK3xaw=)=uc6a+`U+K1(?H z;p+k+($DcD`&lUE=mo{kAeGYgz-~uk>1G0YUqn2t_tL-r=cI|&LbuN^Yo<9L&;%3D zG}Q)P<*%g0O)X^$Ge-qmCM|{J#c#4+xy~aQwLmErStc_u1--R zg|j0mUHoTf-3DrkYdv(%9)uWWFK8u*&r-9Pw3B>q_Cbjo$HIypp8N`$AU0*V@j{ZA z#Et)FItw5`n+p^nps7wrAXsvJV+*$QwXXBtiMxw~kAIYZ{-EE_g_J9eAZcoI53q83 zvLnr{IaJKPI`nMiQHSDuW*WGURw*~}-6@p6`VX|_S8Jx(-|$h!vI`ERJ#k0u7Nb^< z#AsTo3cgH$0$oFUkP;`-G-RtQ2gKdK-oe*psAqI9Q{!WKa@`_#6zd(kv=|YFB2IAg zS-E8yJ3^VGPv?m~`G}<~I~SiBNUY3EO(iGa*Zog|UX|$RH;?rWZ1-`f&Jp+__KQ+r zqa!`zE=(KK3AYk_qLMG&W#{}>L}+YlZ=JIroUlT&ruDqtgJ2|@b69UIrW!N431L~! zvGwI<9ikOtS7bBv=>u}Mk~3p+zRpfFzJFZ=`};3d5C=Tr8Bzj475z05JmK2zR@uo; zA%GW_2&e7L6$Z8Hr#SOf;hw(0>;--dm77$gqDA#O!^h`QuK+E~aRVM&hj~iB1z*Ec zJcb*JUo4il=V1ar(>#;{lQ8jk$cWV26L)QFdMI2niEd>G(#Zq=j6HI5Tr=AIl_j)) Q^0NW?w<{1zb%3?mT&U89sC(w*zo4 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 literal 0 HcmV?d00001 diff --git a/wolnelektury/locale-contrib/de/LC_MESSAGES/django.mo b/wolnelektury/locale-contrib/de/LC_MESSAGES/django.mo index 4cfc69e47c361e1d8c0d33edfa083aca1675eb31..9dfa5d3ef6de21437fbd47e9ea6b995bfe8db841 100644 GIT binary patch delta 1792 zcmY+^duWYu9LMqR&gRV7cI<|Ox$NRHv(3iLW@hF#w@EF;A3`q4r75wHlafnr=}C>O zq)e!Zr%CY#^M_0RAlH#ak!a+PrL-3B&$CDIYv1#Fo^zhx?fd&ad$;Jr)M!Uq+$p2g z5<`jo0kaL*8OI0hP|z$M4`Vu>#1y=M33vn3@GeI1Ifk$klkq2x!XSgFUq0&lN-Q^v zS}hkmV+S!6kE0$u=X(w7xo^WVoY>dvXq!<3HDeOq@V$rqxqpIV@Dq-}FssVM0@U6X zVL#@#Ixcv|)}SWd1tmvs-1UnA$w3e2!?y_vv5E6dAJy_q9!75-VRUWLT+0y54*80#%wg_qLl9< zox|`9>Zop_YUKe^X7)CP{1S)JhM;>SmqmD#f6GtBWi%{s0Hl9k=TS&u?=V9 zZ*0bygE(jWidtwYY0!d-QT>;op5KO)jkTgCzK41%9wXyN?X~|y51!(OAlsRXXHgS< zLhbMoI(NH;G#>3aXQno>fA z@wd(MFSH{Sl-dG91F4dgV(q?$&@QzzZ*Wx8nJb$G#B4&XmQY4@3~H2=l`+3pnR<;_ znAfWLoI$9-W)X7;ow=HJR!&SI<`YGP+O%H03@H6-I#$Zd%ictNLDNy@6NQ9c!RRDD z^p0i{O5%UFspGBzRtNb5ygwu&iLg$}LOd#}@{L{3)-gjA5 zTH@WAIpx8%&gWp3n-bp=a1s;3&acooXG6j*w>U8raF+M0j@!C=%Q|O$a-mz2+7RPD zN(;p~MUj0@cO=uz85E9nR%VnsuQJ}acZT*2IG;x>?d=fCs&GGMABb@ejeHUK14j<2 A$N&HU delta 3983 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 diff --git a/wolnelektury/locale-contrib/es/LC_MESSAGES/django.mo b/wolnelektury/locale-contrib/es/LC_MESSAGES/django.mo index 11bd9156145c5a022d25526591a9f3eef7b5b6bd..a5ad327ba30a3a7c2eb7a7d44caa983894744a74 100644 GIT binary patch delta 1784 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 QW|p{5vyQ~L(d?GMAACcv5dZ)H delta 4016 zcmeI!TWB0r7{Kw9^b)hSsWwe()7vpMHq~YuN?L2I=?z*6DyfN;YN1YdPj*LVXSOr5 zS)<4(DvEunU9A=jih`(AQC3Cilclww*oO)}dB0!4JBT3S|C^m8N?R)`>VtvgHezx&Dm*1c2A}Q1!SRAhghQkUTZ?jNR+v_FV+t=>hNs;^P%djjj0#>p*& z|0W+U=Yi~i>6EUd*h5+Ka^juHw`w2G!KYAW^a^gmk5C3$dRA?BtVZdlA0=Z~BTdwv z#3yhA_Xi^`SO(QdzNNz!lt{afS86@pj@$48d>=2wRvOFu0hAgkplr(sB?A>)hE+Tl zKSo*VsYEYO|5@i8o-#@R$6OVNYp;7)9j^4-hD%{h+Enm~8d&R8xIqI%T$+UI%TzV1>5lYR7i3 z)8_i3?VGIjd~Gw7J7hU_Hm&=Po7`{GWd}h?CgGa0K2g`c*;z6{7*AdeomX~AX?~JjWNca9`n0Y*Gi76>+dE2u@waE~py*h`ivw+iJd?<_ zyH>&QAa4aaXAiN|@o?Nx192L(mCX`)kWQrrywRlAS`k;q^L>*Em*|`)F}HWHj`om6 z$8oWx)D?lb)R#fzD!zy-)b--hAgu8P;6{6-%qL5=rs?sINJK69m7k`hqbpeuauzW9 z@DjbF6cUi6nldaEWpOhl;&MZ0SZCtONGm>xt194eJh@tx5cJqSk+KQBo@BeoRvEjL z)zyOp5$8IxGjUnT0y&OXjz^D6tmu0~c9t5W-$C1@z-};Ou@M^YGm^~FqC39yv!0gZ zkX6HaXgkzpn>Ara>Om@ytyBsl6a3zUq@;+oRu^NTCS{&+?96qVEg>0;W47IZkhv#s zwH%LzmpVy!ZKsHj@T!Za4%am&dyssLWwLr-Q~$*ECpp0+@;`BaeKx)PHy&Wyo7)@K z*A9>BtaTX#R1F! diff --git a/wolnelektury/locale-contrib/pl/LC_MESSAGES/django.mo b/wolnelektury/locale-contrib/pl/LC_MESSAGES/django.mo index dbd1ff37dcda7dda744607f925b73a983a9007a2..3912839c1c18f408a92346130964b44e40138a9a 100644 GIT binary patch delta 2461 zcmXxlZA_JA9LMoL9N>6EhCFEUke=fMqDT}zp{Xb(3LQ!^K~%)d;7NY|GGJs zyw>A?UzqiR(NaV?F&r_|f$}Inw3KJI8xP}3oIst*&NVB-={N)9$e-2nQ;6&ECftfm zn8XC0MIK@QVZNDfUS9aX5}d_}I4;CG)PwgR7g`^#MjvzVJQm3Gs z9>mwN0gHL)i`b6ya1x{RZ-u0%8%nVlS0FEM&8SS#ZIH9V#N6x>cvNq z(X|gz_kV(#+6kPG*Ks4}la;SIZDnHvp29l(42Lk2-ZT|2VdMVRXl+|;t1}eP}GmI(~GS_CElR%*ARcqf#o=k+!Kte=DLXS)%h0G2(`KW!&uAy zV>p4MsE!_|fK;rdRNep{#jW@c>Uk}*!^ED#E$qiic#T)s=pl`p(W93XE<)bLhA@Gn zxCH;kZ5fP8nb`vNk1?)GF^zL^9QB;*?zqPeqiriui8UhSU>)vwx9?6IMJ=bNF%#2x z3!cGm=J%T|R0#mtzu_p%NTMb^IPuVYX__G|!GU>cbv``S5)n$le6>rOn5r|=?H zs0^DZaTQq#HHXJg--DM>9bLj~yo@pY7BwP&p)&OeHkA1_RF#&|yXJU1zKJJLQ&CkJerR@}&OeFj=zY`(e2N-@tGEnjFXkhJ z2|S5cuo}C#Sugr5`Z|$jBO4bmv72xS>PwcwwfGz=)6Y>;as_kn57dkQi$xi9&Rc%O zeiOZPVU!9!gWb3ty_)d*(uI$*KUBl`?`EUm)^J@O!iDTdZ!_!1YRtp8@GkrSNzNiH z{w;L46d5#ogmk&phB1a;poaK2q%ZUMxTrW+V*zePCDgH;@z;q19AHV?5mbh!F@o

    j(7o-%Tq2ivkmw`Sb`p9iPdhZeS(dN)pK4m)4-oed zJKVulxX*3hkIKH$?Pp=aZ8P%LLevp)LQ71Ub`ol;{Hexl*k(eB`}#;|Xw_JGc7WJV zsG!vDCiW7VPwFrzFbG-^znZS8Mi~Vy%I|vOA>s~V1ED%$wt@n0L)XImtK42Ts_;}3 zOr32bHW6x?U$t(cIoJxn;o3Yzw7O&W;X$`ujVja*LUnL2p?Pm0m{D7&^{+OcP=%%l zz6-%W4G4;FhBjzgYWnY|dT1i_mFy*^5o&7)t(JQ2(0Ygn5hGZ6LEGrug~dc!hK=ov a=0pa+%Iz8J&$}>l@ZEU-Sns^?g8u=HN!OSF delta 2296 zcmc)KZA_JQ7{~ETo|HIbfJiFgEsCN7`9KWur1`Wkp|u{?3NVj&3nz~E!E^9o>*3(a zt={Ni(*4d*9%(ddO@ptg$AGgLva?@TjemeLoI3TzhgCSB04?)25P-i=wp97_xFX_MV!J1*Kr=^W|_^z8g%eUOv7hT8+9VF z+W}Mnhm!UsKOew+=1*cd{)9!C?K7K>Wf)?AyPu2I_z}L2SFsomWG6E7A?kqvRDj!^$r5gkj|B2;GYO}Y;GwR-+>Mr*{F7HHu@wfAr~?!#5skE;1N-iM{+ zO{omxA$%KEs^uKN6a%;%UqP+&HBQ4(T#0E!=f}0!iFWE?bc{CYK}B{3m6=~q zDc(${Rbm@5Wj&}`#!;V-p)zzOInE(XOBheb@30=VPa*YCGY{a4STL1c9^&E%ou>uI zP$!#5>(=7u_!ef~X?7R(VLN_R0e0ad6U@HCIgF>xpw74gXJQ*_oqfsq zz8Dwk#vf28`xPmpT}#eSq%N8-Kn`aMFa=j&1+Ky~Y}k$)8Q0Qb3gjJ3$6-`YoJFc& zqp05a1GR2!5@G2*oq;vD6gQ!Vl--6<0ga$aa{+b#Rn*BQJBb7MQ481MLVN<%TYFKN z+K*bN7j<7hlI)lr=c1K~Q&^4uS&35YK%KZB708#Ut{+Aov=OYr%SbF%#w+7s9qRsV zs8m0T3Zx5_iC$DD;+Ur{8{~p=*;%{?FQJ;Llopn2P&MC(YjFr)!YLGZE_S1ua1a&9 zx2W%rG1Nx+ydo;aTy(GuRq}fDvA;EOq4)G@)B_RJ312{^ybE>mqqq`3Km|6AO0|zD zl!^x=DW8da(oQI+E{)aO59OdF+_CNfZn%0Lw^#Sret9^8xN^AiiiQGx!9 zn!kkUxSLhi;qyp;*&zDy9IECH(W)fnn1LG>l7B`1FcWzb%wEP%8Fv%zQ`kVbz4!?} zhD{ZT9*N^_#uu;=-MbU7?H5?WxM7jmbGQey@K4-;S&I{2RF7jV`*kjEP1Nw@6230E z;UYRXi{mTBtC2OW3Dv!?qMGP1>O}7+=Le8C)=r>Kcpg(RrP7SOETb~+WoD$t)_Xex zq2LxL;zrxUUetAVwFX136W-hr2zo)U#R)VwyV0oA5pHumXKN(9%_%O47FXCUAB?!$ zcev4x|2|n4ZV7tMnt<2rh7NZ6nyPQ!BO2K0ZgIATBgu^(3=`bHm%4F7{Iu^x{{M7> f@UDOtbe({A@ZI9=HN<#Ro^FJA>oere9?tm-mtj(% diff --git a/wolnelektury/locale-contrib/uk/LC_MESSAGES/django.mo b/wolnelektury/locale-contrib/uk/LC_MESSAGES/django.mo index cab4ca5f1be2ad8b8f7bd96aaf10b6f89e9049e2..d6335b97cd5037f71d8c48ebafac8f0494b9d61c 100644 GIT binary patch delta 1708 zcmY+^duWYu9LMqBIXgSE&1Rd+HrqMQ#^y3Ma~ZoB2XiTjZIRnpxzt<><&q91|B&#@ z|~TW#xjM+FByWkou!mX(LPkGlbV{*7ZI3I(0VLNul8=m*D8|P0@YyFJr_zgQ@fP>zb<{3u)zYM!$ zEryxjG&1PPh1FPyJCS#ocJKN{)I<+372hK(H{URVzfca zt(@P*X*iv5#x&6p2HIhD8mq)Kf$PRZ1NErGLf-WBTD^rdTKT11Y%|6vT+ zl-mLv6`sE>!a(^_j;TfW7fl7L+^eZps@+jM?;0vCia;WLFkPSK-xg({&8m&pf%)CM zXskv#m@@hZI*~PXbiN|DSZXneUPUKJZd2&h2GRMf-JfV`(EWUeAkpVn&Y0nJ)sxz2 zx>BrYlWl%tb)Z$!Rg7x-;jxv$h4xD@+sQ~O_Suu2qP8JyUJWg9n!~SsPJQG;g6&_l)8Dbp4lWAY*5W13@qTChwmwqq94xt?@CTaZt?U2* delta 4624 zcmeH}e{5Y<9l%e!(RQy=#@Oa$d)u+ItaaNPBExoe8(RZ&5tqRVgSfEWzIWbx*WGvT z+r9U-6l3mi8lr`WJe(RWic3sL#Gp@RDgh!<_=9A5&7#q$ghUK#T#%TkGbH-?-g{rm zoPd$w{KF>g=bUrT`TjoV+ebch=Jx3MB@4fx_&LDuDt=#Wm)ZQ!FE2{I{R(!nyrn~_ z%ivD97~TRiumqPtAHEZQ8t#VEa1ne7UIJf%BBzZ%eM&`Y5d#sl3Cf0R;Sk&l*{ePa zFNL3nvhPI7@4+hbAHzX-O;>WxgCgf)_zpOe@)@|4`DwTg{uZu5zgmoWnuROjLU<$W zfww^RC<8^Yn=(qj-v^hm{x!G}J_c9AU&G5`J7L)gv#=K)g4n8#LHRxfm!e;NlR-Cp z9E!)!LJdzr@%&{dp0u$^1oWg_4#m*bkS%H>l=Fj7Qn?+jg$Ljccobd(pN3z8XJORO z;C`fk5Izp&z+d3I;L3|R1UJKJcoK?$QM8J}DJTvchZ3pf#Qkcx21+fALRl|C@wl4K zr{Go0AGn0$rx-lTf+#pnoIeGx#_hdu28yK_q-Eg=C^`HgyaE0cz89{gBKNf^wFjW@(BdjWaN!H#8^{}Kl`vmiGhOX=dgx)Z(+-VNUkXP_MTMas+y zrFJnNg1g~ekhH7k;7a%tD5-h{dhje1IW}Px!=~UK_;AEPa`sm!q5nISjcbXQgzP&0 zJOvNIL8y^F1aE`eVI7Lcry;hfKfw*Kn*vw|2cdjF1T{PgC3O$M^)UJt1M&E0P!#_L ziiMs05rKX1Cb$(!mEQ;D;FIwE@b^%vd^M#nDY0M={5F&bJppfo7h#2l`=Q*XJK(UC z&u19O^>_w~0l$OTpt^Mufqp0gN8o$lO^`II3FyFkprq<7wBUM#9EA76Kf^O{KYS4- zhu|<_7K5kZfRyhG434spLHabDf*~B>c_MAibYL|G*VVmEQ`bsP2Vg(AS~d zj>jNbRDXb1!4C3p3JyV$-^G>dXye+$`_Zo+86eawTuUhvO?4~r6;H?EUGOV#E6kGE z!FHuS3b92!kV|Uj2avR@=b@bY8|;8AoM-Av~7fqAV0$1B-xOF>|lTy49tG_}(z=uz*m zan(qocdr!$6P{mCBc5Bd{Yr~gXnSsIP~(dom$yZ`-EqsY3mVg^o{PeXvh5h{jfIx& z+HOg#!fG9gcwO|pitg?23CvOlx^oZr=jZ=T3b8~TChpzhexD^^P zhtQ!}TVbkjk2k+hYzYWkK^HxL{`Jxa>}sgjW-6OQd3*5;0??c)lb*r-YplGA(7D}Zw<%npK@_~Dv~ zR)w7-I*mnAAfshF3Ut--L-98(8y(njl#kR<9f*ser^k(N7jdp^g&A?z@}(TS*!8?) z*%jF|W^(#^ObyEzTdnz3JlvosBo?u-__!R&q~g2@m#CIk+EvjV$LZV~k<;~k-Mb>) z*Ix^af3RQ&RmYkfh*RI!FNPj;t%_lxYz4YxkCXgZJ2S3L9F^AX%4IU6o=yXi?83~v z=ldofZqOx9TdGzYgNg0zJ^_H=uGN-Tqrrkk@z_XSdN`b-VThO zduik`3B4`pgCmd4+)$Do--tN$k)4mLO!DP8-bAcDuDGi2joSrE4!K3!4LzVfy(HT?lDsmFnI--07vha?KGb|__Gt6T=67a~)z2*ba`f8Q`|W(? zRr5siyUjVKmP3IKBMEww`^` ztmoUo$rUpTqS5(6ZoOCk&7KhDRBXY8>Iw8Mn!+3P1b5ZvB6*E3U|*=ex~g}V*dVRp nt#*WLw)Fpw@W0;?j%6F|+1pzG?EQ<@kgK0uv957I&o1}}8}RKj -- 2.20.1