From f3dbec099e62eec49b0a9e1d293e7f0dc12361c1 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Thu, 23 Oct 2014 16:03:31 +0200 Subject: [PATCH] Nicer dictionary filtering. --- .../locale/pl/LC_MESSAGES/django.mo | Bin 788 -> 962 bytes .../locale/pl/LC_MESSAGES/django.po | 33 ++++++++-- .../migrations/0003_auto_20141023_1445.py | 36 +++++++++++ apps/dictionary/models.py | 31 +++++++-- .../templates/dictionary/note_list.html | 17 ++--- apps/dictionary/templatetags/__init__.py | 0 apps/dictionary/templatetags/set_get.py | 20 ++++++ apps/dictionary/tests.py | 14 ++-- apps/dictionary/views.py | 61 +++++++++++------- lib/librarian | 2 +- 10 files changed, 164 insertions(+), 50 deletions(-) create mode 100644 apps/dictionary/migrations/0003_auto_20141023_1445.py create mode 100644 apps/dictionary/templatetags/__init__.py create mode 100644 apps/dictionary/templatetags/set_get.py diff --git a/apps/dictionary/locale/pl/LC_MESSAGES/django.mo b/apps/dictionary/locale/pl/LC_MESSAGES/django.mo index 8290bd9ff00464cfe669fd34239d947893601d3d..2dbef9a045833f100998fd2d17891054e24c1492 100644 GIT binary patch delta 385 zcmbQjc8I(E-A5qJ0pv3R6@k=q0coHz1{NR&>4O3`2B10y)`>^$C#y213mEGf z8tEFEDHvE<85w9B7*3wUD8Xx}YhVcyG&Hj^G}wHQ(T;JlAG73SBW5q*fTF6(g3RJd PhVtU7%Hopj%v1&ddaEFo diff --git a/apps/dictionary/locale/pl/LC_MESSAGES/django.po b/apps/dictionary/locale/pl/LC_MESSAGES/django.po index 0fc8f2955..73c52d131 100644 --- a/apps/dictionary/locale/pl/LC_MESSAGES/django.po +++ b/apps/dictionary/locale/pl/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: WolneLektury-dictionary\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-12-16 09:20+0100\n" -"PO-Revision-Date: 2011-09-16 16:10+0100\n" +"POT-Creation-Date: 2014-10-23 16:02+0200\n" +"PO-Revision-Date: 2014-10-23 16:03+0100\n" "Last-Translator: Radek Czajka \n" "Language-Team: LANGUAGE \n" "Language: pl\n" @@ -17,22 +17,41 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" +"X-Generator: Poedit 1.5.4\n" -#: templates/dictionary/note_list.html:7 -#: templates/dictionary/note_list.html:11 +#: templates/dictionary/note_list.html:8 +#: templates/dictionary/note_list.html:12 msgid "Footnotes" msgstr "Przypisy" -#: templates/dictionary/note_list.html:16 +#: templates/dictionary/note_list.html:17 msgid "By first letter" msgstr "Pierwsza litera" -#: templates/dictionary/note_list.html:18 -#: templates/dictionary/note_list.html:20 +#: templates/dictionary/note_list.html:19 +#: templates/dictionary/note_list.html:21 +#: templates/dictionary/note_list.html:37 +#: templates/dictionary/note_list.html:39 +#: templates/dictionary/note_list.html:56 +#: templates/dictionary/note_list.html:58 +#: templates/dictionary/note_list.html:74 +#: templates/dictionary/note_list.html:76 msgid "all" msgstr "wszystkie" #: templates/dictionary/note_list.html:35 +msgid "By type" +msgstr "Według typu" + +#: templates/dictionary/note_list.html:54 +msgid "By qualifier" +msgstr "Według kwalifikatora" + +#: templates/dictionary/note_list.html:72 +msgid "By language" +msgstr "Według języka" + +#: templates/dictionary/note_list.html:92 #, python-format msgid "%(c)s footnote found" msgid_plural "%(c)s footnotes found" diff --git a/apps/dictionary/migrations/0003_auto_20141023_1445.py b/apps/dictionary/migrations/0003_auto_20141023_1445.py new file mode 100644 index 000000000..ee4beeebe --- /dev/null +++ b/apps/dictionary/migrations/0003_auto_20141023_1445.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('dictionary', '0002_auto_20141006_1422'), + ] + + operations = [ + migrations.CreateModel( + name='Qualifier', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('qualifier', models.CharField(unique=True, max_length=128, db_index=True)), + ('name', models.CharField(max_length=255)), + ], + options={ + 'ordering': ['qualifier'], + }, + bases=(models.Model,), + ), + migrations.RemoveField( + model_name='note', + name='qualifier', + ), + migrations.AddField( + model_name='note', + name='qualifiers', + field=models.ManyToManyField(to='dictionary.Qualifier', null=True), + preserve_default=True, + ), + ] diff --git a/apps/dictionary/models.py b/apps/dictionary/models.py index 73fbb0d47..d0d8b69ac 100644 --- a/apps/dictionary/models.py +++ b/apps/dictionary/models.py @@ -12,12 +12,23 @@ task_logger = get_task_logger(__name__) from catalogue.models import Book +class Qualifier(models.Model): + qualifier = models.CharField(max_length=128, db_index=True, unique=True) + name = models.CharField(max_length=255) + + class Meta: + ordering = ['qualifier'] + + def __unicode__(self): + return self.name or self.qualifier + + class Note(models.Model): """Represents a single annotation from a book.""" html = models.TextField() sort_key = models.CharField(max_length=128, db_index=True) fn_type = models.CharField(max_length=10, db_index=True) - qualifier = models.CharField(max_length=128, db_index=True, blank=True) + qualifiers = models.ManyToManyField(Qualifier, null=True) language = models.CharField(max_length=10, db_index=True) class Meta: @@ -41,25 +52,35 @@ def build_notes(book): book.notesource_set.all().delete() if book.html_file: from librarian import html - for anchor, fn_type, qualifier, text_str, html_str in \ + from librarian.fn_qualifiers import FN_QUALIFIERS + + for anchor, fn_type, qualifiers, text_str, html_str in \ html.extract_annotations(book.html_file.path): sort_key = sortify(text_str).strip()[:128] - qualifier = (qualifier or '')[:128] + language = book.language note = None notes = Note.objects.filter(sort_key=sort_key, - qualifier=qualifier, fn_type=fn_type, + fn_type=fn_type, language=language, html=html_str) if notes: note = notes[0] else: note = Note.objects.create( sort_key=sort_key, - qualifier=qualifier, html=html_str, fn_type=fn_type, language=language ) + + qualifier_objects = [] + for qualifier in qualifiers: + obj, created = Qualifier.objects.get_or_create( + qualifier=qualifier, defaults={ + 'name': FN_QUALIFIERS.get(qualifier, '') + }) + qualifier_objects.append(obj) + note.qualifiers = qualifier_objects note.notesource_set.create(book=book, anchor=anchor) Note.objects.filter(notesource=None).delete() diff --git a/apps/dictionary/templates/dictionary/note_list.html b/apps/dictionary/templates/dictionary/note_list.html index 5109454b8..330b2a7f8 100755 --- a/apps/dictionary/templates/dictionary/note_list.html +++ b/apps/dictionary/templates/dictionary/note_list.html @@ -1,5 +1,6 @@ {% extends "base.html" %} {% load i18n pagination_tags %} +{% load set_get from set_get %} {% block bodyid %}footnotes{% endblock %} @@ -15,7 +16,7 @@

{% trans "By first letter" %}: {% if letter %} - {% trans "all" %} + {% trans "all" %} {% else %} {% trans "all" %} {% endif %} @@ -25,7 +26,7 @@ {% if let == letter %} {{ let|upper }} {% else %} - {{ let|upper }} + {{ let|upper }} {% endif %} {% endfor %}

@@ -33,7 +34,7 @@

{% trans "By type" %}: {% if fn_type %} - {% trans "all" %} + {% trans "all" %} {% else %} {% trans "all" %} {% endif %} @@ -43,7 +44,7 @@ {% if fnt == fn_type %} {{ fnt }} {% else %} - {{ fnt }} + {{ fnt }} {% endif %} {% endfor %}

@@ -52,7 +53,7 @@

{% trans "By qualifier" %}: {% if qualifier %} - {% trans "all" %} + {% trans "all" %} {% else %} {% trans "all" %} {% endif %} @@ -62,7 +63,7 @@ {% if qual == qualifier %} {{ qual }} {% else %} - {{ qual }} + {{ qual }} {% endif %} {% endfor %}

@@ -70,7 +71,7 @@

{% trans "By language" %}: {% if language %} - {% trans "all" %} + {% trans "all" %} {% else %} {% trans "all" %} {% endif %} @@ -80,7 +81,7 @@ {% if lang == language %} {{ lang }} {% else %} - {{ lang }} + {{ lang }} {% endif %} {% endfor %}

diff --git a/apps/dictionary/templatetags/__init__.py b/apps/dictionary/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/dictionary/templatetags/set_get.py b/apps/dictionary/templatetags/set_get.py new file mode 100644 index 000000000..9afe3e87f --- /dev/null +++ b/apps/dictionary/templatetags/set_get.py @@ -0,0 +1,20 @@ +# -*- 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 import template +from django.utils.http import urlencode + +register = template.Library() + + +@register.simple_tag(takes_context=True) +def set_get(context, *omit, **kwargs): + request = context['request'] + query = request.GET.dict() + for k in omit: + if k in query: + del query[k] + for k, v in kwargs.items(): + query[k] = v + return urlencode(query) diff --git a/apps/dictionary/tests.py b/apps/dictionary/tests.py index 526819a01..0c83cf183 100755 --- a/apps/dictionary/tests.py +++ b/apps/dictionary/tests.py @@ -24,7 +24,7 @@ class DictionaryTests(WLTestCase): rose --- kind of a flower. rose --- kind of a flower. - rose (color) --- #FF007F. + rose (techn.) --- #FF007F. """ @@ -33,20 +33,20 @@ class DictionaryTests(WLTestCase): self.assertEqual( len(self.client.get('/przypisy/').context['object_list']), 2, - 'There should be a note on the note list.') + 'There should be two notes on the note list.') self.assertEqual( - len(self.client.get('/przypisy/?ltr=r').context['object_list']), + len(self.client.get('/przypisy/?ltr=a').context['object_list']), 0, 'There should not be a note for the letter A.') self.assertEqual( len(self.client.get('/przypisy/?ltr=r').context['object_list']), 2, - 'There should be a note for the letter R.') + 'Both notes start with the letter R.') self.assertEqual( - len(self.client.get('/przypisy/?qual=color').context['object_list']), - 2, - 'There should be a note for the letter R.') + len(self.client.get('/przypisy/?qual=techn.').context['object_list']), + 1, + 'There should be a note qualified with \'techn.\' qualifier.') diff --git a/apps/dictionary/views.py b/apps/dictionary/views.py index fa9712dda..42aab6c36 100755 --- a/apps/dictionary/views.py +++ b/apps/dictionary/views.py @@ -2,48 +2,65 @@ # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # -from dictionary.models import Note +from dictionary.models import Note, Qualifier from django.views.generic.list import ListView -from django.db.models import Count +from django.db.models import Count, Q class NotesView(ListView): def get_queryset(self): - self.letters = ["0-9"] + [chr(a) for a in range(ord('a'), ord('z')+1)] - self.letter = self.request.GET.get('ltr') + objects = Note.objects.select_related('book').all() + filters = {} - self.qualifiers = Note.objects.order_by('qualifier').filter(qualifier__startswith='f').values_list( - 'qualifier', flat=True).distinct() - self.qualifier = self.request.GET.get('qual') + try: + self.qualifier = Qualifier.objects.get(qualifier=self.request.GET.get('qual')) + except Qualifier.DoesNotExist: + self.qualifier = None + else: + filters['qualifier'] = Q(qualifiers=self.qualifier) - self.languages = Note.objects.order_by('language').values_list( - 'language', flat=True).distinct() self.language = self.request.GET.get('lang') + if self.language: + filters['language'] = Q(language=self.language) - self.fn_types = Note.objects.order_by('fn_type').values_list( - 'fn_type', flat=True).distinct() self.fn_type = self.request.GET.get('type') + if self.fn_type: + filters['fn_type'] = Q(fn_type=self.fn_type) - objects = Note.objects.select_related('book').all() - + self.letter = self.request.GET.get('ltr') if self.letter == "0-9": objects = objects.filter(sort_key__regex=r"^[0-9]") + #filters['letter'] = Q(sort_key__regex=r"^[0-9]") elif self.letter: objects = objects.filter(sort_key__startswith=self.letter) + #filters['letter'] = Q(sort_key__startswith=self.letter) - if self.qualifier: - objects = objects.filter(qualifier=self.qualifier) + self.letters = ["0-9"] + [chr(a) for a in range(ord('a'), ord('z')+1)] - if self.language: - objects = objects.filter(language=self.language) + nobj = objects + for key, fltr in filters.items(): + if key != 'qualifier': + nobj = nobj.filter(fltr) + self.qualifiers = Qualifier.objects.filter(note__in=nobj).distinct() - if self.fn_type: - objects = objects.filter(fn_type=self.fn_type) + nobj = objects + for key, fltr in filters.items(): + if key != 'language': + nobj = nobj.filter(fltr) + self.languages = nobj.order_by('language').values_list( + 'language', flat=True).distinct() - return objects + nobj = objects + for key, fltr in filters.items(): + if key != 'fn_type': + nobj = nobj.filter(fltr) + self.fn_types = nobj.order_by('fn_type').values_list( + 'fn_type', flat=True).distinct() - # TODO: wewn. wyszukiwarka, czy wg definiendum? - # TODO: filtr języka + for f in filters.values(): + objects = objects.filter(f) + + return objects def get_context_data(self, **kwargs): context = super(NotesView, self).get_context_data(**kwargs) diff --git a/lib/librarian b/lib/librarian index a04f11bae..a3be47950 160000 --- a/lib/librarian +++ b/lib/librarian @@ -1 +1 @@ -Subproject commit a04f11baee3eb7d090867c2d5639a120ec3217b8 +Subproject commit a3be479506edf42dc58feb22b26e4f5da1e49edd -- 2.20.1