from catalogue.constants import LANGUAGES_3TO2
from catalogue.utils import remove_zip, truncate_html_words
from celery.task import Task, task
+from celery.utils.log import get_task_logger
from waiter.utils import clear_cache
+task_logger = get_task_logger(__name__)
+
class EbookFieldFile(FieldFile):
"""Represents contents of an ebook file field."""
def run(self, obj, field_name):
"""Just run `build` on FieldFile, can't pass it directly to Celery."""
+ task_logger.info("%s -> %s" % (obj.slug, field_name))
ret = self.build(getattr(obj, field_name))
obj.flush_includes()
return ret
from datetime import datetime
from traceback import print_exc
from celery.task import task
+from celery.utils.log import get_task_logger
from django.conf import settings
from wolnelektury.utils import localtime_to_utc
from waiter.models import WaitedFile
+task_logger = get_task_logger(__name__)
+
# TODO: move to model?
def touch_tag(tag):
from django.core.files.storage import DefaultStorage
from catalogue.models import Book
- print "will gen %s" % DefaultStorage().path(file_name)
+ task_logger.info(DefaultStorage().path(file_name))
if not DefaultStorage().exists(file_name):
kwargs = {
'cover': True,
--- /dev/null
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('catalogue', '0004_remove_booktags_count_related_info'),
+ ('dictionary', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='NoteSource',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('anchor', models.CharField(max_length=64)),
+ ('book', models.ForeignKey(to='catalogue.Book')),
+ ('note', models.ForeignKey(to='dictionary.Note')),
+ ],
+ options={
+ 'ordering': ['book'],
+ },
+ bases=(models.Model,),
+ ),
+ migrations.RemoveField(
+ model_name='note',
+ name='anchor',
+ ),
+ migrations.RemoveField(
+ model_name='note',
+ name='book',
+ ),
+ migrations.AddField(
+ model_name='note',
+ name='fn_type',
+ field=models.CharField(default='', max_length=10, db_index=True),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='note',
+ name='language',
+ field=models.CharField(default='', max_length=10, db_index=True),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='note',
+ name='qualifier',
+ field=models.CharField(default='', max_length=128, db_index=True, blank=True),
+ preserve_default=False,
+ ),
+ ]
# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
-from django.db import models
+from django.db import models, transaction
from celery.task import task
from sortify import sortify
+from celery.utils.log import get_task_logger
+
+task_logger = get_task_logger(__name__)
from catalogue.models import Book
class Note(models.Model):
"""Represents a single annotation from a book."""
- book = models.ForeignKey(Book)
- anchor = models.CharField(max_length=64)
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)
+ language = models.CharField(max_length=10, db_index=True)
class Meta:
ordering = ['sort_key']
+class NoteSource(models.Model):
+ """Represents a single annotation from a book."""
+ note = models.ForeignKey(Note)
+ book = models.ForeignKey(Book)
+ anchor = models.CharField(max_length=64)
+
+ class Meta:
+ ordering = ['book']
+
+
@task(ignore_result=True)
def build_notes(book):
- Note.objects.filter(book=book).delete()
- if book.html_file:
- from librarian import html
- for anchor, text_str, html_str in html.extract_annotations(book.html_file.path):
- Note.objects.create(book=book, anchor=anchor,
- html=html_str,
- sort_key=sortify(text_str).strip()[:128])
+ task_logger.info(book.slug)
+ with transaction.atomic():
+ book.notesource_set.all().delete()
+ if book.html_file:
+ from librarian import html
+ for anchor, fn_type, qualifier, 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,
+ 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
+ )
+ note.notesource_set.create(book=book, anchor=anchor)
+
+ Note.objects.filter(notesource=None).delete()
+
def notes_from_book(sender, instance, **kwargs):
build_notes.delay(instance)
<p>
{% trans "By first letter" %}:
{% if letter %}
- <a href='{% url "dictionary_notes" %}'>{% trans "all" %}</a>
+ <a href='?'>{% trans "all" %}</a>
{% else %}
<strong>{% trans "all" %}</strong>
{% endif %}
{% if let == letter %}
<strong>{{ let|upper }}</strong>
{% else %}
- <a href='{% url "dictionary_notes" let %}'>{{ let|upper }}</a>
+ <a href='?ltr={{ let }}'>{{ let|upper }}</a>
{% endif %}
{% endfor %}
</p>
+
+<p>
+{% trans "By type" %}:
+{% if fn_type %}
+ <a href='?'>{% trans "all" %}</a>
+{% else %}
+ <strong>{% trans "all" %}</strong>
+{% endif %}
+
+{% for fnt in fn_types %}
+ |
+ {% if fnt == fn_type %}
+ <strong>{{ fnt }}</strong>
+ {% else %}
+ <a href='?type={{ fnt }}'>{{ fnt }}</a>
+ {% endif %}
+{% endfor %}
+</p>
+
+
+<p>
+{% trans "By qualifier" %}:
+{% if qualifier %}
+ <a href='?'>{% trans "all" %}</a>
+{% else %}
+ <strong>{% trans "all" %}</strong>
+{% endif %}
+
+{% for qual in qualifiers %}
+ |
+ {% if qual == qualifier %}
+ <strong>{{ qual }}</strong>
+ {% else %}
+ <a href='?qual={{ qual|urlencode }}'>{{ qual }}</a>
+ {% endif %}
+{% endfor %}
+</p>
+
+<p>
+{% trans "By language" %}:
+{% if language %}
+ <a href='?'>{% trans "all" %}</a>
+{% else %}
+ <strong>{% trans "all" %}</strong>
+{% endif %}
+
+{% for lang in languages %}
+ |
+ {% if lang == language %}
+ <strong>{{ lang }}</strong>
+ {% else %}
+ <a href='?lang={{ lang }}'>{{ lang }}</a>
+ {% endif %}
+{% endfor %}
+</p>
+
<hr/>
<p>
{% paginate %}
{% for obj in object_list %}
<div class='dictionary-note'>
- {{ obj.html|safe }}
- <div class='dictionary-note-source'>
- (<a href='{% url "book_text" obj.book.slug %}#{{ obj.anchor }}'>{{ obj.book.pretty_title }}</a>)
- </div>
+ {{ obj.html|safe }}
+ {% 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>)
+ </div>
+ {% endfor %}
</div>
{% endfor %}
{% paginate %}
BOOK_TEXT = """<utwor>
<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>
</opowiadanie></utwor>
"""
self.assertEqual(
len(self.client.get('/przypisy/').context['object_list']),
- 1,
+ 2,
'There should be a note on the note list.')
self.assertEqual(
- len(self.client.get('/przypisy/a/').context['object_list']),
+ len(self.client.get('/przypisy/?ltr=r').context['object_list']),
0,
'There should not be a note for the letter A.')
self.assertEqual(
- len(self.client.get('/przypisy/r/').context['object_list']),
- 1,
+ len(self.client.get('/przypisy/?ltr=r').context['object_list']),
+ 2,
+ 'There should be a note for 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.')
urlpatterns = patterns('dictionary.views',
url(r'^$', NotesView.as_view(), name='dictionary_notes'),
- url(r'(?P<letter>[a-z]|0-9)/$', NotesView.as_view(), name='dictionary_notes'),
)
#
from dictionary.models import Note
from django.views.generic.list import ListView
+from django.db.models import Count
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.kwargs.get('letter')
+ self.letter = self.request.GET.get('ltr')
+
+ self.qualifiers = Note.objects.order_by('qualifier').filter(qualifier__startswith='f').values_list(
+ 'qualifier', flat=True).distinct()
+ self.qualifier = self.request.GET.get('qual')
+
+ self.languages = Note.objects.order_by('language').values_list(
+ 'language', flat=True).distinct()
+ self.language = self.request.GET.get('lang')
+
+ self.fn_types = Note.objects.order_by('fn_type').values_list(
+ 'fn_type', flat=True).distinct()
+ self.fn_type = self.request.GET.get('type')
objects = Note.objects.select_related('book').all()
+
if self.letter == "0-9":
objects = objects.filter(sort_key__regex=r"^[0-9]")
elif self.letter:
objects = objects.filter(sort_key__startswith=self.letter)
+
+ if self.qualifier:
+ objects = objects.filter(qualifier=self.qualifier)
+
+ if self.language:
+ objects = objects.filter(language=self.language)
+
+ if self.fn_type:
+ objects = objects.filter(fn_type=self.fn_type)
+
return objects
+ # TODO: wewn. wyszukiwarka, czy wg definiendum?
+ # TODO: filtr języka
+
def get_context_data(self, **kwargs):
context = super(NotesView, self).get_context_data(**kwargs)
- context['letter'] = self.letter
context['letters'] = self.letters
+ context['letter'] = self.letter
+ context['qualifiers'] = self.qualifiers
+ context['qualifier'] = self.qualifier
+ context['languages'] = self.languages
+ context['language'] = self.language
+ context['fn_types'] = self.fn_types
+ context['fn_type'] = self.fn_type
return context
--- /dev/null
+.dictionary-note {
+ @include white-box;
+ @include size(padding, 8px);
+ @include size(margin, 10px 0);
+}
+.dictionary-note-source {
+ @include white-box;
+ @include size(padding, 8px);
+}
@import "main/search";
@import "main/tag";
+@import "dictionary/dictionary";
+
@import "funding/funding";
@import "polls/polls";
-Subproject commit a91e41e489588ebad550cd9e22d157062effa2ff
+Subproject commit a3b6840527ec52ce8b6d74819633d8c85e3973ba