Remove obsolete jsonfield dependency.
authorRadek Czajka <rczajka@rczajka.pl>
Mon, 5 Aug 2019 12:42:55 +0000 (14:42 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Mon, 5 Aug 2019 12:42:55 +0000 (14:42 +0200)
41 files changed:
requirements/requirements.txt
src/catalogue/feeds.py
src/catalogue/fixtures/test-books.yaml
src/catalogue/management/commands/checkcovers.py
src/catalogue/management/commands/report_dead_links.py
src/catalogue/migrations/0001_initial.py
src/catalogue/migrations/0008_auto_20151221_1225.py
src/catalogue/migrations/0016_auto_20171031_1232.py
src/catalogue/models/book.py
src/catalogue/models/bookmedia.py
src/catalogue/models/source.py
src/catalogue/templates/catalogue/book_info.html
src/catalogue/templates/catalogue/book_short.html
src/catalogue/templates/catalogue/book_wide.html
src/catalogue/templates/catalogue/snippets/jplayer.html
src/catalogue/views.py
src/contact/admin.py
src/contact/forms.py
src/contact/migrations/0001_initial.py
src/contact/models.py
src/dictionary/templates/dictionary/note_list.html
src/isbn/forms.py
src/isbn/management/commands/export_onix.py
src/isbn/migrations/0001_initial.py
src/isbn/models.py
src/lesmianator/migrations/0001_initial.py
src/lesmianator/migrations/0002_auto_20151221_1225.py
src/lesmianator/models.py
src/lesmianator/views.py
src/picture/migrations/0001_initial.py
src/picture/migrations/0005_auto_20141022_1001.py
src/picture/migrations/0006_auto_20151221_1225.py
src/picture/models.py
src/picture/tasks.py
src/picture/templates/picture/picture_wide.html
src/picture/templatetags/picture_tags.py
src/picture/views.py
src/reporting/views.py
src/sponsors/admin.py
src/sponsors/migrations/0001_initial.py
src/sponsors/models.py

index a5f0467..b8e46be 100644 (file)
@@ -7,7 +7,6 @@ django-pipeline==1.6.13
 jsmin
 fnp-django-pagination==2.2.3
 django-maintenancemode==0.11.3
-jsonfield==2.0.2
 django-picklefield==1.1.0
 django-modeltranslation==0.13
 django-allauth==0.39
index f0305e7..82b0a75 100644 (file)
@@ -55,10 +55,11 @@ class AudiobookFeed(Feed):
 
     def item_description(self, item):
         lines = []
-        artist = item.extra_info.get('artist_name', None)
+        extra_info = item.get_extra_info_json()
+        artist = extra_info.get('artist_name', None)
         if artist is not None:
             lines.append(u'Czyta: %s' % artist)
-        director = item.extra_info.get('director_name', None)
+        director = extra_info.get('director_name', None)
         if director is not None:
             lines.append(u'Reżyseria: %s' % director)
         return u'<br/>\n'.join(lines)
index fcb3617..6c24400 100644 (file)
     name: Parent Audiobook
     file: mp3/parent.mp3
     uploaded_at: "1970-01-03 0:0Z"
-    extra_info: {"director_name": "Director", "artist_name": "Artist"}
+    extra_info: '{"director_name": "Director", "artist_name": "Artist"}'
 - model: catalogue.bookmedia
   fields:
     book: 1
index 66a69c7..db9fc4c 100644 (file)
@@ -10,7 +10,7 @@ from catalogue import app_settings
 def ancestor_has_cover(book):
     while book.parent:
         book = book.parent
-        if book.extra_info.get('cover_url'):
+        if book.get_extra_info_json().get('cover_url'):
             return True
     return False
 
@@ -54,7 +54,7 @@ class Command(BaseCommand):
 
         with transaction.atomic():
             for book in Book.objects.all().order_by('slug').iterator():
-                extra_info = book.extra_info
+                extra_info = book.get_extra_info_json()
                 if not extra_info.get('cover_url'):
                     if ancestor_has_cover(book):
                         with_ancestral_cover.append(book)
@@ -100,9 +100,10 @@ Bad licenses used: %s (%d covers without license).
                 for book in no_license:
                     print()
                     print(full_url(book))
-                    print(book.extra_info.get('cover_by'))
-                    print(book.extra_info.get('cover_source'))
-                    print(book.extra_info.get('cover_url'))
+                    extra_info = book.get_extra_info_json()
+                    print(extra_info.get('cover_by'))
+                    print(extra_info.get('cover_source'))
+                    print(extra_info.get('cover_url'))
 
             if not_redakcja:
                 print()
@@ -111,9 +112,10 @@ Bad licenses used: %s (%d covers without license).
                 for book in not_redakcja:
                     print()
                     print(full_url(book))
-                    print(book.extra_info.get('cover_by'))
-                    print(book.extra_info.get('cover_source'))
-                    print(book.extra_info.get('cover_url'))
+                    extra_info = book.get_extra_info_json()
+                    print(extra_info.get('cover_by'))
+                    print(extra_info.get('cover_source'))
+                    print(extra_info.get('cover_url'))
 
             if without_cover:
                 print()
index ddcc216..5f193c4 100644 (file)
@@ -21,7 +21,7 @@ class Command(BaseCommand):
                 Book,
                 [
                     ('wiki_link', lambda b: b.wiki_link),
-                    ('źródło', lambda b: b.extra_info.get('source_url')),
+                    ('źródło', lambda b: b.get_extra_info_json().get('source_url')),
                 ],
                 'admin:catalogue_book_change'
             ),
@@ -29,7 +29,7 @@ class Command(BaseCommand):
                 Picture,
                 [
                     ('wiki_link', lambda p: p.wiki_link),
-                    ('źródło', lambda p: p.extra_info.get('source_url')),
+                    ('źródło', lambda p: p.get_extra_info_json().get('source_url')),
                 ],
                 'admin:pictures_picture_change'
             )
@@ -51,8 +51,8 @@ class Command(BaseCommand):
                                 print(
                                     ('Administracja: https://%s%s' % (domain, reverse(admin_name, args=[obj.pk])))
                                     .encode('utf-8'))
-                                if obj.extra_info.get('about'):
-                                    print(('Redakcja: %s' % (obj.extra_info.get('about'),)).encode('utf-8'))
+                                if obj.get_extra_info_json().get('about'):
+                                    print(('Redakcja: %s' % (obj.get_extra_info_json().get('about'),)).encode('utf-8'))
                             print(('    %s (%s): %s' % (name, getattr(e, 'code', 'błąd'), url)).encode('utf-8'))
                 if not clean:
                     print()
index 987d811..3acfad9 100644 (file)
@@ -1,7 +1,6 @@
 from django.db import models, migrations
 import django.db.models.deletion
 import fnpdjango.storage
-import jsonfield.fields
 import catalogue.fields
 import catalogue.models.bookmedia
 from django.conf import settings
@@ -30,12 +29,12 @@ class Migration(migrations.Migration):
                 ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='creation date', db_index=True)),
                 ('changed_at', models.DateTimeField(auto_now=True, verbose_name='creation date', db_index=True)),
                 ('parent_number', models.IntegerField(default=0, verbose_name='Parent number')),
-                ('extra_info', jsonfield.fields.JSONField(default={}, verbose_name='Additional information')),
+                ('extra_info', models.TextField(default='{}', verbose_name='Additional information')),
                 ('gazeta_link', models.CharField(max_length=240, blank=True)),
                 ('wiki_link', models.CharField(max_length=240, blank=True)),
                 ('cover', catalogue.fields.EbookField('cover', upload_to=catalogue.models.book._cover_upload_to, storage=fnpdjango.storage.BofhFileSystemStorage(), max_length=255, blank=True, null=True, verbose_name='cover')),
                 ('cover_thumb', catalogue.fields.EbookField('cover_thumb', max_length=255, upload_to=catalogue.models.book._cover_thumb_upload_to, null=True, verbose_name='cover thumbnail', blank=True)),
-                ('_related_info', jsonfield.fields.JSONField(null=True, editable=False, blank=True)),
+                ('_related_info', models.TextField(null=True, editable=False, blank=True)),
                 ('txt_file', catalogue.fields.EbookField('txt', default='', storage=fnpdjango.storage.BofhFileSystemStorage(), upload_to=catalogue.models.book._txt_upload_to, max_length=255, blank=True, verbose_name='TXT file')),
                 ('fb2_file', catalogue.fields.EbookField('fb2', default='', storage=fnpdjango.storage.BofhFileSystemStorage(), upload_to=catalogue.models.book._fb2_upload_to, max_length=255, blank=True, verbose_name='FB2 file')),
                 ('pdf_file', catalogue.fields.EbookField('pdf', default='', storage=fnpdjango.storage.BofhFileSystemStorage(), upload_to=catalogue.models.book._pdf_upload_to, max_length=255, blank=True, verbose_name='PDF file')),
@@ -60,7 +59,7 @@ class Migration(migrations.Migration):
                 ('name', models.CharField(max_length=512, verbose_name='name')),
                 ('file', catalogue.fields.OverwritingFileField(upload_to=catalogue.models.bookmedia._file_upload_to, max_length=600, verbose_name='XML file')),
                 ('uploaded_at', models.DateTimeField(auto_now_add=True, verbose_name='creation date', db_index=True)),
-                ('extra_info', jsonfield.fields.JSONField(default={}, verbose_name='Additional information', editable=False)),
+                ('extra_info', models.TextField(default='{}', verbose_name='Additional information', editable=False)),
                 ('source_sha1', models.CharField(max_length=40, null=True, editable=False, blank=True)),
                 ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='media', to='catalogue.Book')),
             ],
index c7f0aa7..d8e9af1 100644 (file)
@@ -2,7 +2,6 @@
 from __future__ import unicode_literals
 
 from django.db import migrations, models
-import jsonfield.fields
 import catalogue.fields
 import catalogue.models.bookmedia
 
@@ -40,7 +39,7 @@ class Migration(migrations.Migration):
         migrations.AlterField(
             model_name='book',
             name='extra_info',
-            field=jsonfield.fields.JSONField(default={}, verbose_name='extra information'),
+            field=models.TextField(default='{}', verbose_name='extra information'),
         ),
         migrations.AlterField(
             model_name='book',
@@ -65,7 +64,7 @@ class Migration(migrations.Migration):
         migrations.AlterField(
             model_name='bookmedia',
             name='extra_info',
-            field=jsonfield.fields.JSONField(default={}, verbose_name='extra information', editable=False),
+            field=models.TextField(default='{}', verbose_name='extra information', editable=False),
         ),
         migrations.AlterField(
             model_name='bookmedia',
index 5892a29..f606c13 100644 (file)
@@ -1,6 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
 
 
@@ -12,7 +9,7 @@ def refresh_books(apps, schema_editor):
         book.cached_author = ', '.join(
             TagRelation.objects.filter(content_type__model='book', object_id=book.id, tag__category='author')
             .values_list('tag__name', flat=True))
-        book.has_audience = 'audience' in book.extra_info
+        book.has_audience = 'audience' in book.get_extra_info_json()
         book.save()
 
 
index af4c941..033febf 100644 (file)
@@ -2,6 +2,7 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from collections import OrderedDict
+import json
 from datetime import date, timedelta
 from random import randint
 import os.path
@@ -14,7 +15,6 @@ from django.contrib.contenttypes.fields import GenericRelation
 from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _, get_language
 from django.utils.deconstruct import deconstructible
-import jsonfield
 from fnpdjango.storage import BofhFileSystemStorage
 
 from librarian.cover import WLCover
@@ -65,7 +65,7 @@ class Book(models.Model):
     created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
     changed_at = models.DateTimeField(_('change date'), auto_now=True, db_index=True)
     parent_number = models.IntegerField(_('parent number'), default=0)
-    extra_info = jsonfield.JSONField(_('extra information'), default={})
+    extra_info = models.TextField(_('extra information'), default='{}')
     gazeta_link = models.CharField(blank=True, max_length=240)
     wiki_link = models.CharField(blank=True, max_length=240)
     print_on_demand = models.BooleanField(_('print on demand'), default=False)
@@ -128,6 +128,9 @@ class Book(models.Model):
     def __str__(self):
         return self.title
 
+    def get_extra_info_json(self):
+        return json.loads(self.extra_info or '{}')
+
     def get_initial(self):
         try:
             return re.search(r'\w', self.title, re.U).group(0)
@@ -169,7 +172,7 @@ class Book(models.Model):
         return self.tag_unicode('genre')
 
     def translator(self):
-        translators = self.extra_info.get('translators')
+        translators = self.get_extra_info_json().get('translators')
         if not translators:
             return None
         if len(translators) > 3:
@@ -180,7 +183,7 @@ class Book(models.Model):
         return ', '.join(u'\xa0'.join(reversed(translator.split(', ', 1))) for translator in translators) + others
 
     def cover_source(self):
-        return self.extra_info.get('cover_source', self.parent.cover_source() if self.parent else '')
+        return self.get_extra_info_json().get('cover_source', self.parent.cover_source() if self.parent else '')
 
     def save(self, force_insert=False, force_update=False, **kwargs):
         from sortify import sortify
@@ -195,7 +198,7 @@ class Book(models.Model):
         self.sort_key_author = author
 
         self.cached_author = self.tag_unicode('author')
-        self.has_audience = 'audience' in self.extra_info
+        self.has_audience = 'audience' in self.get_extra_info_json()
 
         if self.preview and not self.preview_key:
             self.preview_key = get_random_hash(self.slug)[:32]
@@ -340,7 +343,7 @@ class Book(models.Model):
         projects = set()
         for mp3 in self.media.filter(type='mp3').iterator():
             # ogg files are always from the same project
-            meta = mp3.extra_info
+            meta = mp3.get_extra_info_json()
             project = meta.get('project')
             if not project:
                 # temporary fallback
@@ -377,7 +380,7 @@ class Book(models.Model):
     def zip_format(format_):
         def pretty_file_name(book):
             return "%s/%s.%s" % (
-                book.extra_info['author'],
+                book.get_extra_info_json()['author'],
                 book.slug,
                 format_)
 
@@ -490,7 +493,7 @@ class Book(models.Model):
             book.common_slug = book_info.variant_of.slug
         else:
             book.common_slug = book.slug
-        book.extra_info = book_info.to_dict()
+        book.extra_info = json.dumps(book_info.to_dict())
         book.load_abstract()
         book.save()
 
@@ -603,7 +606,7 @@ class Book(models.Model):
         need = False
         info = {}
         for field in ('cover_url', 'cover_by', 'cover_source'):
-            val = self.extra_info.get(field)
+            val = self.get_extra_info_json().get(field)
             if val:
                 info[field] = val
             else:
@@ -657,7 +660,7 @@ class Book(models.Model):
         return ', '.join(names)
 
     def publisher(self):
-        publisher = self.extra_info['publisher']
+        publisher = self.get_extra_info_json()['publisher']
         if isinstance(publisher, str):
             return publisher
         elif isinstance(publisher, list):
@@ -724,12 +727,12 @@ class Book(models.Model):
     }
 
     def audiences_pl(self):
-        audiences = self.extra_info.get('audiences', [])
+        audiences = self.get_extra_info_json().get('audiences', [])
         audiences = sorted(set([self._audiences_pl.get(a, (99, a)) for a in audiences]))
         return [a[1] for a in audiences]
 
     def stage_note(self):
-        stage = self.extra_info.get('stage')
+        stage = self.get_extra_info_json().get('stage')
         if stage and stage < '0.4':
             return (_('This work needs modernisation'),
                     reverse('infopage', args=['wymagajace-uwspolczesnienia']))
@@ -786,7 +789,7 @@ class Book(models.Model):
         return self.SORT_KEY_SEP.join((self.sort_key_author, self.sort_key, str(self.id)))
 
     def cover_color(self):
-        return WLCover.epoch_colors.get(self.extra_info.get('epoch'), '#000000')
+        return WLCover.epoch_colors.get(self.get_extra_info_json().get('epoch'), '#000000')
 
     @cached_render('catalogue/book_mini_box.html')
     def mini_box(self):
index 9f71364..d1f2bf0 100644 (file)
@@ -6,7 +6,6 @@ import json
 from collections import namedtuple
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
-import jsonfield
 from slugify import slugify
 from mutagen import MutagenError
 
@@ -33,7 +32,7 @@ class BookMedia(models.Model):
     index = models.IntegerField(_('index'), default=0)
     file = models.FileField(_('file'), max_length=600, upload_to=_file_upload_to, storage=OverwriteStorage())
     uploaded_at = models.DateTimeField(_('creation date'), auto_now_add=True, editable=False, db_index=True)
-    extra_info = jsonfield.JSONField(_('extra information'), default={}, editable=False)
+    extra_info = models.TextField(_('extra information'), default='{}', editable=False)
     book = models.ForeignKey('Book', models.CASCADE, related_name='media')
     source_sha1 = models.CharField(null=True, blank=True, max_length=40, editable=False)
 
@@ -46,6 +45,9 @@ class BookMedia(models.Model):
         verbose_name_plural = _('book media')
         app_label = 'catalogue'
 
+    def get_extra_info_json(self):
+        return json.loads(self.extra_info or '{}')
+
     def save(self, parts_count=None, *args, **kwargs):
         from catalogue.utils import ExistingFile, remove_zip
 
@@ -75,12 +77,9 @@ class BookMedia(models.Model):
             remove_zip("%s_%s" % (old.book.slug, old.type))
         remove_zip("%s_%s" % (self.book.slug, self.type))
 
-        extra_info = self.extra_info
-        if isinstance(extra_info, str):
-            # Walkaround for weird jsonfield 'no-decode' optimization.
-            extra_info = json.loads(extra_info)
+        extra_info = self.get_extra_info_json()
         extra_info.update(self.read_meta())
-        self.extra_info = extra_info
+        self.extra_info = json.dumps(extra_info)
         self.source_sha1 = self.read_source_sha1(self.file.path, self.type)
         return super(BookMedia, self).save(*args, **kwargs)
 
@@ -148,11 +147,11 @@ class BookMedia(models.Model):
 
     @property
     def director(self):
-        return self.extra_info.get('director_name', None)
+        return self.get_extra_info_json().get('director_name', None)
 
     @property
     def artist(self):
-        return self.extra_info.get('artist_name', None)
+        return self.get_extra_info_json().get('artist_name', None)
 
     def file_url(self):
         return self.file.url
index c678cad..08cac0c 100644 (file)
@@ -37,7 +37,7 @@ class Source(models.Model):
         # and invalidate their cached includes.
         if old_name != self.name or old_netloc != self.netloc:
             for book in Book.objects.all():
-                source = book.extra_info.get('source_url', '')
+                source = book.get_extra_info_json().get('source_url', '')
                 if self.netloc in source or (old_netloc != self.netloc and old_netloc in source):
                     book.clear_cache()
         return ret
index 014a983..85d1fbc 100755 (executable)
@@ -1,10 +1,11 @@
 {% load i18n %}
 {% load catalogue_tags %}
 
+{% with extra_info=book.get_extra_info_json %}
 <p>
-  {% if book.extra_info.license %}
+  {% if extra_info.license %}
     {% trans "This work is licensed under:" %}
-    <a href="{{ book.extra_info.license }}">{{ book.extra_info.license_description }}</a>
+    <a href="{{ extra_info.license }}">{{ extra_info.license_description }}</a>
   {% else %}
     {% blocktrans %}This work isn't covered by copyright and is part of the
     public domain, which means it can be freely used, published and
   {% endif %}
 </p>
 
-{% if book.extra_info.source_name %}
-  <p>{% trans "Resource prepared based on:" %} {{ book.extra_info.source_name }}</p>
+{% if extra_info.source_name %}
+  <p>{% trans "Resource prepared based on:" %} {{ extra_info.source_name }}</p>
 {% endif %}
 
-{% if book.extra_info.description %}
-  <p>{{ book.extra_info.description }}</p>
+{% if extra_info.description %}
+  <p>{{ extra_info.description }}</p>
 {% endif %}
 
-{% if book.extra_info.editor or book.extra_info.technical_editor %}
+{% if extra_info.editor or extra_info.technical_editor %}
   <p>
     {% if is_picture %}
       {% trans "Edited by:" %}
     {% else %}
       {% trans "Edited and annotated by:" %}
     {% endif %}
-    {% all_editors book.extra_info %}.
+    {% all_editors extra_info %}.
   </p>
 {% endif %}
 
-{% if book.extra_info.publisher %}
+{% if extra_info.publisher %}
   <p>
     {% trans "Publisher:" %}
     {{ book.publisher }}
   </p>
 {% endif %}
 
-{% if book.extra_info.funders %}
+{% if extra_info.funders %}
   <p>
     {% trans "Publication funded by:" %}
-    {% for funder in book.extra_info.funders %}{{ funder }}{% if not forloop.last %}, {% else %}.{% endif %}{% endfor %}
+    {% for funder in extra_info.funders %}{{ funder }}{% if not forloop.last %}, {% else %}.{% endif %}{% endfor %}
   </p>
 {% endif %}
 
-{% if book.extra_info.cover_by %}
+{% if extra_info.cover_by %}
   <p>
     {% trans "Cover image by:" %}
-    <a href="{{ book.extra_info.cover_source }}">{{ book.extra_info.cover_by }}</a>.
+    <a href="{{ extra_info.cover_source }}">{{ extra_info.cover_by }}</a>.
   </p>
 {% endif %}
 
-{% if book.extra_info.isbn_html %}
+{% if extra_info.isbn_html %}
   <p>
-    {{ book.extra_info.isbn_html }}
+    {{ extra_info.isbn_html }}
   </p>
 {% endif %}
+
+{% endwith %}
index ccb1004..59debf4 100644 (file)
             {% endfor %}
           </span></span>
 
-          {% if book.extra_info.location %}
-            <span class="category">
-            <span class="mono"> {% trans "Region" %}:</span>&nbsp;<span class="book-box-tag">
-                {{ book.extra_info.location }}
-            </span></span>
-          {% endif %}
+         {% with extra_info=book.get_extra_info_json %}
+            {% if extra_info.location %}
+              <span class="category">
+              <span class="mono"> {% trans "Region" %}:</span>&nbsp;<span class="book-box-tag">
+                  {{ extra_info.location }}
+              </span></span>
+            {% endif %}
+         {% endwith %}
 
           {% if book.is_foreign %}
             <span class="category">
index ad0384c..7721c23 100644 (file)
@@ -8,9 +8,11 @@
 
 
 {% block cover-area-extra %}
-  {% if book.extra_info.license %}
-    {% license_icon book.extra_info.license %}
-  {% endif %}
+  {% with license=book.get_extra_info_json.license %}
+    {% if license %}
+      {% license_icon license %}
+    {% endif %}
+  {% endwith %}
 {% endblock %}
 
 
index 46eef40..8847f99 100644 (file)
               <li data-mp3='{{ i.mp3.file.url }}' data-ogg='{{ i.ogg.file.url }}'>
                 <div class='play'>{{ i.mp3.name }}
                   <div class='extra-info'>
-                    {% trans "Artist:" %}&nbsp;<span class='artist'>{{ i.mp3.extra_info.artist_name }}</span>,
-                    {% trans "director:" %}&nbsp;<span class='director'>{{ i.mp3.extra_info.director_name }}</span>
+                  {% with extra_info=i.mp3.get_extra_info_json %}
+                    {% trans "Artist:" %}&nbsp;<span class='artist'>{{ extra_info.artist_name }}</span>,
+                    {% trans "director:" %}&nbsp;<span class='director'>{{ extra_info.director_name }}</span>
                     <p>
-                      {% with fb=i.mp3.extra_info.funded_by %}
+                      {% with fb=extra_info.funded_by %}
                         {% if fb %}Dofinansowano ze środków: {{ fb }}.{% endif %}
                       {% endwith %}
                     </p>
+                 {% endwith %}
                   </div>
                 </div>
               </li>
index ccd51ff..b4ac30f 100644 (file)
@@ -457,4 +457,4 @@ def ridero_cover(request, slug):
 
 def get_isbn(request, book_format, slug):
     book = Book.objects.get(slug=slug)
-    return HttpResponse(book.extra_info.get('isbn_%s' % book_format))
+    return HttpResponse(book.get_extra_info_json().get('isbn_%s' % book_format))
index a059433..32d12ec 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 import csv
 import json
 
@@ -36,7 +35,7 @@ class ContactAdmin(admin.ModelAdmin):
         except BaseException:
             return ''
         else:
-            return Contact.pretty_print(obj.body.get(field_name, ''), for_html=True)
+            return Contact.pretty_print(obj.get_body_json().get(field_name, ''), for_html=True)
 
     def __getattr__(self, name):
         if name.startswith('admin_list_'):
@@ -48,13 +47,14 @@ class ContactAdmin(admin.ModelAdmin):
         if object_id:
             try:
                 instance = Contact.objects.get(pk=object_id)
-                assert isinstance(instance.body, dict)
+                body = instance.get_body_json()
+                assert isinstance(body, dict)
             except (Contact.DoesNotExist, AssertionError):
                 pass
             else:
                 # Create readonly fields from the body JSON.
                 attachments = list(instance.attachment_set.all())
-                body_keys = instance.body.keys() + [a.tag for a in attachments]
+                body_keys = body.keys() + [a.tag for a in attachments]
 
                 # Find the original form.
                 try:
@@ -82,7 +82,7 @@ class ContactAdmin(admin.ModelAdmin):
                     f.short_description = orig_fields[key].label if key in orig_fields else _(key)
                     setattr(self, "body__%s" % key, f)
 
-                for k, v in instance.body.items():
+                for k, v in body.items():
                     attach_getter(k, Contact.pretty_print(v, for_html=True))
 
                 download_link = "<a href='%(url)s'>%(url)s</a>"
@@ -133,7 +133,7 @@ def extract_view(request, form_tag, extract_type_slug):
         if extract_type_slug == 'contacts':
             keys = ['contact']
         elif extract_type_slug == 'all':
-            keys = contact.body.keys() + ['contact']
+            keys = contact.get_body_json().keys() + ['contact']
             keys = [key for key in orig_keys if key in keys] + [key for key in keys if key not in orig_keys]
         else:
             keys = form.get_extract_fields(contact, extract_type_slug)
@@ -149,7 +149,7 @@ def extract_view(request, form_tag, extract_type_slug):
             if extract_type_slug == 'contacts':
                 records = [dict(contact=contact.contact)]
             elif extract_type_slug == 'all':
-                records = [dict(contact=contact.contact, **contact.body)]
+                records = [dict(contact=contact.contact, **contact.get_body_json())]
             else:
                 records = form.get_extract_records(keys, contact, extract_type_slug)
 
index ff1687a..ee44e4b 100644 (file)
@@ -1,3 +1,4 @@
+import json
 from django.contrib.sites.models import Site
 from django.core.exceptions import ValidationError
 from django.core.files.uploadedfile import UploadedFile
@@ -56,7 +57,7 @@ class ContactForm(forms.Form):
                     body.setdefault(f.form_tag, []).append(sub_body)
 
         contact = Contact.objects.create(
-            body=body,
+            body=json.dumps(body),
             ip=request.META['REMOTE_ADDR'],
             contact=self.cleaned_data['contact'],
             form_tag=self.form_tag)
index 216c4c5..682a097 100644 (file)
@@ -1,6 +1,5 @@
 from django.db import migrations, models
 import django.db.models.deletion
-import jsonfield.fields
 
 
 class Migration(migrations.Migration):
@@ -25,7 +24,7 @@ class Migration(migrations.Migration):
                 ('ip', models.GenericIPAddressField(verbose_name='IP address')),
                 ('contact', models.EmailField(max_length=128, verbose_name='contact')),
                 ('form_tag', models.CharField(max_length=32, verbose_name='form', db_index=True)),
-                ('body', jsonfield.fields.JSONField(verbose_name='body')),
+                ('body', models.TextField(verbose_name='body')),
             ],
             options={
                 'ordering': ('-created_at',),
index aae3f2d..b173b4d 100644 (file)
@@ -1,10 +1,10 @@
+import json
 import yaml
 from hashlib import sha1
 from django.db import models
 from django.urls import reverse
 from django.utils.encoding import smart_text
 from django.utils.translation import ugettext_lazy as _
-from jsonfield import JSONField
 from . import app_settings
 
 
@@ -13,7 +13,7 @@ class Contact(models.Model):
     ip = models.GenericIPAddressField(_('IP address'))
     contact = models.EmailField(_('contact'), max_length=128)
     form_tag = models.CharField(_('form'), max_length=32, db_index=True)
-    body = JSONField(_('body'))
+    body = models.TextField(_('body'))
 
     @staticmethod
     def pretty_print(value, for_html=False):
@@ -32,7 +32,7 @@ class Contact(models.Model):
         return str(self.created_at)
 
     def digest(self):
-        serialized_body = ';'.join(sorted('%s:%s' % item for item in self.body.items()))
+        serialized_body = ';'.join(sorted('%s:%s' % item for item in self.get_body_json().items()))
         data = '%s%s%s%s%s' % (self.id, self.contact, serialized_body, self.ip, self.form_tag)
         return sha1(data).hexdigest()
 
@@ -45,7 +45,11 @@ class Contact(models.Model):
         return list(orig_fields.keys())
 
     def items(self):
-        return [(key, self.body[key]) for key in self.keys() if key in self.body]
+        body = self.get_body_json()
+        return [(key, body[key]) for key in self.keys() if key in body]
+
+    def get_body_json(self):
+        return json.loads(self.body or '{}')
 
 
 class Attachment(models.Model):
index 3083e04..3a19b4e 100755 (executable)
           {% for note_source in obj.notesource_set.all %}
             <div class='dictionary-note-source'>
               <a href='{% url "book_text" note_source.book.slug %}#{{ note_source.anchor }}'>{{ note_source.book.pretty_title }}</a>
-              (<a href='{{ note_source.book.extra_info.about }}'>źródło na Platformie Redakcyjnej</a>)
+              (<a href='{{ note_source.book.get_extra_info_json.about }}'>źródło na Platformie Redakcyjnej</a>)
             </div>
           {% endfor %}
         </div>
index 47a72ba..5f2940c 100644 (file)
@@ -1,5 +1,5 @@
-# -*- coding: utf-8 -*-
 from datetime import date
+import json
 from urllib.request import urlopen
 
 from django import forms
@@ -48,7 +48,7 @@ class WLConfirmForm(WLISBNForm):
         for file_format in data['formats']:
             data['product_form'] = PRODUCT_FORMS[file_format]
             data['product_form_detail'] = PRODUCT_FORM_DETAILS[file_format]
-            data['contributors'] = self.contributors(data)
+            data['contributors'] = json.dumps(self.contributors(data))
             ONIXRecord.new_record(purpose=ISBNPool.PURPOSE_WL, data=data)
         return data
 
@@ -93,7 +93,7 @@ class FNPISBNForm(forms.Form):
             'title': self.cleaned_data['title'],
             'language': self.cleaned_data['language'],
             'publishing_date': self.cleaned_data['publishing_date'],
-            'contributors': [self.prepare_author(a) for a in self.cleaned_data['authors'].split(',')],
+            'contributors': json.dumps([self.prepare_author(a) for a in self.cleaned_data['authors'].split(',')]),
             'edition_type': 'NED',
             'imprint': 'Fundacja Nowoczesna Polska',
             'dc_slug': self.slug(),
index a78e882..860bc79 100644 (file)
@@ -1,3 +1,4 @@
+import json
 from django.core.management.base import BaseCommand
 from django.utils import timezone
 
@@ -123,7 +124,7 @@ class Command(BaseCommand):
         else:
             part_number = ''
         contributors = ''
-        for no, contributor in enumerate(record.contributors, start=1):
+        for no, contributor in enumerate(json.loads(record.contributors), start=1):
             contributors += self.render_contributor(no, contributor)
         return PRODUCT % {
             'datestamp': record.datestamp.strftime('%Y%m%d'),
index b3c9aec..78a45b9 100644 (file)
@@ -1,6 +1,5 @@
 from django.db import migrations, models
 import django.db.models.deletion
-import jsonfield.fields
 
 
 class Migration(migrations.Migration):
@@ -30,7 +29,7 @@ class Migration(migrations.Migration):
                 ('product_form_detail', models.CharField(max_length=8, blank=True)),
                 ('title', models.CharField(max_length=256)),
                 ('part_number', models.CharField(max_length=64, blank=True)),
-                ('contributors', jsonfield.fields.JSONField()),
+                ('contributors', models.TextField()),
                 ('edition_type', models.CharField(max_length=4)),
                 ('edition_number', models.IntegerField(default=1)),
                 ('language', models.CharField(max_length=4)),
index d65ca60..b5e5de3 100644 (file)
@@ -3,7 +3,6 @@
 #
 from django.db import models
 from django.db.models import F
-from jsonfield import JSONField
 
 
 class ISBNPool(models.Model):
@@ -59,7 +58,7 @@ class ONIXRecord(models.Model):
     product_form_detail = models.CharField(max_length=8, blank=True)
     title = models.CharField(max_length=256)
     part_number = models.CharField(max_length=64, blank=True)
-    contributors = JSONField()  # roles, names, optional: ISNI, date of birth/death
+    contributors = models.TextField()  # roles, names, optional: ISNI, date of birth/death
     edition_type = models.CharField(max_length=4)
     edition_number = models.IntegerField(default=1)
     language = models.CharField(max_length=4)
index f8741e4..3199bf0 100644 (file)
@@ -1,6 +1,5 @@
 from django.db import models, migrations
 import django.db.models.deletion
-import jsonfield.fields
 from django.conf import settings
 
 
@@ -30,7 +29,7 @@ class Migration(migrations.Migration):
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('slug', models.SlugField(max_length=120, verbose_name='Slug')),
                 ('text', models.TextField(verbose_name='text')),
-                ('created_from', jsonfield.fields.JSONField(null=True, verbose_name='Additional information', blank=True)),
+                ('created_from', models.TextField(null=True, verbose_name='Additional information', blank=True)),
                 ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
                 ('seen_at', models.DateTimeField(auto_now_add=True, verbose_name='last view date')),
                 ('view_count', models.IntegerField(default=1, verbose_name='view count')),
index 29a33a8..94d6537 100644 (file)
@@ -1,8 +1,4 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
-import jsonfield.fields
 
 
 class Migration(migrations.Migration):
@@ -15,7 +11,7 @@ class Migration(migrations.Migration):
         migrations.AlterField(
             model_name='poem',
             name='created_from',
-            field=jsonfield.fields.JSONField(null=True, verbose_name='extra information', blank=True),
+            field=models.TextField(null=True, verbose_name='extra information', blank=True),
         ),
         migrations.AlterField(
             model_name='poem',
index f12f1d7..00440a1 100644 (file)
@@ -17,7 +17,6 @@ from django.contrib.contenttypes.fields import GenericForeignKey
 from django.conf import settings
 from django.urls import reverse
 
-from jsonfield import JSONField
 from catalogue.models import Book, Tag
 
 
@@ -25,7 +24,7 @@ class Poem(models.Model):
     slug = models.SlugField(_('slug'), max_length=120, db_index=True)
     text = models.TextField(_('text'))
     created_by = models.ForeignKey(User, models.SET_NULL, null=True)
-    created_from = JSONField(_('extra information'), null=True, blank=True)
+    created_from = models.TextField(_('extra information'), null=True, blank=True)
     created_at = models.DateTimeField(_('creation date'), auto_now_add=True, editable=False)
     seen_at = models.DateTimeField(_('last view date'), auto_now_add=True, editable=False)
     view_count = models.IntegerField(_('view count'), default=1)
index f814bbc..3e8cfa5 100644 (file)
@@ -1,7 +1,7 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+import json
 from django.shortcuts import render, get_object_or_404
 from django.views.decorators import cache
 
@@ -38,7 +38,7 @@ def poem_from_book(request, slug):
     user = request.user if request.user.is_authenticated() else None
     text = Poem.write(Continuations.get(book))
     p = Poem(slug=get_random_hash(text), text=text, created_by=user)
-    p.created_from = [book.id]
+    p.created_from = json.dumps([book.id])
     p.save()
 
     return render(
@@ -54,7 +54,7 @@ def poem_from_set(request, shelf):
     text = Poem.write(Continuations.get(tag))
     p = Poem(slug=get_random_hash(text), text=text, created_by=user)
     books = Book.tagged.with_any((tag,))
-    p.created_from = [b.id for b in books]
+    p.created_from = json.dumps([b.id for b in books])
     p.save()
 
     book = books[0] if len(books) == 1 else None
@@ -68,8 +68,9 @@ def poem_from_set(request, shelf):
 def get_poem(request, poem):
     p = get_object_or_404(Poem, slug=poem)
     p.visit()
-    if p.created_from:
-        books = Book.objects.filter(id__in=p.created_from)
+    created_from = json.loads(p.created_from or '[]')
+    if created_from:
+        books = Book.objects.filter(id__in=created_from)
         book = books[0] if len(books) == 1 else None
     else:
         books = book = None
index 11321d4..22611bb 100644 (file)
@@ -3,7 +3,6 @@ from django.conf import settings
 from django.db import models, migrations
 import django.db.models.deletion
 import sorl.thumbnail.fields
-import jsonfield.fields
 import django.core.files.storage
 
 
@@ -26,11 +25,11 @@ class Migration(migrations.Migration):
                 ('xml_file', models.FileField(upload_to='xml', storage=django.core.files.storage.FileSystemStorage(base_url='/media/pictures/', location=join(settings.MEDIA_ROOT, 'pictures')), verbose_name='xml_file')),
                 ('image_file', sorl.thumbnail.fields.ImageField(upload_to='images', storage=django.core.files.storage.FileSystemStorage(base_url='/media/pictures/', location=join(settings.MEDIA_ROOT, 'pictures')), verbose_name='image_file')),
                 ('html_file', models.FileField(upload_to='html', storage=django.core.files.storage.FileSystemStorage(base_url='/media/pictures/', location=join(settings.MEDIA_ROOT, 'pictures')), verbose_name='html_file')),
-                ('areas_json', jsonfield.fields.JSONField(default={}, verbose_name='picture areas JSON', editable=False)),
-                ('extra_info', jsonfield.fields.JSONField(default={}, verbose_name='Additional information')),
+                ('areas_json', models.TextField(default='{}', verbose_name='picture areas JSON', editable=False)),
+                ('extra_info', models.TextField(default={}, verbose_name='Additional information')),
                 ('culturepl_link', models.CharField(max_length=240, blank=True)),
                 ('wiki_link', models.CharField(max_length=240, blank=True)),
-                ('_related_info', jsonfield.fields.JSONField(null=True, editable=False, blank=True)),
+                ('_related_info', models.TextField(null=True, editable=False, blank=True)),
                 ('width', models.IntegerField(null=True)),
                 ('height', models.IntegerField(null=True)),
             ],
@@ -45,7 +44,7 @@ class Migration(migrations.Migration):
             name='PictureArea',
             fields=[
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('area', jsonfield.fields.JSONField(default={}, verbose_name='area', editable=False)),
+                ('area', models.TextField(default='{}', verbose_name='area', editable=False)),
                 ('kind', models.CharField(db_index=True, max_length=10, verbose_name='form', choices=[('thing', 'thing'), ('theme', 'motif')])),
                 ('picture', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='areas', to='picture.Picture')),
             ],
index 7f8d346..9ece9d7 100644 (file)
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
+import json
 from django.core.files.base import ContentFile
 from django.db import models, migrations
 from django.template.loader import render_to_string
@@ -12,14 +10,14 @@ def rebuild_extra_info(apps, schema_editor):
     from librarian import dcparser
     for pic in Picture.objects.all():
         info = dcparser.parse(pic.xml_file.path, PictureInfo)
-        pic.extra_info = info.to_dict()
-        areas_json = pic.areas_json
-        for field in areas_json[u'things'].values():
-            field[u'object'] = field[u'object'].capitalize()
-        pic.areas_json = areas_json
+        pic.extra_info = json.dumps(info.to_dict())
+        areas_json = json.loads(pic.areas_json)
+        for field in areas_json['things'].values():
+            field['object'] = field['object'].capitalize()
+        pic.areas_json = json.dumps(areas_json)
         html_text = render_to_string('picture/picture_info.html', {
-                    'things': pic.areas_json['things'],
-                    'themes': pic.areas_json['themes'],
+                    'things': areas_json['things'],
+                    'themes': areas_json['themes'],
                     })
         pic.html_file.save("%s.html" % pic.slug, ContentFile(html_text))
         pic.save()
index 9f1a82c..50f97b5 100644 (file)
@@ -1,8 +1,4 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import migrations, models
-import jsonfield.fields
 
 
 class Migration(migrations.Migration):
@@ -15,7 +11,7 @@ class Migration(migrations.Migration):
         migrations.AlterField(
             model_name='picture',
             name='extra_info',
-            field=jsonfield.fields.JSONField(default={}, verbose_name='extra information'),
+            field=models.TextField(default='{}', verbose_name='extra information'),
         ),
         migrations.AlterField(
             model_name='picture',
index 75d84e2..1ad09e2 100644 (file)
@@ -15,8 +15,8 @@ from catalogue.utils import split_tags
 from picture import tasks
 from wolnelektury.utils import cached_render, clear_cached_renders
 from io import BytesIO
-import jsonfield
 import itertools
+import json
 import logging
 import re
 
@@ -34,7 +34,7 @@ picture_storage = FileSystemStorage(location=path.join(
 
 class PictureArea(models.Model):
     picture = models.ForeignKey('picture.Picture', models.CASCADE, related_name='areas')
-    area = jsonfield.JSONField(_('area'), default={}, editable=False)
+    area = models.TextField(_('area'), default='{}', editable=False)
     kind = models.CharField(
         _('kind'), max_length=10, blank=False, null=False, db_index=True,
         choices=(('thing', _('thing')), ('theme', _('theme'))))
@@ -51,9 +51,12 @@ class PictureArea(models.Model):
         pa = PictureArea()
         pa.picture = picture
         pa.kind = kind
-        pa.area = coords
+        pa.area = json.dumps(coords)
         return pa
 
+    def get_area_json(self):
+        return json.loads(self.area)
+
     @cached_render('picture/picturearea_short.html')
     def midi_box(self):
         themes = self.tags.filter(category='theme')
@@ -77,14 +80,14 @@ class Picture(models.Model):
     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'')
+        _('sort key by author'), max_length=120, db_index=True, editable=False, default='')
     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)
     image_file = ImageField(_('image file'), upload_to="images", storage=picture_storage)
     html_file = models.FileField(_('html file'), upload_to="html", storage=picture_storage)
-    areas_json = jsonfield.JSONField(_('picture areas JSON'), default={}, editable=False)
-    extra_info = jsonfield.JSONField(_('extra information'), default={})
+    areas_json = models.TextField(_('picture areas JSON'), default='{}', editable=False)
+    extra_info = models.TextField(_('extra information'), default='{}')
     culturepl_link = models.CharField(blank=True, max_length=240)
     wiki_link = models.CharField(blank=True, max_length=240)
 
@@ -196,7 +199,7 @@ class Picture(models.Model):
 
             picture.areas.all().delete()
             picture.title = str(picture_xml.picture_info.title)
-            picture.extra_info = picture_xml.picture_info.to_dict()
+            picture.extra_info = json.dumps(picture_xml.picture_info.to_dict())
 
             picture_tags = set(catalogue.models.Tag.tags_from_info(picture_xml.picture_info))
             for tag in picture_tags:
@@ -268,7 +271,7 @@ class Picture(models.Model):
                     area.tags = _tags.union(picture_tags)
 
             picture.tags = picture_tags
-            picture.areas_json = area_data
+            picture.areas_json = json.dumps(area_data)
 
             if image_file is not None:
                 img = image_file
index e80b0fc..e815f19 100644 (file)
@@ -1,7 +1,7 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+import json
 from traceback import print_exc
 
 from celery.task import task
@@ -13,10 +13,11 @@ from django.template.loader import render_to_string
 def generate_picture_html(picture_id):
     import picture.models
     pic = picture.models.Picture.objects.get(pk=picture_id)
+    areas_json = json.loads(pic.areas_json)
 
     html_text = render_to_string('picture/picture_info.html', {
-                'things': pic.areas_json['things'],
-                'themes': pic.areas_json['themes'],
+                'things': areas_json['things'],
+                'themes': areas_json['themes'],
                 })
     pic.html_file.save("%s.html" % pic.slug, ContentFile(html_text))
 
index 3ca7afe..884b46c 100644 (file)
 
 
 {% block extra_categories %}
-  {% if picture.extra_info.styles %}
+  {% with extra_info=picture.get_extra_info_json %}
+  {% if extra_info.styles %}
     <span class="category">
       <span class="mono"> {% trans "Style" %}:</span>&nbsp;<span class="book-box-tag">
-        {% for tag in picture.extra_info.styles %}
+        {% for tag in extra_info.styles %}
           <a>{{ tag }}</a>
           {% if not forloop.last %}<span>, </span>{% endif %}
         {% endfor %}
     </span>
   {% endif %}
 
-  {% if picture.extra_info.medium %}
+  {% if extra_info.medium %}
     <span class="category">
       <span class="mono"> {% trans "Medium" %}:</span>&nbsp;<span class="book-box-tag">
-        <a>{{ picture.extra_info.medium }}</a>
+        <a>{{ extra_info.medium }}</a>
       </span>
     </span>
   {% endif %}
 
-  {% if picture.extra_info.original_dimensions %}
+  {% if extra_info.original_dimensions %}
     <span class="category">
       <span class="mono"> {% trans "Dimensions" %}:</span>&nbsp;<span class="book-box-tag">
-        <a>{{ picture.extra_info.original_dimensions }}</a>
+        <a>{{ extra_info.original_dimensions }}</a>
       </span>
     </span>
   {% endif %}
 
   <span class="category">
     <span class="mono"> {% trans "Date" %}:</span>&nbsp;<span class="book-box-tag">
-      <a>{{ picture.extra_info.created_at }}</a>
+      <a>{{ extra_info.created_at }}</a>
     </span>
   </span>
+  {% endwith %}
 {% endblock %}
 
 
 
 
 {% block right-column %}
+  {% with extra_info=picture.get_extra_info_json %}
   <div class="right-column">
     <div class="other-tools">
       <h2 class="mono">{% trans "See" %}</h2>
       <ul class="plain">
-        {% if picture.extra_info.source_url %}
-          <li><a href="{{ picture.extra_info.source_url }}">{% trans "Source" %}</a> {% trans "of the picture" %}</li>
+        {% if extra_info.source_url %}
+          <li><a href="{{ extra_info.source_url }}">{% trans "Source" %}</a> {% trans "of the picture" %}</li>
         {% endif %}
         <li><a href="{{ picture.xml_file.url }}">{% trans "Source XML file" %}</a></li>
-        {% if picture.extra_info.about and not hide_about %}
-          <li>{% trans "Picture on" %} <a href="{{ picture.extra_info.about }}">{% trans "Editor's Platform" %}</a></li>
+        {% if extra_info.about and not hide_about %}
+          <li>{% trans "Picture on" %} <a href="{{ extra_info.about }}">{% trans "Editor's Platform" %}</a></li>
         {% endif %}
         {% if picture.wiki_link %}
           <li><a href="{{ picture.wiki_link }}">{% trans "Picture description on Wikipedia" %}</a></li>
       </ul>
     </div>
   </div>
+  {% endwith %}
 {% endblock %}
index 70d58e0..43f2775 100644 (file)
@@ -46,7 +46,7 @@ def area_thumbnail_url(area, geometry):
     # what to do about this?
     _engine = sorl.thumbnail.default.engine
     sorl.thumbnail.default.engine = cropper
-    coords = to_square(area.area)
+    coords = to_square(area.get_area_json())
 
     try:
         th = sorl.thumbnail.default.backend.get_thumbnail(
index 6345f20..43601a1 100644 (file)
@@ -36,7 +36,7 @@ def picture_detail(request, slug):
 def picture_viewer(request, slug):
     picture = get_object_or_404(Picture, slug=slug)
     sponsors = []
-    for sponsor in picture.extra_info.get('sponsors', []):
+    for sponsor in picture.get_extra_info_json().get('sponsors', []):
         have_sponsors = Sponsor.objects.filter(name=sponsor)
         if have_sponsors.exists():
             sponsors.append(have_sponsors[0])
@@ -60,7 +60,7 @@ def picture_page(request, key=None):
             'epoch': picture.tag_unicode('epoch'),
             'kind': picture.tag_unicode('kind'),
             'genre': picture.tag_unicode('genre'),
-            'style': picture.extra_info['style'],
+            'style': picture.get_extra_info_json()['style'],
             'image_url': picture.image_file.url,
             'width': picture.width,
             'height': picture.height,
index cc1bf7e..9eecf0d 100644 (file)
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
@@ -25,9 +24,11 @@ def stats_page(request):
         else:
             mt['deprecated'] = '-'
 
-    licenses = set(
-        (b.extra_info.get('license'), b.extra_info.get('license_description'))
-        for b in Book.objects.all().iterator() if b.extra_info.get('license'))
+    licenses = set()
+    for b in Book.objects.all().iterator():
+        extra_info = b.get_extra_info_json()
+        if extra_info.get('license'):
+            licenses.add((extra_info.get('license'), extra_info.get('license_description')))
 
     return render(request, 'reporting/main.html', {
         'media_types': media_types,
index 274fe90..df11973 100644 (file)
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.contrib import admin
-from jsonfield import JSONField
+from django.db.models import TextField
 from sponsors import models
 from sponsors import widgets
 
@@ -16,7 +15,7 @@ class SponsorAdmin(admin.ModelAdmin):
 
 class SponsorPageAdmin(admin.ModelAdmin):
     formfield_overrides = {
-        JSONField: {'widget': widgets.SponsorPageWidget},
+        TextField: {'widget': widgets.SponsorPageWidget},
     }
     list_display = ('name',)
     search_fields = ('name',)
index becb6fa..5f4b523 100644 (file)
@@ -1,8 +1,4 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
 from django.db import models, migrations
-import jsonfield.fields
 
 
 class Migration(migrations.Migration):
@@ -29,7 +25,7 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('name', models.CharField(max_length=120, verbose_name='name')),
-                ('sponsors', jsonfield.fields.JSONField(default={}, verbose_name='sponsors')),
+                ('sponsors', models.TextField(default='{}', verbose_name='sponsors')),
                 ('_html', models.TextField(editable=False, blank=True)),
                 ('sprite', models.ImageField(upload_to='sponsorzy/sprite', blank=True)),
             ],
index c193f42..6e4ffe1 100644 (file)
@@ -10,7 +10,6 @@ from django.utils.translation import ugettext_lazy as _
 from django.template.loader import render_to_string
 from PIL import Image
 
-from jsonfield import JSONField
 from django.core.files.base import ContentFile
 
 THUMB_WIDTH = 120
@@ -35,14 +34,17 @@ class Sponsor(models.Model):
 
 class SponsorPage(models.Model):
     name = models.CharField(_('name'), max_length=120)
-    sponsors = JSONField(_('sponsors'), default={})
+    sponsors = models.TextField(_('sponsors'), default='{}')
     _html = models.TextField(blank=True, editable=False)
     sprite = models.ImageField(upload_to='sponsorzy/sprite', blank=True)
 
+    def get_sponsors_json(self):
+        return json.loads(self.sponsors or '[]')
+
     def populated_sponsors(self):
         result = []
         offset = 0
-        for column in self.sponsors:
+        for column in self.get_sponsors_json():
             result_group = {'name': column['name'], 'sponsors': []}
             sponsor_objects = Sponsor.objects.in_bulk(column['sponsors'])
             for sponsor_pk in column['sponsors']:
@@ -56,7 +58,7 @@ class SponsorPage(models.Model):
 
     def render_sprite(self):
         sponsor_ids = []
-        for column in self.sponsors:
+        for column in self.get_sponsors_json():
             sponsor_ids.extend(column['sponsors'])
         sponsors = Sponsor.objects.in_bulk(sponsor_ids)
         sprite = Image.new('RGBA', (THUMB_WIDTH, len(sponsors) * THUMB_HEIGHT))
@@ -87,9 +89,6 @@ class SponsorPage(models.Model):
     html = property(fget=html)
 
     def save(self, *args, **kwargs):
-        if isinstance(self.sponsors, str):
-            # Walkaround for weird jsonfield 'no-decode' optimization.
-            self.sponsors = json.loads(self.sponsors)
         self.render_sprite()
         self._html = render_to_string('sponsors/page.html', {
             'sponsors': self.populated_sponsors(),