From: Radek Czajka Date: Tue, 27 Aug 2024 08:33:09 +0000 (+0200) Subject: Pictures: leave raw models for archiving. X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/0fc6db6bec64211a85a2f09dc615ddaa67e5215a Pictures: leave raw models for archiving. --- diff --git a/src/catalogue/models/collection.py b/src/catalogue/models/collection.py index d9ab5da00..f6284ad28 100644 --- a/src/catalogue/models/collection.py +++ b/src/catalogue/models/collection.py @@ -19,8 +19,6 @@ class Collection(models.Model): limit_choices_to={'category': 'author'}, blank=True ) - kind = models.CharField('rodzaj', max_length=10, blank=False, default='book', db_index=True, - choices=(('book', 'książki'), ('picture', 'obrazy'))) listed = models.BooleanField('na liście', default=True, db_index=True) role = models.CharField(max_length=128, blank=True, db_index=True, choices=[ ('', '–'), diff --git a/src/catalogue/templates/catalogue/related_books.html b/src/catalogue/templates/catalogue/related_books.html deleted file mode 100644 index cd2f358ac..000000000 --- a/src/catalogue/templates/catalogue/related_books.html +++ /dev/null @@ -1,24 +0,0 @@ -{% spaceless %} - {% load catalogue_random_book from catalogue_tags %} - {% load picture_random_picture from picture_tags %} - - {% for pic in pics %} - {{ pic.mini_box }} - {% endfor %} - - {% for book in books %} - {{ book.mini_box }} - {% endfor %} - - {% if random %} - {% catalogue_random_book random_excluded_books as random_book %} - {% if random_book %} - {{ random_book.mini_box }} - {% else %} - {% picture_random_picture random_excluded_pics as random_pic %} - {% if random_pic %} - {{ random_pic.mini_box }} - {% endif %} - {% endif %} - {% endif %} -{% endspaceless %} diff --git a/src/catalogue/urls.py b/src/catalogue/urls.py index d02f4a41e..bb00c8a36 100644 --- a/src/catalogue/urls.py +++ b/src/catalogue/urls.py @@ -7,17 +7,10 @@ from django.views.generic import ListView, RedirectView from catalogue.feeds import AudiobookFeed from catalogue.models import Book from catalogue import views -import picture.views import search.views urlpatterns = [ - path('obraz/strona/', picture.views.picture_page, name='picture_page'), - # pictures - currently pictures are coupled with catalogue, hence the url is here - path('obraz/', picture.views.picture_list_thumb, name='picture_list_thumb'), - path('obraz/.html', picture.views.picture_viewer, name='picture_viewer'), - path('obraz//', picture.views.picture_detail, name='picture_detail'), - # old search page - redirected path('szukaj/', RedirectView.as_view( url='/szukaj/', query_string=True, permanent=True)), diff --git a/src/picture/migrations/0005_auto_20141022_1001.py b/src/picture/migrations/0005_auto_20141022_1001.py index 712c0e678..efc8f22f1 100644 --- a/src/picture/migrations/0005_auto_20141022_1001.py +++ b/src/picture/migrations/0005_auto_20141022_1001.py @@ -2,28 +2,7 @@ # Copyright © Fundacja Wolne Lektury. See NOTICE for more information. # import json -from django.core.files.base import ContentFile -from django.db import models, migrations -from django.template.loader import render_to_string - - -def rebuild_extra_info(apps, schema_editor): - Picture = apps.get_model("picture", "Picture") - from librarian.picture import PictureInfo - from librarian import dcparser - for pic in Picture.objects.all(): - info = dcparser.parse(pic.xml_file.path, PictureInfo) - 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': areas_json['things'], - 'themes': areas_json['themes'], - }) - pic.html_file.save("%s.html" % pic.slug, ContentFile(html_text)) - pic.save() +from django.db import migrations class Migration(migrations.Migration): @@ -33,5 +12,4 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(rebuild_extra_info), ] diff --git a/src/picture/models.py b/src/picture/models.py index fc659ba21..ae3d66a79 100644 --- a/src/picture/models.py +++ b/src/picture/models.py @@ -43,19 +43,6 @@ class PictureArea(models.Model): tags = managers.TagDescriptor(catalogue.models.Tag) tag_relations = GenericRelation(catalogue.models.Tag.intermediary_table_model) - short_html_url_name = 'picture_area_short' - - @classmethod - def rectangle(cls, picture, kind, coords): - pa = PictureArea() - pa.picture = picture - pa.kind = kind - pa.area = json.dumps(coords) - return pa - - def get_area_json(self): - return json.loads(self.area) - class Picture(models.Model): """ @@ -85,272 +72,11 @@ class Picture(models.Model): tags = managers.TagDescriptor(catalogue.models.Tag) tag_relations = GenericRelation(catalogue.models.Tag.intermediary_table_model) - short_html_url_name = 'picture_short' - - is_picture = True - - class AlreadyExists(Exception): - pass - class Meta: ordering = ('sort_key_author', 'sort_key') verbose_name = 'obraz' verbose_name_plural = 'obrazy' - def get_extra_info_json(self): - return json.loads(self.extra_info or '{}') - - def save(self, force_insert=False, force_update=False, **kwargs): - from sortify import sortify - - self.sort_key = sortify(self.title)[:120] - - try: - author = self.authors().first().sort_key - except AttributeError: - author = '' - self.sort_key_author = author - - ret = super(Picture, self).save(force_insert, force_update) - - return ret - def __str__(self): return self.title - - def authors(self): - return self.tags.filter(category='author') - - def epochs(self): - return self.tags.filter(category='epoch') - - def genres(self): - return self.tags.filter(category='genre') - - def kinds(self): - return self.tags.filter(category='kind') - - def tag_unicode(self, category): - relations = prefetched_relations(self, category) - if relations: - return ', '.join(rel.tag.name for rel in relations) - else: - return ', '.join(self.tags.filter(category=category).values_list('name', flat=True)) - - def author_unicode(self): - return self.tag_unicode('author') - - def tags_by_category(self): - return split_tags(self.tags) - - def get_absolute_url(self): - return reverse('picture_detail', args=[self.slug]) - - def get_initial(self): - try: - return re.search(r'\w', self.title, re.U).group(0) - except AttributeError: - return '' - - def get_next(self): - try: - return type(self).objects.filter(sort_key__gt=self.sort_key)[0] - except IndexError: - return None - - def get_previous(self): - try: - return type(self).objects.filter(sort_key__lt=self.sort_key).order_by('-sort_key')[0] - except IndexError: - return None - - @classmethod - def from_xml_file(cls, xml_file, image_file=None, image_store=None, overwrite=False): - """ - Import xml and it's accompanying image file. - If image file is missing, it will be fetched by librarian.picture.ImageStore - which looks for an image file in the same directory the xml is, with extension matching - its mime type. - """ - from sortify import sortify - from django.core.files import File - from librarian.picture import WLPicture, ImageStore - close_xml_file = False - close_image_file = False - - if image_file is not None and not isinstance(image_file, File): - image_file = File(open(image_file, 'rb')) - close_image_file = True - - if not isinstance(xml_file, File): - xml_file = File(open(xml_file)) - close_xml_file = True - - with transaction.atomic(): - # use librarian to parse meta-data - if image_store is None: - image_store = ImageStore(picture_storage.path('images')) - picture_xml = WLPicture.from_file(xml_file, image_store=image_store) - - picture, created = Picture.objects.get_or_create(slug=picture_xml.slug[:120]) - if not created and not overwrite: - raise Picture.AlreadyExists('Picture %s already exists' % picture_xml.slug) - - picture.areas.all().delete() - picture.title = str(picture_xml.picture_info.title) - picture.extra_info = json.dumps(picture_xml.picture_info.to_dict()) - - picture_tags = set([t for (t, rel) in catalogue.models.Tag.tags_from_info(picture_xml.picture_info)]) - for tag in picture_tags: - if not tag.for_pictures: - tag.for_pictures = True - tag.save() - - area_data = {'themes': {}, 'things': {}} - - # Treat all names in picture XML as in default language. - lang = settings.LANGUAGE_CODE - - for part in picture_xml.partiter(): - if picture_xml.frame: - c = picture_xml.frame[0] - part['coords'] = [[p[0] - c[0], p[1] - c[1]] for p in part['coords']] - if part.get('object', None) is not None: - _tags = set() - for objname in part['object'].split(','): - objname = objname.strip() - assert objname, 'Empty object name' - # str.capitalize() is wrong, because it also lowers letters - objname = objname[0].upper() + objname[1:] - tag, created = catalogue.models.Tag.objects.get_or_create( - slug=slugify(objname), category='thing') - if created: - tag.name = objname - setattr(tag, 'name_%s' % lang, tag.name) - tag.sort_key = sortify(tag.name) - tag.for_pictures = True - tag.save() - area_data['things'][tag.slug] = { - 'object': objname, - 'coords': part['coords'], - } - - _tags.add(tag) - if not tag.for_pictures: - tag.for_pictures = True - tag.save() - area = PictureArea.rectangle(picture, 'thing', part['coords']) - area.save() - # WTF thing area does not inherit tags from picture and theme area does, is it intentional? - area.tags = _tags - else: - _tags = set() - for motifs in part['themes']: - for motif in motifs.split(','): - tag, created = catalogue.models.Tag.objects.get_or_create( - slug=slugify(motif), category='theme') - if created: - tag.name = motif - tag.sort_key = sortify(tag.name) - tag.for_pictures = True - tag.save() - # motif_tags.add(tag) - _tags.add(tag) - if not tag.for_pictures: - tag.for_pictures = True - tag.save() - area_data['themes'][tag.slug] = { - 'theme': motif, - 'coords': part['coords'] - } - - logging.debug("coords for theme: %s" % part['coords']) - area = PictureArea.rectangle(picture, 'theme', part['coords']) - area.save() - area.tags = _tags.union(picture_tags) - - picture.tags = picture_tags - picture.areas_json = json.dumps(area_data) - - if image_file is not None: - img = image_file - else: - img = picture_xml.image_file() - - modified = cls.crop_to_frame(picture_xml, img) - modified = cls.add_source_note(picture_xml, modified) - - picture.width, picture.height = modified.size - - modified_file = BytesIO() - modified.save(modified_file, format='JPEG', quality=95) - # FIXME: hardcoded extension - detect from DC format or orginal filename - picture.image_file.save(path.basename(picture_xml.image_path), File(modified_file)) - - picture.xml_file.save("%s.xml" % picture.slug, File(xml_file)) - picture.save() - - if close_xml_file: - xml_file.close() - if close_image_file: - image_file.close() - - return picture - - @classmethod - def crop_to_frame(cls, wlpic, image_file): - img = Image.open(image_file) - if wlpic.frame is None or wlpic.frame == [[0, 0], [-1, -1]]: - return img - img = img.crop(itertools.chain(*wlpic.frame)) - return img - - @staticmethod - def add_source_note(wlpic, img): - from PIL import ImageDraw, ImageFont - from librarian import get_resource - - annotated = Image.new(img.mode, (img.size[0], img.size[1] + 40), (255, 255, 255)) - annotated.paste(img, (0, 0)) - annotation = Image.new('RGB', (img.size[0] * 3, 120), (255, 255, 255)) - ImageDraw.Draw(annotation).text( - (30, 15), - wlpic.picture_info.source_name, - (0, 0, 0), - font=ImageFont.truetype(get_resource("fonts/DejaVuSerif.ttf"), 75) - ) - annotated.paste(annotation.resize((img.size[0], 40), Image.ANTIALIAS), (0, img.size[1])) - return annotated - - @property - def info(self): - if not hasattr(self, '_info'): - from librarian import dcparser - from librarian import picture - info = dcparser.parse(self.xml_file.path, picture.PictureInfo) - self._info = info - return self._info - - def pretty_title(self, html_links=False): - names = [(tag.name, tag.get_absolute_url()) for tag in self.authors().only('name', 'category', 'slug')] - names.append((self.title, self.get_absolute_url())) - - if html_links: - names = ['%s' % (tag[1], tag[0]) for tag in names] - else: - names = [tag[0] for tag in names] - return ', '.join(names) - - @cached_render('picture/picture_mini_box.html') - def mini_box(self): - return { - 'picture': self, - } - - def related_themes(self): - return catalogue.models.Tag.objects.usage_for_queryset( - self.areas.all(), counts=True).filter(category__in=('theme', 'thing')) - - def clear_cache(self): - clear_cached_renders(self.mini_box) diff --git a/src/picture/templates/admin/picture/picture/change_list.html b/src/picture/templates/admin/picture/picture/change_list.html deleted file mode 100644 index eefffbfbc..000000000 --- a/src/picture/templates/admin/picture/picture/change_list.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "admin/change_list.html" %} - -{% block content %} -
- {% csrf_token %} -

- XML:
- Obraz:
- -

-
- {{ block.super }} -{% endblock content %} diff --git a/src/picture/templates/picture/picture_detail.html b/src/picture/templates/picture/picture_detail.html deleted file mode 100644 index bc15d61f7..000000000 --- a/src/picture/templates/picture/picture_detail.html +++ /dev/null @@ -1,177 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} - -{% load chunks %} -{% load static %} -{% load thumbnail %} -{% load catalogue_tags %} - - -{% block global-content %} - - - -
-
- -
- - -
-
- - {% for tag in picture.authors %} -
-
- {% include 'catalogue/author_box.html' %} -
-
- {% endfor %} - -
-
- {% if themes %} -

Motywy obecne na tym obrazie Wszystkie motywy

-
- -
- - - {% endif %} - {% if things %} -

Obiekty na tym obrazie Wszystkie obiekty

-
- -
- - - {% endif %} - -
-
-
- - - - - -
-
-
-

Czytaj także

- - {% related_pictures_2022 picture=picture as related_books %} - {% for rel in related_books %} - - {% endfor %} - - - -
-
- -
- - -{% endblock %} diff --git a/src/picture/templates/picture/picture_info.html b/src/picture/templates/picture/picture_info.html deleted file mode 100644 index 79c3043b9..000000000 --- a/src/picture/templates/picture/picture_info.html +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/src/picture/templates/picture/picture_mini_box.html b/src/picture/templates/picture/picture_mini_box.html deleted file mode 100644 index c1736a097..000000000 --- a/src/picture/templates/picture/picture_mini_box.html +++ /dev/null @@ -1,18 +0,0 @@ -{% spaceless %} - {% load thumbnail %} - {% with picture.author_unicode as author %} - - {% endwith %} -{% endspaceless %} diff --git a/src/picture/templates/picture/picture_short.html b/src/picture/templates/picture/picture_short.html deleted file mode 100644 index 4b3a7cb0a..000000000 --- a/src/picture/templates/picture/picture_short.html +++ /dev/null @@ -1,86 +0,0 @@ -{% load i18n %} -{% load thumbnail %} -{% load catalogue_tags social_tags %} -{% load picture_tags %} - -
-
-
-
- {% with picture.get_absolute_url as main_link %} - {% with picture.tags_by_category as tags %} -
-
- {% for tag in tags.author %} - {{ tag }}{% if not forloop.last %}, - {% endif %}{% endfor %} -
- -
- -
- {% block picture-view %} - {% if main_link %}{% endif %} - {% thumbnail picture.image_file "216x288" crop="center" as thumb %} - - {% endthumbnail %} - {% if main_link %}{% endif %} - {% endblock %} - {# what about licensing icons here #} -
- -
- {% spaceless %} - - {% trans "Epoka" %}:  - {% for tag in tags.epoch %} - {{ tag }} - {% if not forloop.last %}, {% endif %} - {% endfor %} - - - - - {% trans "Rodzaj" %}:  - {% for tag in tags.kind %} - {{ tag }} - {% if not forloop.last %}, {% endif %} - {% endfor %} - - - - - {% trans "Gatunek" %}:  - {% for tag in tags.genre %} - {{ tag }} - {% if not forloop.last %}, {% endif %} - {% endfor %} - - - - {% block extra_categories %} - {% endblock %} - {% endspaceless %} -
- {% endwith %} - {% endwith %} -
- -
- {% block book-box-extra-info %}{% endblock %} - {% block box-append %}{% endblock %} - {% block right-column %}{% endblock %} -
-
-
diff --git a/src/picture/templates/picture/picture_viewer.html b/src/picture/templates/picture/picture_viewer.html deleted file mode 100644 index fa9d039bf..000000000 --- a/src/picture/templates/picture/picture_viewer.html +++ /dev/null @@ -1,103 +0,0 @@ -{% extends "catalogue/viewer_base.html" %} -{% load i18n %} -{% load static from static %} -{% load catalogue_tags %} -{% load thumbnail %} - - -{% block title %}{{ picture.pretty_title }}{% endblock %} - - -{% block body-id %}picture-viewer{% endblock %} - - -{% block js-dependencies %} - -{% endblock %} - - -{% block no-menu-extra %} -
  • +
  • -
  • -
  • -{% endblock %} - - -{% block menu %} -
  • - + - - -
  • - - {% spaceless %} -
  • - {% with picture.get_previous as prev %} - {% if prev %}<{%endif %} - {% endwith %} - {% with picture.get_next as next %} - {% if next %}>{% endif %} - {% endwith %} -
  • - {% endspaceless %} - -
  • - - {{ picture.pretty_title }} - -
  • - -
  • {% trans "Obiekty" context "na obrazie" %}
  • -
  • {% trans "Motywy" %}
  • - - - -
  • - {% for sponsor in sponsors %} - {% thumbnail sponsor.logo "120x300" as logo %} - {{ sponsor.name }} - {% endthumbnail %} - {% endfor %} -
  • -{% endblock %} - -{% block main %} -
    - {% thumbnail picture.image_file "700x500" as pic %} -
    -
    - {% endthumbnail %} -
    -{% endblock %} - - -{% block footer %} - {{ picture.html_file.read.decode|safe }} - -
    -
    - - Wolne Lektury - - {% for sponsor in sponsors %} - {% thumbnail sponsor.logo "220x220" as logo %} - {{ sponsor.name }} - {% endthumbnail %} - {% endfor %} -
    - - {% book_info picture %} -
    - -
    - {% include 'picture/picture_short.html' %} -
    -{% endblock %} diff --git a/src/picture/templatetags/__init__.py b/src/picture/templatetags/__init__.py deleted file mode 100644 index 8b1378917..000000000 --- a/src/picture/templatetags/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/picture/views.py b/src/picture/views.py deleted file mode 100644 index 72d1e37b2..000000000 --- a/src/picture/views.py +++ /dev/null @@ -1,118 +0,0 @@ -# This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Wolne Lektury. See NOTICE for more information. -# -from collections import Counter -from django.conf import settings -from django.contrib.auth.decorators import permission_required -from django.shortcuts import render, get_object_or_404, render -from picture.models import Picture -from catalogue.models import Tag -from catalogue.utils import split_tags -from sponsors.models import Sponsor -from wolnelektury.utils import ajax -from catalogue.helpers import get_top_level_related_tags - - -def picture_list_thumb(request): - pictures = Picture.objects.all() - - related_tags = Tag.objects.usage_for_model(Picture, counts=True) - related_tags = sorted(related_tags, key=lambda t: -t.count) - suggestion_categories = Counter() - suggestions = [] - for t in related_tags: - if suggestion_categories[t.category] < 3: - suggestion_categories[t.category] += 1 - suggestions.append(t) - if sum(suggestion_categories.values()) == 10: - break - template_name = 'catalogue/author_detail.html' - return render(request, template_name, { - 'object_list': pictures, - 'title': 'Galeria', - 'tags': [], - 'suggest': suggestions, - 'list_type': 'gallery', - }) - - -def picture_detail(request, slug): - picture = get_object_or_404(Picture, slug=slug) - - theme_things = split_tags(picture.related_themes()) - - template_name = 'picture/picture_detail.html' - - return render(request, template_name, { - 'picture': picture, - 'themes': theme_things.get('theme', []), - 'things': theme_things.get('thing', []), - 'active_menu_item': 'gallery', - }) - - -def picture_viewer(request, slug): - picture = get_object_or_404(Picture, slug=slug) - 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]) - return render(request, "picture/picture_viewer.html", { - 'picture': picture, - 'sponsors': sponsors, - }) - - -@ajax(method='get') -def picture_page(request, key=None): - pictures = Picture.objects.order_by('-id') - if key is not None: - pictures = pictures.filter(id__lt=key) - pictures = pictures[:settings.PICTURE_PAGE_SIZE] - picture_data = [ - { - 'id': picture.id, - 'title': picture.title, - 'author': picture.author_unicode(), - 'epoch': picture.tag_unicode('epoch'), - 'kind': picture.tag_unicode('kind'), - 'genre': picture.tag_unicode('genre'), - 'style': picture.get_extra_info_json()['style'], - 'image_url': picture.image_file.url, - 'width': picture.width, - 'height': picture.height, - } - for picture in pictures - ] - return { - 'pictures': picture_data, - 'count': Picture.objects.count(), - } - - -# ========= -# = Admin = -# ========= -@permission_required('picture.add_picture') -def import_picture(request): - """docstring for import_book""" - from django.http import HttpResponse - from picture.forms import PictureImportForm - - import_form = PictureImportForm(request.POST, request.FILES) - if import_form.is_valid(): - try: - import_form.save() - except: - import sys - import pprint - import traceback - info = sys.exc_info() - exception = pprint.pformat(info[1]) - tb = '\n'.join(traceback.format_tb(info[2])) - return HttpResponse("Wystąpił błąd: %(exception)s\n\n%(tb)s" % - {'exception': exception, 'tb': tb}, content_type='text/plain') - return HttpResponse('Obraz został zimportowany') - else: - return HttpResponse('Błąd importu pliku: %r' % import_form.errors)