-57ed361b5c5cea4c3fc6d4ea1d8c2a9addb087e2 wolnelektury-0.9
-5f1b5a4c1444518b6b4d151a9a20baf061b77179 wolnelektury-0.9
-4da7f67e38ed4599d3925c92a7fab8f61467bfc3 wolnelektury-0.9
-fed64f1e09c1859829839fe8c09dc1c2848389a4 wolnelektury-0.9.1
-5eee4534e79b9102c6e7c579cc2a6c8d74de9c05 wolnelektury-0.9.2
-293052c8f613021de21e6d036e48652f4a414b35 wolnelektury-0.9.3
-13036b75ecc3711e9270342fc8d7cd41d2676d6b wolnelektury-0.9.4
+fa4bcbf759901d7444c98e3f0a3a9aa7da1b8a1b wolnelektury-1.0
# -*- coding: utf-8 -*-
+import datetime
+
+from django.conf import settings
+from django.db import models
+from django.db.models import signals
from django import forms
from django.forms.widgets import flatatt
from django.forms.util import smart_unicode
+from django.utils import simplejson as json
from django.utils.html import escape
from django.utils.safestring import mark_safe
-from django.utils.simplejson import dumps
+
+
+class JSONEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, datetime.datetime):
+ return obj.strftime('%Y-%m-%d %H:%M:%S')
+ elif isinstance(obj, datetime.date):
+ return obj.strftime('%Y-%m-%d')
+ elif isinstance(obj, datetime.time):
+ return obj.strftime('%H:%M:%S')
+ return json.JSONEncoder.default(self, obj)
+
+
+def dumps(data):
+ return JSONEncoder().encode(data)
+
+
+def loads(str):
+ return json.loads(str, encoding=settings.DEFAULT_CHARSET)
+
+
+class JSONField(models.TextField):
+ def db_type(self):
+ return 'text'
+
+ def get_internal_type(self):
+ return 'TextField'
+
+ def pre_save(self, model_instance, add):
+ value = getattr(model_instance, self.attname, None)
+ return dumps(value)
+
+ def contribute_to_class(self, cls, name):
+ super(JSONField, self).contribute_to_class(cls, name)
+ signals.post_init.connect(self.post_init, sender=cls)
+
+ def get_json(model_instance):
+ return dumps(getattr(model_instance, self.attname, None))
+ setattr(cls, 'get_%s_json' % self.name, get_json)
+
+ def set_json(model_instance, json):
+ return setattr(model_instance, self.attname, loads(json))
+ setattr(cls, 'set_%s_json' % self.name, set_json)
+
+ def post_init(self, **kwargs):
+ instance = kwargs.get('instance', None)
+ value = self.value_from_object(instance)
+ if (value):
+ setattr(instance, self.attname, loads(value))
+ else:
+ setattr(instance, self.attname, None)
class JQueryAutoCompleteWidget(forms.TextInput):
final_attrs['id'] = 'id_%s' % name
html = u'''<input type="text" %(attrs)s/>
- <script type="text/javascript"><!--//
+ <script type="text/javascript">//<!--
%(js)s//--></script>
''' % {
'attrs' : flatatt(final_attrs),
--- /dev/null
+from south.db import db
+from catalogue.fields import JSONField
+
+class Migration:
+
+ def forwards(self):
+ db.add_column('catalogue_book', 'extra_info', JSONField())
+
+ def backwards(self):
+ db.delete_column('catalogue_book', 'extra_info')
+
from newtagging.models import TagBase
from newtagging import managers
+from catalogue.fields import JSONField
from librarian import html, dcparser
('genre', _('genre')),
('theme', _('theme')),
('set', _('set')),
+ ('book', _('book')),
)
db_index=True, choices=TAG_CATEGORIES)
description = models.TextField(_('description'), blank=True)
main_page = models.BooleanField(_('main page'), default=False, db_index=True, help_text=_('Show tag on main page'))
-
+
user = models.ForeignKey(User, blank=True, null=True)
book_count = models.IntegerField(_('book count'), default=0, blank=False, null=False)
created_at = models.DateTimeField(_('creation date'), auto_now=True)
_short_html = models.TextField(_('short HTML'), editable=False)
parent_number = models.IntegerField(_('parent number'), default=0)
+ extra_info = JSONField(_('extra information'))
# Formats
xml_file = models.FileField(_('XML file'), upload_to=book_upload_path('xml'), blank=True)
objects = models.Manager()
tagged = managers.ModelTaggedItemManager(Tag)
tags = managers.TagDescriptor(Tag)
+
@property
def name(self):
if len(self._short_html):
return mark_safe(self._short_html)
else:
- tags = self.tags.filter(~Q(category__in=('set', 'theme')))
+ tags = self.tags.filter(~Q(category__in=('set', 'theme', 'book')))
tags = [u'<a href="%s">%s</a>' % (tag.get_absolute_url(), tag.name) for tag in tags]
formats = []
book_shelves = list(book.tags.filter(category='set'))
book.title = book_info.title
+ book.extra_info = book_info.to_dict()
book._short_html = ''
book.save()
tag.category = category
tag.save()
book_tags.append(tag)
+
+ book_tag, created = Tag.objects.get_or_create(slug=('l-' + book.slug)[:120])
+ if created:
+ book_tag.name = book.title[:50]
+ book_tag.sort_key = ('l-' + book.slug)[:120]
+ book_tag.category = 'book'
+ book_tag.save()
+ book_tags.append(book_tag)
+
book.tags = book_tags
if hasattr(book_info, 'parts'):
child_book.parent = book
child_book.parent_number = n
child_book.save()
-
+
+ book_descendants = list(book.children.all())
+ while len(book_descendants) > 0:
+ child_book = book_descendants.pop(0)
+ for fragment in child_book.fragments.all():
+ fragment.tags = set(list(fragment.tags) + [book_tag])
+ book_descendants += list(child_book.children.all())
+
# Save XML and HTML files
book.xml_file.save('%s.xml' % book.slug, File(file(xml_file)), save=False)
tag.save()
themes.append(tag)
new_fragment.save()
- new_fragment.tags = list(book.tags) + themes
+ new_fragment.tags = set(list(book.tags) + themes + [book_tag])
book_themes += themes
book_themes = set(book_themes)
# Public interface. Do not change this URLs.
url(r'^lektura/(?P<slug>[a-zA-Z0-9-]+)\.html$', 'book_text', name='book_text'),
url(r'^lektura/(?P<slug>[a-zA-Z0-9-]+)/$', 'book_detail', name='book_detail'),
+ url(r'^lektura/(?P<book_slug>[a-zA-Z0-9-]+)/motyw/(?P<theme_slug>[a-zA-Z0-9-]+)/$',
+ 'book_fragments', name='book_fragments'),
url(r'^(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', name='tagged_object_list'),
)
except models.Tag.DoesNotExist:
raise Http404
+ if len([tag for tag in tags if tag.category == 'book']):
+ raise Http404
+
model = models.Book
shelf = [tag for tag in tags if tag.category == 'set']
shelf_is_set = (len(tags) == 1 and tags[0].category == 'set')
user_is_owner = (len(shelf) and request.user.is_authenticated() and request.user == shelf[0].user)
- extra_where = 'NOT catalogue_tag.category = "set"'
+ extra_where = 'catalogue_tag.category NOT IN ("set", "book")'
related_tags = models.Tag.objects.related_for_model(tags, model, counts=True, extra={'where': [extra_where]})
categories = split_tags(related_tags)
)
+def book_fragments(request, book_slug, theme_slug):
+ book = get_object_or_404(models.Book, slug=book_slug)
+ book_tag = get_object_or_404(models.Tag, slug='l-' + book_slug)
+ theme = get_object_or_404(models.Tag, slug=theme_slug)
+ fragments = models.Fragment.tagged.with_all([book_tag, theme])
+
+ form = forms.SearchForm()
+ return render_to_response('catalogue/book_fragments.html', locals(),
+ context_instance=RequestContext(request))
+
+
def book_detail(request, slug):
book = get_object_or_404(models.Book, slug=slug)
+ book_tag = get_object_or_404(models.Tag, slug = 'l-' + slug)
tags = list(book.tags.filter(~Q(category='set')))
categories = split_tags(tags)
book_children = book.children.all().order_by('parent_number')
+ extra_where = 'catalogue_tag.category = "theme"'
+ book_themes = models.Tag.objects.related_for_model(book_tag, models.Fragment, counts=True, extra={'where': [extra_where]})
form = forms.SearchForm()
return render_to_response('catalogue/book_detail.html', locals(),
books = models.Book.objects.filter(title__icontains=prefix)
tags = models.Tag.objects.filter(name__icontains=prefix)
if user.is_authenticated():
- tags = tags.filter(~Q(category='set') | Q(user=user))
+ tags = tags.filter(~Q(category='book') & (~Q(category='set') | Q(user=user)))
else:
- tags = tags.filter(~Q(category='set'))
+ tags = tags.filter(~Q(category='book') & ~Q(category='set'))
return list(books) + list(tags)
def tags_starting_with(request):
- prefix = request.GET['q']
+ prefix = request.GET.get('q', '')
# Prefix must have at least 2 characters
if len(prefix) < 2:
return HttpResponse('')
<utwor>
+
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <rdf:Description rdf:about="http://wiki.wolnepodreczniki.pl/Lektury:Zapolska/Moralność_pani_Dulskiej">
+ <dc:creator xml:lang="pl">Zapolska, Gabriela</dc:creator>
+ <dc:title xml:lang="pl">Moralność pani Dulskiej</dc:title>
+ <dc:contributor.editor xml:lang="pl">Sekuła, Aleksandra</dc:contributor.editor>
+ <dc:contributor.technical_editor xml:lang="pl">Sutkowska, Olga</dc:contributor.technical_editor>
+ <dc:publisher xml:lang="pl">Fundacja Nowoczesna Polska</dc:publisher>
+ <dc:subject.period xml:lang="pl">Modernizm</dc:subject.period>
+ <dc:subject.type xml:lang="pl">Dramat</dc:subject.type>
+ <dc:subject.genre xml:lang="pl">Tragifarsa</dc:subject.genre>
+ <dc:subject.genre xml:lang="pl">Komedia</dc:subject.genre>
+ <dc:description xml:lang="pl">Publikacja zrealizowana w ramach projektu Wolne Lektury (http://wolnelektury.pl). Reprodukcja cyfrowa wykonana przez Bibliotekę Narodową z egzemplarza pochodzącego ze zbiorów BN.</dc:description>
+ <dc:identifier.url xml:lang="pl">http://wolnelektury.pl/katalog/lektura/moralnosc-pani-dulskiej</dc:identifier.url>
+ <dc:source.URL xml:lang="pl">http://www.polona.pl/dlibra/doccontent2?id=582&from=&from=generalsearch&dirids=1&lang=pl</dc:source.URL>
+ <dc:source xml:lang="pl">Zapolska, Gabriela (1857-1921), Moralność pani Dulskiej: komedya w trzech aktach, Tow. Akc. S. Orgelbranda S-ów, Warszawa, 1907</dc:source>
+ <dc:rights xml:lang="pl">Domena publiczna - Gabriela Zapolska, herbu Korwin, właśc. Maria Gabriela Korwin-Piotro, zm. 17 grudnia 1921</dc:rights>
+ <dc:date.pd xml:lang="pl">1921</dc:date.pd>
+ <dc:format xml:lang="pl">xml</dc:format>
+ <dc:type xml:lang="pl">text</dc:type>
+ <dc:type xml:lang="en">text</dc:type>
+ <dc:date xml:lang="pl">2007-11-10</dc:date>
+ <dc:audience xml:lang="pl">G</dc:audience>
+ <dc:audience xml:lang="pl">L</dc:audience>
+ <dc:language xml:lang="pl">pol</dc:language>
+ </rdf:Description>
+ </rdf:RDF>
+
<dramat_wspolczesny>
<xsl:template match="utwor">
- <html>
+ <!-- <html>
<head>
<title>Książka z serwisu WolneLektury.pl</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
</head>
<style>
body {
- font-size: 14px;
+ font-size: 16px;
font: Georgia, "Times New Roman", serif;
line-height: 1.5em;
- margin: 3em;
- max-width: 36em;
+ margin: 0;
}
a {
text-decoration: none;
}
+ #book-text {
+ margin: 3em;
+ max-width: 36em;
+ }
+
+ /* ================================== */
+ /* = Header with logo and menu = */
+ /* ================================== */
+ #header {
+ margin: 3.4em 0 0 1.4em;
+ }
+
+ img {
+ border: none;
+ }
+
+
+ #menu {
+ position: fixed;
+ left: 0em;
+ top: 0em;
+ width: 100%;
+ height: 1.5em;
+ background: #333;
+ color: #FFF;
+ opacity: 0.9;
+ }
+
+ #menu ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ }
+
+ #menu li a {
+ display: block;
+ float: left;
+ width: 7.5em;
+ height: 1.5em;
+ margin-left: 0.5em;
+ text-align: center;
+ color: #FFF;
+ }
+
+ #menu li a:hover, #menu li a:active {
+ color: #000;
+ background: #FFF url(/media/img/arrow-down.png) no-repeat center right;
+ }
+
+ #menu li a.selected {
+ color: #000;
+ background: #FFF url(/media/img/arrow-up.png) no-repeat center right;
+ }
+
+ #toc, #themes {
+ position: fixed;
+ left: 0em;
+ top: 1.5em;
+ width: 37em;
+ padding: 1.5em;
+ background: #FFF;
+ border-bottom: 0.25em solid #DDD;
+ border-right: 0.25em solid #DDD;
+ display: none;
+ height: 16em;
+ overflow-x: hidden;
+ overflow-y: auto;
+ opacity: 0.9;
+ }
+
+ #toc ol, #themes ol {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ }
+
+ #toc ol li {
+ font-weight: bold;
+ }
+
+ #toc ol ol {
+ padding: 0 0 1.5em 1.5em;
+ margin: 0;
+ }
+
+ #toc ol ol li {
+ font-weight: normal;
+ }
+
+ #toc h2 {
+ display: none;
+ }
+
+ #toc .anchor {
+ float: none;
+ margin: 0;
+ color: blue;
+ font-size: 16px;
+ position: inherit;
+ }
+
/* =================================================== */
/* = Common elements: headings, paragraphs and lines = */
/* =================================================== */
font-size: 0.875em;
}
+ /* ============= */
+ /* = Numbering = */
+ /* ============= */
+ .anchor {
+ position: absolute;
+ margin: -0.25em -0.5em;
+ left: 1em;
+ color: #777;
+ font-size: 12px;
+ width: 2em;
+ text-align: center;
+ padding: 0.25em 0.5em;
+ line-height: 1.5em;
+ }
+
+ .anchor:hover, #book-text .anchor:active {
+ color: #FFF;
+ background-color: #CCC;
+ }
+
/* =================== */
/* = Custom elements = */
/* =================== */
div.didaskalia {
font-style: italic;
- margin: 0.5em 0 0;
+ margin: 0.5em 0 0 1.5em;
}
div.kwestia {
p.motto_podpis {
font-size: 0.875em;
+ text-align: right;
}
div.fragment {
padding-bottom: 1.5em;
}
- div.note p, div.note p.paragraph {
+ div.note p, div.dedication p, div.note p.paragraph, div.dedication p.paragraph {
text-align: right;
font-style: italic;
}
visibility: hidden;
}
- .anchor {
- position: absolute;
- margin: -0.25em -0.5em;
- left: 1em;
- color: #777;
- font-size: 12px;
- width: 2em;
+ hr.spacer-line {
+ margin: 1.5em 0;
+ border: none;
+ border-bottom: 0.1em solid #000;
+ }
+
+ p.spacer-asterisk {
+ padding: 0;
+ margin: 1.5em 0;
text-align: center;
- padding: 0.25em 0.5em;
- line-height: 1.5em;
}
- .anchor:hover, #book-text .anchor:active {
- color: #FFF;
- background-color: #CCC;
+ div.person-list ol {
+ list-style: none;
+ padding: 0 0 0 1.5em;
}
-
- #toc {
- display: none;
+
+ p.place-and-time {
+ font-style: italic;
+ }
+
+ em.math, em.foreign-word, em.book-title, em.didaskalia {
+ font-style: italic;
+ }
+
+ em.author-emphasis {
+ letter-spacing: 0.1em;
+ }
+
+ em.person {
+ font-style: normal;
+ font-variant: small-caps;
}
</style>
- <body>
+ <body> -->
<div id="book-text">
<xsl:apply-templates select="powiesc|opowiadanie|liryka_l|liryka_lp|dramat_wierszowany_l|dramat_wierszowany_lp|dramat_wspolczesny" />
<xsl:if test="count(descendant::*[self::pe or self::pa or self::pr or self::pt][not(parent::extra)])">
</div>
</xsl:if>
</div>
- </body>
- </html>
+ <!-- </body>
+ </html> -->
</xsl:template>
raise ParseError(e)
description = tree.find('//' + book_info.RDF('Description'))
+ book_info.wiki_url = description.get(cls.RDF('about'), None)
+
if description is None:
raise ParseError('no Description tag found in document')
root = etree.Element(self.RDF('RDF'))
description = etree.SubElement(root, self.RDF('Description'))
+ if self.wiki_url:
+ description.set(self.RDF('about'), self.wiki_url)
+
for tag, (attribute, converter) in self.mapping.iteritems():
if hasattr(self, attribute):
e = etree.Element(tag)
return unicode(etree.tostring(root, 'utf-8'), 'utf-8')
+ def to_dict(self):
+ etree._namespace_map[str(self.RDF)] = 'rdf'
+ etree._namespace_map[str(self.DC)] = 'dc'
+
+ result = {'about': self.wiki_url}
+ for tag, (attribute, converter) in self.mapping.iteritems():
+ if hasattr(self, attribute):
+ result[attribute] = unicode(getattr(self, attribute))
+
+ return result
+
def parse(file_name):
return BookInfo.from_file(file_name)
doc = etree.parse(doc_file, parser)
result = doc.xslt(style)
- if result.find('//h1') is not None:
+ if result.find('//p') is not None:
add_anchors(result.getroot())
add_table_of_contents(result.getroot())
result.write(output_filename, xml_declaration=False, pretty_print=True, encoding='utf-8')
+++ /dev/null
-from django.core.management import setup_environ
-import settings
-
-setup_environ(settings)
-
-from catalogue.models import Tag, Book
-from django.db import connection
-
-query = 'ALTER TABLE catalogue_tag ADD COLUMN book_count integer NOT NULL DEFAULT 0'
-
-cursor = connection.cursor()
-cursor.execute(query)
-
-for shelf in Tag.objects.filter(category='set'):
- shelf.book_count = len(Book.tagged.with_all(shelf))
- shelf.save()
#!/usr/bin/env python
+import os
from os.path import abspath, dirname, join
import sys
<p><img src="/media/img/indicator.gif" alt="*"/> Ładowanie</p>
</div>
</div>
- <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
- </script>
- <script type="text/javascript">
- _uacct = "UA-2576694-1";
- urchinTracker();
- </script>
+ <script type="text/javascript">
+ var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+ document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+ </script>
+ <script type="text/javascript">
+ var pageTracker = _gat._getTracker("UA-2576694-1");
+ pageTracker._trackPageview();
+ </script>
</body>
</html>
</form>
<div id="books-list">
+ <p>Na podstawie: {{ book.extra_info.source_name }}</p>
{% if book.has_description %}
<div id="description">
{{ book.description|safe }}
<a href="{{ tag.get_absolute_url }}">{{ tag }}</a>
{% endfor %}
</li>
+ <li><a href="{{ book.extra_info.about }}">Lektura na wiki projektu</a></li>
+ <li><a href="{{ book.extra_info.source_url }}">Lektura w CBN Polona</a></li>
+ <li><a href="{{ book.xml_file.url }}">Kod źródłowy utworu (XML)</a></li>
</ul>
</div>
<div id="themes-list">
- {% if categories.theme %}
- <h2>Motywy w utworze</h2>
- <ul>
- {% for theme in categories.theme %}
- <li><a href="{{ theme.get_absolute_url }}">{{ theme }}</a></li>
- {% endfor %}
- </ul>
- {% endif %}
+ <h2>Motywy w utworze</h2>
+ <ul>
+ {% for theme in book_themes %}
+ <li><a href="{% url book_fragments book.slug,theme.slug %}">{{ theme }} ({{ theme.count }})</a></li>
+ {% endfor %}
+ </ul>
</div>
<div class="clearboth"></div>
</div>
<p><img src="/media/img/indicator.gif" alt="*"/> Ładowanie</p>
</div>
</div>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
--- /dev/null
+{% extends "base.html" %}
+{% load catalogue_tags pagination_tags %}
+
+{% block title %}Motyw {{ theme }} w utworze {{ book }} w WolneLektury.pl{% endblock %}
+
+{% block bodyid %}tagged-object-list{% endblock %}
+
+{% block body %}
+ <h1>Motyw {{ theme }} w utworze {{ book }}</h1>
+ <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
+ <p>{{ form.q }} <input type="submit" value="Szukaj" /> <strong>lub</strong> <a href="{{ book.get_absolute_url }}">wróć do strony utworu</a></p>
+ </form>
+
+ {% autopaginate fragments 10 %}
+ <div id="books-list">
+ <ol>
+ {% for fragment in fragments %}
+ <li>{{ fragment.short_html }}</li>
+ {% endfor %}
+ </ol>
+ {% paginate %}
+ </div>
+ <div id="tags-list">
+ <div id="categories-list">
+ Zobacz opis <a href="{{ book.get_absolute_url }}">utworu {{ book }}</a>
+ </div>
+ <div id="themes-list">
+ </div>
+ <div class="clearboth"></div>
+ </div>
+ <div id="set-window">
+ <div class="header"><a href="#" class="jqmClose">Zamknij</a></div>
+ <div class="target">
+ <p><img src="/media/img/indicator.gif" alt="*"/> Ładowanie</p>
+ </div>
+ </div>
+{% endblock %}
\ No newline at end of file
</ol>
</div>
{{ book.html_file.read|safe }}
- <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
- </script>
- <script type="text/javascript">
- _uacct = "UA-2576694-1";
- urchinTracker();
- </script>
+ <script type="text/javascript">
+ var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+ document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+ </script>
+ <script type="text/javascript">
+ var pageTracker = _gat._getTracker("UA-2576694-1");
+ pageTracker._trackPageview();
+ </script>
</body>
</html>