Nicer dictionary filtering.
authorRadek Czajka <radekczajka@nowoczesnapolska.org.pl>
Thu, 23 Oct 2014 14:03:31 +0000 (16:03 +0200)
committerRadek Czajka <radekczajka@nowoczesnapolska.org.pl>
Thu, 23 Oct 2014 14:03:31 +0000 (16:03 +0200)
apps/dictionary/locale/pl/LC_MESSAGES/django.mo
apps/dictionary/locale/pl/LC_MESSAGES/django.po
apps/dictionary/migrations/0003_auto_20141023_1445.py [new file with mode: 0644]
apps/dictionary/models.py
apps/dictionary/templates/dictionary/note_list.html
apps/dictionary/templatetags/__init__.py [new file with mode: 0644]
apps/dictionary/templatetags/set_get.py [new file with mode: 0644]
apps/dictionary/tests.py
apps/dictionary/views.py
lib/librarian

index 8290bd9..2dbef9a 100644 (file)
Binary files a/apps/dictionary/locale/pl/LC_MESSAGES/django.mo and b/apps/dictionary/locale/pl/LC_MESSAGES/django.mo differ
index 0fc8f29..73c52d1 100644 (file)
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: WolneLektury-dictionary\n"
 "Report-Msgid-Bugs-To: \n"
 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 <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "Language: pl\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: LANGUAGE <LL@li.org>\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"
 "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"
 
 msgid "Footnotes"
 msgstr "Przypisy"
 
-#: templates/dictionary/note_list.html:16
+#: templates/dictionary/note_list.html:17
 msgid "By first letter"
 msgstr "Pierwsza litera"
 
 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 "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"
 #, 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 (file)
index 0000000..ee4beee
--- /dev/null
@@ -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,
+        ),
+    ]
index 73fbb0d..d0d8b69 100644 (file)
@@ -12,12 +12,23 @@ task_logger = get_task_logger(__name__)
 from catalogue.models import Book
 
 
 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)
 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:
     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
         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]
                     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,
                 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,
                     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
                         )
                         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()
                 note.notesource_set.create(book=book, anchor=anchor)
 
         Note.objects.filter(notesource=None).delete()
index 5109454..330b2a7 100755 (executable)
@@ -1,5 +1,6 @@
 {% extends "base.html" %}
 {% load i18n pagination_tags %}
 {% extends "base.html" %}
 {% load i18n pagination_tags %}
+{% load set_get from set_get %}
 
 
 {% block bodyid %}footnotes{% endblock %}
 
 
 {% block bodyid %}footnotes{% endblock %}
@@ -15,7 +16,7 @@
 <p>
 {% trans "By first letter" %}:
 {% if letter %}
 <p>
 {% trans "By first letter" %}:
 {% if letter %}
-    <a href='?'>{% trans "all" %}</a>
+    <a href='?{% set_get "page" "ltr" %}'>{% trans "all" %}</a>
 {% else %}
     <strong>{% trans "all" %}</strong>
 {% endif %}
 {% else %}
     <strong>{% trans "all" %}</strong>
 {% endif %}
@@ -25,7 +26,7 @@
     {% if let == letter %}
         <strong>{{ let|upper }}</strong>
     {% else %}
     {% if let == letter %}
         <strong>{{ let|upper }}</strong>
     {% else %}
-        <a href='?ltr={{ let }}'>{{ let|upper }}</a>
+        <a href='?{% set_get "page" ltr=let %}'>{{ let|upper }}</a>
     {% endif %}
 {% endfor %}
 </p>
     {% endif %}
 {% endfor %}
 </p>
@@ -33,7 +34,7 @@
 <p>
 {% trans "By type" %}:
 {% if fn_type %}
 <p>
 {% trans "By type" %}:
 {% if fn_type %}
-    <a href='?'>{% trans "all" %}</a>
+    <a href='?{% set_get "page" "type" %}'>{% trans "all" %}</a>
 {% else %}
     <strong>{% trans "all" %}</strong>
 {% endif %}
 {% else %}
     <strong>{% trans "all" %}</strong>
 {% endif %}
@@ -43,7 +44,7 @@
     {% if fnt == fn_type %}
         <strong>{{ fnt }}</strong>
     {% else %}
     {% if fnt == fn_type %}
         <strong>{{ fnt }}</strong>
     {% else %}
-        <a href='?type={{ fnt }}'>{{ fnt }}</a>
+        <a href='?{% set_get "page" type=fnt %}'>{{ fnt }}</a>
     {% endif %}
 {% endfor %}
 </p>
     {% endif %}
 {% endfor %}
 </p>
@@ -52,7 +53,7 @@
 <p>
 {% trans "By qualifier" %}:
 {% if qualifier %}
 <p>
 {% trans "By qualifier" %}:
 {% if qualifier %}
-    <a href='?'>{% trans "all" %}</a>
+    <a href='?{% set_get "page" "qual" %}'>{% trans "all" %}</a>
 {% else %}
     <strong>{% trans "all" %}</strong>
 {% endif %}
 {% else %}
     <strong>{% trans "all" %}</strong>
 {% endif %}
@@ -62,7 +63,7 @@
     {% if qual == qualifier %}
         <strong>{{ qual }}</strong>
     {% else %}
     {% if qual == qualifier %}
         <strong>{{ qual }}</strong>
     {% else %}
-        <a href='?qual={{ qual|urlencode }}'>{{ qual }}</a>
+        <a href='?{% set_get "page" qual=qual.qualifier %}'>{{ qual }}</a>
     {% endif %}
 {% endfor %}
 </p>
     {% endif %}
 {% endfor %}
 </p>
@@ -70,7 +71,7 @@
 <p>
 {% trans "By language" %}:
 {% if language %}
 <p>
 {% trans "By language" %}:
 {% if language %}
-    <a href='?'>{% trans "all" %}</a>
+    <a href='?{% set_get "page" "lang" %}'>{% trans "all" %}</a>
 {% else %}
     <strong>{% trans "all" %}</strong>
 {% endif %}
 {% else %}
     <strong>{% trans "all" %}</strong>
 {% endif %}
@@ -80,7 +81,7 @@
     {% if lang == language %}
         <strong>{{ lang }}</strong>
     {% else %}
     {% if lang == language %}
         <strong>{{ lang }}</strong>
     {% else %}
-        <a href='?lang={{ lang }}'>{{ lang }}</a>
+        <a href='?{% set_get "page" lang=lang %}'>{{ lang }}</a>
     {% endif %}
 {% endfor %}
 </p>
     {% endif %}
 {% endfor %}
 </p>
diff --git a/apps/dictionary/templatetags/__init__.py b/apps/dictionary/templatetags/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/dictionary/templatetags/set_get.py b/apps/dictionary/templatetags/set_get.py
new file mode 100644 (file)
index 0000000..9afe3e8
--- /dev/null
@@ -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)
index 526819a..0c83cf1 100755 (executable)
@@ -24,7 +24,7 @@ class DictionaryTests(WLTestCase):
         <opowiadanie>
             <akap><pe><slowo_obce>rose</slowo_obce> --- kind of a flower.</pe></akap>
             <akap><pe><slowo_obce>rose</slowo_obce> --- kind of a flower.</pe></akap>
         <opowiadanie>
             <akap><pe><slowo_obce>rose</slowo_obce> --- kind of a flower.</pe></akap>
             <akap><pe><slowo_obce>rose</slowo_obce> --- kind of a flower.</pe></akap>
-            <akap><pe><slowo_obce>rose</slowo_obce> (color) --- #FF007F.</pe></akap>
+            <akap><pe><slowo_obce>rose</slowo_obce> (techn.) --- #FF007F.</pe></akap>
         </opowiadanie></utwor>
         """
 
         </opowiadanie></utwor>
         """
 
@@ -33,20 +33,20 @@ class DictionaryTests(WLTestCase):
         self.assertEqual(
             len(self.client.get('/przypisy/').context['object_list']),
             2,
         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(
 
         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,
             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(
 
         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.')
 
 
index fa9712d..42aab6c 100755 (executable)
@@ -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.
 #
 # 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.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):
 
 
 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')
         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')
         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]")
         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)
         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)
 
     def get_context_data(self, **kwargs):
         context = super(NotesView, self).get_context_data(**kwargs)
index a04f11b..a3be479 160000 (submodule)
@@ -1 +1 @@
-Subproject commit a04f11baee3eb7d090867c2d5639a120ec3217b8
+Subproject commit a3be479506edf42dc58feb22b26e4f5da1e49edd