#
from functools import wraps
-from django.http import (HttpResponse, HttpResponseRedirect,
- HttpResponseForbidden)
+from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.utils.encoding import force_unicode
if request.is_ajax():
return HttpResponseForbidden('Not logged in')
else:
- return HttpResponseRedirect('/uzytkownicy/zaloguj')# next?=request.build_full_path())
+ return HttpResponseRedirect('/uzytkownicy/zaloguj') # next?=request.build_full_path())
def placeholdized(form):
if request.is_ajax():
return HttpResponse(LazyEncoder(ensure_ascii=False).encode(response_data))
else:
- if (self.POST_login and not request.user.is_authenticated()
- and not request.is_ajax()):
+ if self.POST_login and not request.user.is_authenticated() and not request.is_ajax():
return require_login(request)
form = self.form_class(*form_args, **form_kwargs)
"view_kwargs": kwargs,
}
context.update(self.extra_context(request, obj))
- return render_to_response(template, context,
- context_instance=RequestContext(request))
+ return render_to_response(template, context, context_instance=RequestContext(request))
def redirect_or_refresh(self, request, path, message=None):
"""If the form is AJAX, refresh the page. If not, go to `path`."""
def get_ssis(self, emitter_format):
"""Yields SSI include statements for the queryset."""
- url_pattern = reverse('api_include',
- kwargs={'model': self.queryset.model.__name__.lower(),
- 'pk': '0000',
- 'emitter_format': emitter_format,
- 'lang': get_language(),
- })
+ url_pattern = reverse(
+ 'api_include',
+ kwargs={
+ 'model': self.queryset.model.__name__.lower(),
+ 'pk': '0000',
+ 'emitter_format': emitter_format,
+ 'lang': get_language(),
+ })
for instance in self.queryset:
- yield "<!--#include file='%s'-->" % url_pattern.replace('0000',
- str(instance.pk))
+ yield "<!--#include file='%s'-->" % url_pattern.replace('0000', str(instance.pk))
class SsiEmitterMixin(object):
def construct(self):
- if isinstance(self.data, QuerySet) and self.data.model in (Book,
- Fragment, Tag):
+ if isinstance(self.data, QuerySet) and self.data.model in (Book, Fragment, Tag):
return SsiQS(self.data)
else:
- return super(SsiEmitterMixin, self).construct()
+ return super(SsiEmitterMixin, self).construct() # WTF
class SsiJsonEmitter(SsiEmitterMixin, JSONEmitter):
'</resource><resource>'.join(self.construct().get_ssis('xml'))
Emitter.register('xml', SsiXmlEmitter, 'text/xml; charset=utf-8')
-
from stats.utils import piwik_track
-from . import emitters # Register our emitters
+from . import emitters # Register our emitters
API_BASE = WL_BASE = MEDIA_BASE = lazy(
lambda: u'http://' + Site.objects.get_current().domain, unicode)()
book_tag_categories = ['author', 'epoch', 'kind', 'genre']
-
def read_tags(tags, allowed):
""" Reads a path of filtering tags.
except KeyError:
raise ValueError('Unknown category.')
- if not category in allowed:
+ if category not in allowed:
raise ValueError('Category not allowed.')
if category == 'book':
book.cover, "139x193").url if book.cover else ''
-
class BookDetailHandler(BaseHandler, BookDetails):
""" Main handler for Book objects.
return book.tags.filter(category='genre')
@piwik_track
- def read(self, request, tags=None, top_level=False,
- audiobooks=False, daisy=False, pk=None):
+ def read(self, request, tags=None, top_level=False, audiobooks=False, daisy=False, pk=None):
""" Lists all books with given tags.
:param tags: filtering tags; should be a path of categories
def get_tags(cls, book):
return book.tags.filter(category=category)
return get_tags
+
+
def _tag_getter(category):
@classmethod
def get_tag(cls, book):
return ', '.join(tag.name for tag in book.tags.filter(category=category))
return get_tag
-for plural, singular in category_singular.items():
- setattr(BookDetails, plural, _tags_getter(singular))
- setattr(BookDetails, singular, _tag_getter(singular))
+
+
+def add_tag_getters():
+ for plural, singular in category_singular.items():
+ setattr(BookDetails, plural, _tags_getter(singular))
+ setattr(BookDetails, singular, _tag_getter(singular))
+
+add_tag_getters()
+
# add fields for files in Book
-def _file_getter(format):
- field = "%s_file" % format
+def _file_getter(book_format):
+ field = "%s_file" % book_format
+
@classmethod
def get_file(cls, book):
f = getattr(book, field)
else:
return ''
return get_file
-for format in Book.formats:
- setattr(BookDetails, format, _file_getter(format))
+
+
+def add_file_getters():
+ for book_format in Book.formats:
+ setattr(BookDetails, book_format, _file_getter(book_format))
+
+add_file_getters()
class CollectionDetails(object):
return Book.objects.filter(collection.get_query())
-
class CollectionDetailHandler(BaseHandler, CollectionDetails):
allowed_methods = ('GET',)
fields = ['url', 'title', 'description', 'books']
try:
category_sng = category_singular[category]
- except KeyError, e:
+ except KeyError:
return rc.NOT_FOUND
try:
try:
category_sng = category_singular[category]
- except KeyError, e:
+ except KeyError:
return rc.NOT_FOUND
tags = Tag.objects.filter(category=category_sng).exclude(items=None)
def href(cls, fragment):
""" Returns URI in the API for the fragment. """
- return API_BASE + reverse("api_fragment",
- args=[fragment.book.slug, fragment.anchor])
+ return API_BASE + reverse("api_fragment", args=[fragment.book.slug, fragment.anchor])
@classmethod
def url(cls, fragment):
fields = ['book', 'url', 'anchor', 'href']
allowed_methods = ('GET',)
- categories = set(['author', 'epoch', 'kind', 'genre', 'book', 'theme'])
+ categories = {'author', 'epoch', 'kind', 'genre', 'book', 'theme'}
@piwik_track
def read(self, request, tags):
from time import mktime
from piston.resource import Resource
+
def timestamp(dtime):
- "converts a datetime.datetime object to a timestamp int"
+ """converts a datetime.datetime object to a timestamp int"""
return int(mktime(dtime.timetuple()))
+
class CsrfExemptResource(Resource):
"""A Custom Resource that is csrf exempt"""
def __init__(self, handler, authentication=None):
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from datetime import datetime
-import os
import os.path
-import re
import sqlite3
from django.core.management.base import BaseCommand
from api.helpers import timestamp
from api.settings import MOBILE_INIT_DB
from catalogue.models import Book, Tag
+from wolnelektury.utils import makedirs
class Command(BaseCommand):
if size < 10:
return "%.1f %s" % (size, unit)
return "%d %s" % (size, unit)
-
-
- if not isinstance(value, unicode):
- value = unicode(value, 'utf-8')
-
- # try to replace chars
- value = re.sub('[^a-zA-Z0-9\\s\\-]{1}', replace_char, value)
- value = value.lower()
- value = re.sub(r'[^a-z0-9{|}]+', '~', value)
-
- return value.encode('ascii', 'ignore')
-
+ #
+ # if not isinstance(value, unicode):
+ # value = unicode(value, 'utf-8')
+ #
+ # # try to replace chars
+ # value = re.sub('[^a-zA-Z0-9\\s\\-]{1}', replace_char, value)
+ # value = value.lower()
+ # value = re.sub(r'[^a-z0-9{|}]+', '~', value)
+ #
+ # return value.encode('ascii', 'ignore')
def init_db(last_checked):
- if not os.path.isdir(MOBILE_INIT_DB):
- os.makedirs(MOBILE_INIT_DB)
+ makedirs(MOBILE_INIT_DB)
db = sqlite3.connect(os.path.join(MOBILE_INIT_DB, 'initial.db-%d' % last_checked))
schema = """
)
-
book_sql = """
INSERT INTO book
(id, title, cover, html_file, html_file_size, parent, parent_number, sort_key, pretty_size, authors)
def add_book(db, book):
- id = book.id
title = book.title
if book.html_file:
html_file = book.html_file.url
def add_tag(db, tag):
- id = tag.id
category = categories[tag.category]
name = tag.name
sort_key = tag.sort_key
else:
category = None
content_type = ContentType.objects.get_for_model(sender)
- Deleted.objects.create(content_type=content_type, object_id=instance.id,
- created_at=instance.created_at, category=category, slug=instance.slug)
+ Deleted.objects.create(
+ content_type=content_type, object_id=instance.id, created_at=instance.created_at, category=category,
+ slug=instance.slug)
pre_delete.connect(_pre_delete_handler)
books = self.load_json('/api/authors/joe/books/')
self.assertEqual([b['title'] for b in books], [self.book_tagged.title],
- 'Wrong tagged book list.')
+ 'Wrong tagged book list.')
def test_detail(self):
book = self.load_json('/api/books/a-book/')
self.assertEqual(book['title'], self.book.title,
- 'Wrong book details.')
+ 'Wrong book details.')
class TagTests(ApiTest):
def test_tag_list(self):
tags = self.load_json('/api/authors/')
self.assertEqual(len(tags), 1,
- 'Wrong tag list.')
+ 'Wrong tag list.')
def test_tag_detail(self):
tag = self.load_json('/api/authors/joe/')
self.assertEqual(tag['name'], self.tag.name,
- 'Wrong tag details.')
+ 'Wrong tag details.')
class PictureTests(ApiTest):
def test_publish(self):
slug = "kandinsky-composition-viii"
- xml = SimpleUploadedFile('composition8.xml', open(path.join(picture.tests.__path__[0], "files", slug + ".xml")).read())
- img = SimpleUploadedFile('kompozycja-8.png', open(path.join(picture.tests.__path__[0], "files", slug + ".png")).read())
+ xml = SimpleUploadedFile(
+ 'composition8.xml', open(path.join(picture.tests.__path__[0], "files", slug + ".xml")).read())
+ img = SimpleUploadedFile(
+ 'kompozycja-8.png', open(path.join(picture.tests.__path__[0], "files", slug + ".png")).read())
import_form = PictureImportForm({}, {
'picture_xml_file': xml,
book_list_resource = CsrfExemptResource(handler=handlers.BooksHandler, authentication=auth)
ebook_list_resource = Resource(handler=handlers.EBooksHandler)
-#book_list_resource = Resource(handler=handlers.BooksHandler)
+# book_list_resource = Resource(handler=handlers.BooksHandler)
book_resource = Resource(handler=handlers.BookDetailHandler)
collection_resource = Resource(handler=handlers.CollectionDetailHandler)
url(r'^oauth/authorize/$', 'oauth_user_auth'),
url(r'^oauth/access_token/$', csrf_exempt(oauth_access_token)),
-) + patterns('',
+) + patterns(
+ '',
url(r'^$', TemplateView.as_view(template_name='api/main.html'), name='api'),
url(r'^include/(?P<model>book|fragment|tag)/(?P<pk>\d+)\.(?P<lang>.+)\.(?P<emitter_format>xml|json)$',
incl, name='api_include'),
+# -*- coding: utf-8 -*-
#############################################################################
# from http://djangosnippets.org/snippets/243/
from django.http import HttpResponse
from django.contrib.auth import authenticate, login
-#
-def view_or_basicauth(view, request, test_func, realm = "", *args, **kwargs):
+
+def view_or_basicauth(view, request, test_func, realm="", *args, **kwargs):
"""
This is a helper function used by 'logged_in_or_basicauth' and
'has_perm_or_basicauth' (deleted) that does the nitty of determining if they
#
-def logged_in_or_basicauth(realm = ""):
+def logged_in_or_basicauth(realm=""):
"""
A simple decorator that requires a user to be logged in. If they are not
logged in the request is examined for a 'authorization' header.
"""Default settings for catalogue app."""
DEFAULT_LANGUAGE = u'pol'
# PDF needs TeXML + XeLaTeX, MOBI needs Calibre.
- DONT_BUILD = set(['pdf', 'mobi'])
+ DONT_BUILD = {'pdf', 'mobi'}
FORMAT_ZIPS = {
'epub': 'wolnelektury_pl_epub',
'pdf': 'wolnelektury_pl_pdf',
}
REDAKCJA_URL = "http://redakcja.wolnelektury.pl"
- GOOD_LICENSES = set([r'CC BY \d\.\d', r'CC BY-SA \d\.\d'])
+ GOOD_LICENSES = {r'CC BY \d\.\d', r'CC BY-SA \d\.\d'}
RELATED_RANDOM_PICTURE_CHANCE = .5
def _more_DONT_BUILD(self, value):
for format_ in ['cover', 'pdf', 'epub', 'mobi', 'fb2', 'txt']:
attname = 'NO_BUILD_%s' % format_.upper()
if hasattr(settings, attname):
- logging.warn("%s is deprecated, "
- "use CATALOGUE_DONT_BUILD instead", attname)
+ logging.warn("%s is deprecated, use CATALOGUE_DONT_BUILD instead", attname)
if getattr(settings, attname):
value.add(format_)
else:
for format_ in ['epub', 'pdf', 'mobi', 'fb2']:
attname = 'ALL_%s_ZIP' % format_.upper()
if hasattr(settings, attname):
- logging.warn("%s is deprecated, "
- "use CATALOGUE_FORMAT_ZIPS[%s] instead",
- attname, format_)
+ logging.warn("%s is deprecated, use CATALOGUE_FORMAT_ZIPS[%s] instead", attname, format_)
value[format_] = getattr(settings, attname)
return value
search_fields = ('name',)
ordering = ('name',)
- prepopulated_fields = {'slug': ('name',), 'sort_key': ('name',),}
+ prepopulated_fields = {'slug': ('name',), 'sort_key': ('name',)}
radio_fields = {'category': admin.HORIZONTAL}
inlines = [MediaInline]
def change_view(self, request, object_id, extra_context=None):
- if not request.GET.has_key('advanced'):
+ if 'advanced' not in request.GET:
self.form = forms.ModelForm
self.fields = ('title', 'description', 'gazeta_link', 'wiki_link')
self.readonly_fields = ('title',)
self.form = TaggableModelForm
self.fields = None
self.readonly_fields = ()
- return super(BookAdmin, self).change_view(request, object_id,
- extra_context=extra_context)
+ return super(BookAdmin, self).change_view(request, object_id, extra_context=extra_context)
class FragmentAdmin(TaggableModelAdmin):
#
from django.apps import AppConfig
+
class CatalogueConfig(AppConfig):
name = 'catalogue'
from catalogue import models
+
def absolute_url(url):
return "http://%s%s" % (Site.objects.get_current().domain, url)
'daisy': 'WolneLektury.pl - audiobooki w formacie DAISY',
}
- def get_object(self, request, type):
- return {'type': type, 'all': 'all' in request.GET}
+ def get_object(self, request, obj_type):
+ return {'type': obj_type, 'all': 'all' in request.GET}
def title(self, args):
return self.titles[args['type']]
def item_categories(self, item):
return sorted(set(author.name for author in
- item.book.tags.filter(category='author').iterator()))
+ item.book.tags.filter(category='author').iterator()))
def item_description(self, item):
lines = []
class BuildPdf(BuildEbook):
@staticmethod
def transform(wldoc, fieldfile):
- return wldoc.as_pdf(morefloats=settings.LIBRARIAN_PDF_MOREFLOATS,
- cover=True)
+ return wldoc.as_pdf(morefloats=settings.LIBRARIAN_PDF_MOREFLOATS, cover=True)
def build(self, fieldfile):
BuildEbook.build(self, fieldfile)
if lang not in [ln[0] for ln in settings.LANGUAGES]:
lang = None
- fieldfile.save(None, ContentFile(html_output.get_string()),
- save=False)
+ fieldfile.save(None, ContentFile(html_output.get_string()), save=False)
type(book).objects.filter(pk=book.pk).update(**{
fieldfile.field.attname: fieldfile
})
elif lang is not None:
# Don't create unknown themes in non-default languages.
try:
- tag = Tag.objects.get(category='theme',
- **{"name_%s" % lang: theme_name})
+ tag = Tag.objects.get(category='theme', **{"name_%s" % lang: theme_name})
except Tag.DoesNotExist:
pass
else:
short_text = truncate_html_words(text, 15)
if text == short_text:
short_text = ''
- new_fragment = Fragment.objects.create(anchor=fragment.id,
- book=book, text=text, short_text=short_text)
+ new_fragment = Fragment.objects.create(anchor=fragment.id, book=book, text=text, short_text=short_text)
new_fragment.save()
new_fragment.tags = set(meta_tags + themes)
return True
return False
+
@BuildEbook.register('cover_thumb')
@task(ignore_result=True)
class BuildCoverThumb(BuildEbook):
return WLCover(wldoc.book_info, height=193).output_file()
-
class OverwritingFieldFile(FieldFile):
"""
Deletes the old file before saving the new one.
def save(self, name, content, *args, **kwargs):
leave = kwargs.pop('leave', None)
# delete if there's a file already and there's a new one coming
- if not leave and self and (not hasattr(content, 'path') or
- content.path != self.path):
+ if not leave and self and (not hasattr(content, 'path') or content.path != self.path):
self.delete(save=False)
- return super(OverwritingFieldFile, self).save(
- name, content, *args, **kwargs)
+ return super(OverwritingFieldFile, self).save(name, content, *args, **kwargs)
class OverwritingFileField(models.FileField):
class DownloadFormatsForm(forms.Form):
- formats = forms.MultipleChoiceField(required=False, choices=FORMATS,
- widget=forms.CheckboxSelectMultiple)
+ formats = forms.MultipleChoiceField(required=False, choices=FORMATS, widget=forms.CheckboxSelectMultiple)
def __init__(self, *args, **kwargs):
super(DownloadFormatsForm, self).__init__(*args, **kwargs)
('', _('Normal leading')),
('onehalfleading', _('One and a half leading')),
('doubleleading', _('Double leading')),
- )),
+ )),
('fontsize', _("Font size"), (
('', _('Default')),
('13pt', _('Big'))
- )),
-# ('pagesize', _("Paper size"), (
-# ('a4paper', _('A4')),
-# ('a5paper', _('A5')),
-# )),
- )
+ )),
+ # ('pagesize', _("Paper size"), (
+ # ('a4paper', _('A4')),
+ # ('a5paper', _('A5')),
+ # )),
+)
class CustomPDFForm(forms.Form):
def clean(self):
self.cleaned_data['cust'] = self.customizations
- self.cleaned_data['path'] = get_customized_pdf_path(self.book,
- self.cleaned_data['cust'])
+ self.cleaned_data['path'] = get_customized_pdf_path(self.book, self.cleaned_data['cust'])
if not WaitedFile.can_order(self.cleaned_data['path']):
raise ValidationError(_('Queue is full. Please try again later.'))
return self.cleaned_data
if not self.cleaned_data['cust'] and self.book.pdf_file:
# Don't build with default options, just redirect to the standard file.
return {"redirect": self.book.pdf_file.url}
- url = WaitedFile.order(self.cleaned_data['path'],
- lambda p, waiter_id: build_custom_pdf.delay(self.book.id,
- self.cleaned_data['cust'], p, waiter_id),
+ url = WaitedFile.order(
+ self.cleaned_data['path'],
+ lambda p, waiter_id: build_custom_pdf.delay(self.book.id, self.cleaned_data['cust'], p, waiter_id),
self.book.pretty_title()
- )
- #return redirect(url)
+ )
return {"redirect": url}
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from django.conf import settings
-from django.contrib.contenttypes.models import ContentType
-from django.db.models import Count
from .models import Tag, Book
from os.path import getmtime
import cPickle
from collections import defaultdict
-
BOOK_CATEGORIES = ('author', 'epoch', 'genre', 'kind')
-
_COUNTERS = None
_COUNTER_TIME = None
+
+
def get_top_level_related_tags(tags, categories=None):
"""
Finds tags related to given tags through books, and counts their usage.
tag.count = _COUNTERS['count'][tuple(sorted(tagids + (tag.pk,)))]
yield tag
- #~ return related
-
def update_counters():
def combinations(things):
#
from librarian import DocProvider
+
class ORMDocProvider(DocProvider):
"""Used for getting books' children."""
current_domain = lazy(lambda: Site.objects.get_current().domain, str)()
+
+
def full_url(obj):
return 'http://%s%s' % (
current_domain,
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
- help='Suppress output'),
+ help='Suppress output'),
)
help = 'Checks cover sources and licenses.'
good_license = re.compile("(%s)" % ")|(".join(
app_settings.GOOD_LICENSES))
- with transaction.commit_on_success():
+ with transaction.atomic():
for book in Book.objects.all().order_by('slug').iterator():
extra_info = book.extra_info
if not extra_info.get('cover_url'):
else:
without_cover.append(book)
else:
- if not extra_info.get('cover_source', ''
- ).startswith(redakcja_url):
+ if not extra_info.get('cover_source', '').startswith(redakcja_url):
not_redakcja.append(book)
match = re_license.match(extra_info.get('cover_by', ''))
if match:
from django.core.management.base import BaseCommand
from catalogue.models import Book
+from librarian import ParseError
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
- help='Suppress output'),
+ help='Suppress output'),
make_option('-d', '--dry-run', action='store_true', dest='dry_run', default=False,
- help="Just check for problems, don't fix them"),
+ help="Just check for problems, don't fix them"),
)
help = 'Checks integrity of catalogue data.'
verbose = options['verbose']
- with transaction.commit_on_success():
+ with transaction.atomic():
for book in Book.objects.all().iterator():
try:
info = book.wldocument().book_info
- except:
+ except ParseError:
if verbose:
print "ERROR! Bad XML for book:", book.slug
print "To resolve: republish."
from django.core.management.base import BaseCommand
from django.core.management.color import color_style
from django.core.files import File
+from django.db import transaction
from librarian.picture import ImageStore
-from wolnelektury.management.profile import profile
+# from wolnelektury.management.profile import profile
from catalogue.models import Book
from picture.models import Picture
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
- help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
+ help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
make_option('-f', '--force', action='store_true', dest='force', default=False,
- help='Overwrite works already in the catalogue'),
+ help='Overwrite works already in the catalogue'),
make_option('-D', '--dont-build', dest='dont_build',
- metavar="FORMAT,...",
- help="Skip building specified formats"),
+ metavar="FORMAT,...",
+ help="Skip building specified formats"),
make_option('-S', '--no-search-index', action='store_false', dest='search_index', default=True,
- help='Skip indexing imported works for search'),
+ help='Skip indexing imported works for search'),
make_option('-p', '--picture', action='store_true', dest='import_picture', default=False,
- help='Import pictures'),
+ help='Import pictures'),
)
help = 'Imports books from the specified directories.'
args = 'directory [directory ...]'
raise ex
return picture
- # @profile
+ # @profile
+ @transaction.atomic
def handle(self, *directories, **options):
- from django.db import transaction
-
self.style = color_style()
verbose = options.get('verbose')
import_picture = options.get('import_picture')
- index = None
if options.get('search_index') and not settings.NO_SEARCH_INDEX:
index = Index()
try:
index.index.rollback()
raise e
- # Start transaction management.
- with transaction.atomic():
- files_imported = 0
- files_skipped = 0
-
- for dir_name in directories:
- if not os.path.isdir(dir_name):
- print self.style.ERROR("%s: Not a directory. Skipping." % dir_name)
- else:
- # files queue
- files = sorted(os.listdir(dir_name))
- postponed = {}
- while files:
- file_name = files.pop(0)
- file_path = os.path.join(dir_name, file_name)
- file_base, ext = os.path.splitext(file_path)
-
- # Skip files that are not XML files
- if not ext == '.xml':
- continue
-
- if verbose > 0:
- print "Parsing '%s'" % file_path
+ files_imported = 0
+ files_skipped = 0
+
+ for dir_name in directories:
+ if not os.path.isdir(dir_name):
+ print self.style.ERROR("%s: Not a directory. Skipping." % dir_name)
+ else:
+ # files queue
+ files = sorted(os.listdir(dir_name))
+ postponed = {}
+ while files:
+ file_name = files.pop(0)
+ file_path = os.path.join(dir_name, file_name)
+ file_base, ext = os.path.splitext(file_path)
+
+ # Skip files that are not XML files
+ if not ext == '.xml':
+ continue
+
+ if verbose > 0:
+ print "Parsing '%s'" % file_path
+ else:
+ sys.stdout.write('.')
+ sys.stdout.flush()
+
+ # Import book files
+ try:
+ if import_picture:
+ self.import_picture(file_path, options)
+ else:
+ self.import_book(file_path, options)
+
+ files_imported += 1
+
+ except (Book.AlreadyExists, Picture.AlreadyExists):
+ print self.style.ERROR(
+ '%s: Book or Picture already imported. Skipping. To overwrite use --force.' %
+ file_path)
+ files_skipped += 1
+
+ except Book.DoesNotExist, e:
+ if file_name not in postponed or postponed[file_name] < files_imported:
+ # push it back into the queue, maybe the missing child will show up
+ if verbose:
+ print self.style.NOTICE('Waiting for missing children')
+ files.append(file_name)
+ postponed[file_name] = files_imported
else:
- sys.stdout.write('.')
- sys.stdout.flush()
-
- # Import book files
- try:
- if import_picture:
- self.import_picture(file_path, options)
- else:
- self.import_book(file_path, options)
-
- files_imported += 1
-
- except (Book.AlreadyExists, Picture.AlreadyExists):
- print self.style.ERROR('%s: Book or Picture already imported. Skipping. To overwrite use --force.' %
- file_path)
- files_skipped += 1
-
- except Book.DoesNotExist, e:
- if file_name not in postponed or postponed[file_name] < files_imported:
- # push it back into the queue, maybe the missing child will show up
- if verbose:
- print self.style.NOTICE('Waiting for missing children')
- files.append(file_name)
- postponed[file_name] = files_imported
- else:
- # we're in a loop, nothing's being imported - some child is really missing
- raise e
+ # we're in a loop, nothing's being imported - some child is really missing
+ raise e
# Print results
print
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('-t', '--tags', dest='tags', metavar='SLUG,...',
- help='Use only books tagged with this tags'),
+ help='Use only books tagged with this tags'),
make_option('-i', '--include', dest='include', metavar='SLUG,...',
- help='Include specific books by slug'),
+ help='Include specific books by slug'),
make_option('-e', '--exclude', dest='exclude', metavar='SLUG,...',
- help='Exclude specific books by slug')
+ help='Exclude specific books by slug')
)
help = 'Prepare ZIP package with files of given type.'
args = '[%s] output_path.zip' % '|'.join(Book.formats)
from urllib2 import urlopen, HTTPError, URLError
from django.core.urlresolvers import reverse
from django.contrib.sites.models import Site
- from django.contrib.sites.shortcuts import get_current_site
- domain = get_current_site(None).domain
+ domain = Site.objects.get_current().domain
fields = [
- (Book, [
- ('gazeta_link', lambda b: b.gazeta_link),
- ('wiki_link', lambda b: b.wiki_link),
- ('źródło', lambda b: b.extra_info.get('source_url')),
- ], 'admin:catalogue_book_change'
+ (
+ Book,
+ [
+ ('gazeta_link', lambda b: b.gazeta_link),
+ ('wiki_link', lambda b: b.wiki_link),
+ ('źródło', lambda b: b.extra_info.get('source_url')),
+ ],
+ 'admin:catalogue_book_change'
),
- (Picture, [
- ('gazeta_link', lambda p: p.culturepl_link),
- ('wiki_link', lambda p: p.wiki_link),
- ('źródło', lambda p: p.extra_info.get('source_url')),
- ], 'admin:pictures_picture_change'
+ (
+ Picture,
+ [
+ ('gazeta_link', lambda p: p.culturepl_link),
+ ('wiki_link', lambda p: p.wiki_link),
+ ('źródło', lambda p: p.extra_info.get('source_url')),
+ ],
+ 'admin:pictures_picture_change'
)
]
clean = False
print(unicode(obj).encode('utf-8'))
print(('Na stronie: https://%s%s' % (domain, obj.get_absolute_url())).encode('utf-8'))
- print(('Administracja: https://%s%s' % (domain, reverse(admin_name, args=[obj.pk]))).encode('utf-8'))
+ print(
+ ('Administracja: https://%s%s' % (domain, reverse(admin_name, args=[obj.pk])))
+ .encode('utf-8'))
if obj.extra_info.get('about'):
print(('Redakcja: %s' % (obj.extra_info.get('about'),)).encode('utf-8'))
print((' %s (%s): %s' % (name, getattr(e, 'code', 'błąd'), url)).encode('utf-8'))
import os.path
from django.core.management.base import BaseCommand
+from django.db import transaction
from catalogue.models import Book, BookMedia
from catalogue.utils import ExistingFile
class Command(BaseCommand):
- help = "Saves uploaded media with a given book and a given name. If media has a source SHA1 info - matching media is replaced."
+ help = "Saves uploaded media with a given book and a given name. " \
+ "If media has a source SHA1 info - matching media is replaced."
args = 'path slug name'
+ @transaction.atomic
def handle(self, *args, **options):
- from django.db import transaction
-
path, slug, name = args
- # Start transaction management.
- transaction.commit_unless_managed()
- transaction.enter_transaction_management()
- transaction.managed(True)
-
book = Book.objects.get(slug=slug)
root, ext = os.path.splitext(path)
bm.name = name
bm.file.save(None, ExistingFile(path))
bm.save()
- transaction.commit()
- transaction.leave_transaction_management()
bofh_storage = BofhFileSystemStorage()
-def _cover_upload_to(i, n):
- return 'book/cover/%s.jpg' % i.slug
+def _make_upload_to(path):
+ def _upload_to(i, n):
+ return path % i.slug
+ return _upload_to
+
+
+_cover_upload_to = _make_upload_to('book/cover/%s.jpg')
+_cover_thumb_upload_to = _make_upload_to('book/cover_thumb/%s.jpg')
-def _cover_thumb_upload_to(i, n):
- return 'book/cover_thumb/%s.jpg' % i.slug
def _ebook_upload_to(upload_path):
- def _upload_to(i, n):
- return upload_path % i.slug
- return _upload_to
+ return _make_upload_to(upload_path)
class Book(models.Model):
"""Represents a book imported from WL-XML."""
- title = models.CharField(_('title'), max_length=32767)
+ title = models.CharField(_('title'), max_length=32767)
sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False)
- sort_key_author = models.CharField(_('sort key by author'), max_length=120, db_index=True, editable=False, default=u'')
- slug = models.SlugField(_('slug'), max_length=120, db_index=True,
- unique=True)
+ sort_key_author = models.CharField(
+ _('sort key by author'), max_length=120, db_index=True, editable=False, default=u'')
+ slug = models.SlugField(_('slug'), max_length=120, db_index=True, unique=True)
common_slug = models.SlugField(_('slug'), max_length=120, db_index=True)
- language = models.CharField(_('language code'), max_length=3, db_index=True,
- default=app_settings.DEFAULT_LANGUAGE)
- description = models.TextField(_('description'), blank=True)
- created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
- changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
+ language = models.CharField(_('language code'), max_length=3, db_index=True, default=app_settings.DEFAULT_LANGUAGE)
+ description = models.TextField(_('description'), blank=True)
+ created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
+ changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
parent_number = models.IntegerField(_('parent number'), default=0)
- extra_info = jsonfield.JSONField(_('extra information'), default={})
- gazeta_link = models.CharField(blank=True, max_length=240)
- wiki_link = models.CharField(blank=True, max_length=240)
+ extra_info = jsonfield.JSONField(_('extra information'), default={})
+ gazeta_link = models.CharField(blank=True, max_length=240)
+ wiki_link = models.CharField(blank=True, max_length=240)
# files generated during publication
- cover = EbookField('cover', _('cover'),
- null=True, blank=True,
- upload_to=_cover_upload_to,
- storage=bofh_storage, max_length=255)
+ cover = EbookField(
+ 'cover', _('cover'),
+ null=True, blank=True,
+ upload_to=_cover_upload_to,
+ storage=bofh_storage, max_length=255)
# Cleaner version of cover for thumbs
- cover_thumb = EbookField('cover_thumb', _('cover thumbnail'),
- null=True, blank=True,
- upload_to=_cover_thumb_upload_to,
- max_length=255)
+ cover_thumb = EbookField(
+ 'cover_thumb', _('cover thumbnail'),
+ null=True, blank=True,
+ upload_to=_cover_thumb_upload_to,
+ max_length=255)
ebook_formats = constants.EBOOK_FORMATS
formats = ebook_formats + ['html', 'xml']
- parent = models.ForeignKey('self', blank=True, null=True,
- related_name='children')
- ancestor = models.ManyToManyField('self', blank=True,
- editable=False, related_name='descendant', symmetrical=False)
+ parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
+ ancestor = models.ManyToManyField('self', blank=True, editable=False, related_name='descendant', symmetrical=False)
- objects = models.Manager()
- tagged = managers.ModelTaggedItemManager(Tag)
- tags = managers.TagDescriptor(Tag)
+ objects = models.Manager()
+ tagged = managers.ModelTaggedItemManager(Tag)
+ tags = managers.TagDescriptor(Tag)
tag_relations = GenericRelation(Tag.intermediary_table_model)
html_built = django.dispatch.Signal()
from sortify import sortify
self.sort_key = sortify(self.title)[:120]
- self.title = unicode(self.title) # ???
+ self.title = unicode(self.title) # ???
try:
author = self.tags.filter(category='author')[0].sort_key
@permalink
def get_absolute_url(self):
- return ('catalogue.views.book_detail', [self.slug])
+ return 'catalogue.views.book_detail', [self.slug]
@staticmethod
@permalink
def create_url(slug):
- return ('catalogue.views.book_detail', [slug])
+ return 'catalogue.views.book_detail', [slug]
@property
def name(self):
def get_mp3(self):
return self.get_media("mp3")
+
def get_odt(self):
return self.get_media("odt")
+
def get_ogg(self):
return self.get_media("ogg")
+
def get_daisy(self):
return self.get_media("daisy")
else:
meta_fallbacks = None
- return WLDocument.from_file(self.xml_file.path,
- provider=ORMDocProvider(self),
- parse_dublincore=parse_dublincore,
- meta_fallbacks=meta_fallbacks)
+ return WLDocument.from_file(
+ self.xml_file.path,
+ provider=ORMDocProvider(self),
+ parse_dublincore=parse_dublincore,
+ meta_fallbacks=meta_fallbacks)
@staticmethod
def zip_format(format_):
field_name = "%s_file" % format_
books = Book.objects.filter(parent=None).exclude(**{field_name: ""})
- paths = [(pretty_file_name(b), getattr(b, field_name).path)
- for b in books.iterator()]
+ paths = [(pretty_file_name(b), getattr(b, field_name).path) for b in books.iterator()]
return create_zip(paths, app_settings.FORMAT_ZIPS[format_])
def zip_audiobooks(self, format_):
index.index.rollback()
raise e
-
@classmethod
def from_xml_file(cls, xml_file, **kwargs):
from django.core.files import File
xml_file.close()
@classmethod
- def from_text_and_meta(cls, raw_file, book_info, overwrite=False,
- dont_build=None, search_index=True,
- search_index_tags=True):
+ def from_text_and_meta(cls, raw_file, book_info, overwrite=False, dont_build=None, search_index=True,
+ search_index_tags=True):
if dont_build is None:
dont_build = set()
dont_build = set.union(set(dont_build), set(app_settings.DONT_BUILD))
try:
children.append(Book.objects.get(slug=part_url.slug))
except Book.DoesNotExist:
- raise Book.DoesNotExist(_('Book "%s" does not exist.') %
- part_url.slug)
+ raise Book.DoesNotExist(_('Book "%s" does not exist.') % part_url.slug)
# Read book metadata
book_slug = book_info.url.slug
old_cover = None
else:
if not overwrite:
- raise Book.AlreadyExists(_('Book %s already exists') % (
- book_slug))
+ raise Book.AlreadyExists(_('Book %s already exists') % book_slug)
# Save shelves for this book
book_shelves = list(book.tags.filter(category='set'))
old_cover = book.cover_info()
return book
@classmethod
+ @transaction.atomic
def repopulate_ancestors(cls):
"""Fixes the ancestry cache."""
# TODO: table names
- with transaction.atomic():
- cursor = connection.cursor()
- if connection.vendor == 'postgres':
- cursor.execute("TRUNCATE catalogue_book_ancestor")
- cursor.execute("""
- WITH RECURSIVE ancestry AS (
- SELECT book.id, book.parent_id
- FROM catalogue_book AS book
- WHERE book.parent_id IS NOT NULL
- UNION
- SELECT ancestor.id, book.parent_id
- FROM ancestry AS ancestor, catalogue_book AS book
- WHERE ancestor.parent_id = book.id
- AND book.parent_id IS NOT NULL
- )
- INSERT INTO catalogue_book_ancestor
- (from_book_id, to_book_id)
- SELECT id, parent_id
- FROM ancestry
- ORDER BY id;
- """)
- else:
- cursor.execute("DELETE FROM catalogue_book_ancestor")
- for b in cls.objects.exclude(parent=None):
- parent = b.parent
- while parent is not None:
- b.ancestor.add(parent)
- parent = parent.parent
+ cursor = connection.cursor()
+ if connection.vendor == 'postgres':
+ cursor.execute("TRUNCATE catalogue_book_ancestor")
+ cursor.execute("""
+ WITH RECURSIVE ancestry AS (
+ SELECT book.id, book.parent_id
+ FROM catalogue_book AS book
+ WHERE book.parent_id IS NOT NULL
+ UNION
+ SELECT ancestor.id, book.parent_id
+ FROM ancestry AS ancestor, catalogue_book AS book
+ WHERE ancestor.parent_id = book.id
+ AND book.parent_id IS NOT NULL
+ )
+ INSERT INTO catalogue_book_ancestor
+ (from_book_id, to_book_id)
+ SELECT id, parent_id
+ FROM ancestry
+ ORDER BY id;
+ """)
+ else:
+ cursor.execute("DELETE FROM catalogue_book_ancestor")
+ for b in cls.objects.exclude(parent=None):
+ parent = b.parent
+ while parent is not None:
+ b.ancestor.add(parent)
+ parent = parent.parent
def flush_includes(self, languages=True):
if not languages:
return books
def pretty_title(self, html_links=False):
- names = [(tag.name, tag.get_absolute_url())
- for tag in self.tags.filter(category='author')]
+ names = [(tag.name, tag.get_absolute_url()) for tag in self.tags.filter(category='author')]
books = self.parents() + [self]
names.extend([(b.title, b.get_absolute_url()) for b in books])
return objects.exclude(ancestor__in=objects)
@classmethod
- def book_list(cls, filter=None):
+ def book_list(cls, book_filter=None):
"""Generates a hierarchical listing of all books.
Books are optionally filtered with a test function.
books_by_parent = {}
books = cls.objects.all().order_by('parent_number', 'sort_key').only(
'title', 'parent', 'slug')
- if filter:
- books = books.filter(filter).distinct()
+ if book_filter:
+ books = books.filter(book_filter).distinct()
book_ids = set(b['pk'] for b in books.values("pk").iterator())
for book in books.iterator():
"L": (3, u"liceum"),
"LP": (3, u"liceum"),
}
+
def audiences_pl(self):
audiences = self.extra_info.get('audiences', [])
audiences = sorted(set([self._audiences_pl.get(a, (99, a)) for a in audiences]))
return None
-# add the file fields
-for format_ in Book.formats:
- field_name = "%s_file" % format_
- # This weird globals() assignment makes Django migrations comfortable.
- _upload_to = _ebook_upload_to('book/%s/%%s.%s' % (format_, format_))
- _upload_to.__name__ = '_%s_upload_to' % format_
- globals()[_upload_to.__name__] = _upload_to
-
- EbookField(format_, _("%s file" % format_.upper()),
- upload_to=_upload_to,
- storage=bofh_storage,
- max_length=255,
- blank=True,
- default=''
- ).contribute_to_class(Book, field_name)
+def add_file_fields():
+ for format_ in Book.formats:
+ field_name = "%s_file" % format_
+ # This weird globals() assignment makes Django migrations comfortable.
+ _upload_to = _ebook_upload_to('book/%s/%%s.%s' % (format_, format_))
+ _upload_to.__name__ = '_%s_upload_to' % format_
+ globals()[_upload_to.__name__] = _upload_to
+
+ EbookField(
+ format_, _("%s file" % format_.upper()),
+ upload_to=_upload_to,
+ storage=bofh_storage,
+ max_length=255,
+ blank=True,
+ default=''
+ ).contribute_to_class(Book, field_name)
+
+add_file_fields()
from django.utils.translation import ugettext_lazy as _
import jsonfield
from fnpdjango.utils.text.slughifi import slughifi
+from mutagen import MutagenError
+
from catalogue.fields import OverwritingFileField
def _file_upload_to(i, _n):
- return 'book/%(ext)s/%(name)s.%(ext)s' % {
- 'ext': i.ext(), 'name': slughifi(i.name)}
+ return 'book/%(ext)s/%(name)s.%(ext)s' % {'ext': i.ext(), 'name': slughifi(i.name)}
+
class BookMedia(models.Model):
"""Represents media attached to a book."""
('ogg', FileFormat(name='Ogg Vorbis', ext='ogg')),
('daisy', FileFormat(name='DAISY', ext='daisy.zip')),
])
- format_choices = [(k, _('%s file' % t.name))
- for k, t in formats.items()]
+ format_choices = [(k, _('%s file' % t.name)) for k, t in formats.items()]
type = models.CharField(_('type'), db_index=True, choices=format_choices, max_length=20)
name = models.CharField(_('name'), max_length=512)
- file = OverwritingFileField(_('file'), max_length=600,
- upload_to=_file_upload_to)
+ file = OverwritingFileField(_('file'), max_length=600, upload_to=_file_upload_to)
uploaded_at = models.DateTimeField(_('creation date'), auto_now_add=True, editable=False, db_index=True)
extra_info = jsonfield.JSONField(_('extra information'), default={}, editable=False)
book = models.ForeignKey('Book', related_name='media')
return "%s (%s)" % (self.name, self.file.name.split("/")[-1])
class Meta:
- ordering = ('type', 'name')
- verbose_name = _('book media')
+ ordering = ('type', 'name')
+ verbose_name = _('book media')
verbose_name_plural = _('book media')
app_label = 'catalogue'
audio = id3.ID3(self.file.path)
artist_name = ', '.join(', '.join(tag.text) for tag in audio.getall('TPE1'))
director_name = ', '.join(', '.join(tag.text) for tag in audio.getall('TPE3'))
- project = ", ".join([t.data for t in audio.getall('PRIV')
- if t.owner == 'wolnelektury.pl?project'])
- funded_by = ", ".join([t.data for t in audio.getall('PRIV')
- if t.owner == 'wolnelektury.pl?funded_by'])
- except:
+ project = ", ".join([
+ t.data for t in audio.getall('PRIV')
+ if t.owner == 'wolnelektury.pl?project'])
+ funded_by = ", ".join([
+ t.data for t in audio.getall('PRIV')
+ if t.owner == 'wolnelektury.pl?funded_by'])
+ except MutagenError:
pass
elif self.type == 'ogg':
try:
director_name = ', '.join(audio.get('conductor', []))
project = ", ".join(audio.get('project', []))
funded_by = ", ".join(audio.get('funded_by', []))
- except:
+ except (MutagenError, AttributeError):
pass
else:
return {}
audio = id3.ID3(filepath)
return [t.data for t in audio.getall('PRIV')
if t.owner == 'wolnelektury.pl?flac_sha1'][0]
- except:
+ except MutagenError:
return None
elif filetype == 'ogg':
try:
audio = mutagen.File(filepath)
return audio.get('flac_sha1', [None])[0]
- except:
+ except (MutagenError, AttributeError):
return None
else:
return None
models.SlugField(_('slug'), max_length=120, unique=True, db_index=True)
book_slugs = models.TextField(_('book slugs'))
- kind = models.CharField(_('kind'), max_length=10, blank=False, default='book', db_index=True, choices=((('book'), _('book')), (('picture'), ('picture'))))
+ kind = models.CharField(_('kind'), max_length=10, blank=False, default='book', db_index=True,
+ choices=(('book', _('book')), ('picture', _('picture'))))
class Meta:
ordering = ('title',)
@models.permalink
def get_absolute_url(self):
- return ("collection", [self.slug])
+ return "collection", [self.slug]
def get_query(self):
slugs = self.book_slugs.split()
# allow URIs
- slugs = [slug.rstrip('/').rsplit('/', 1)[-1] if '/' in slug else slug
- for slug in slugs]
+ # WTF
+ slugs = [slug.rstrip('/').rsplit('/', 1)[-1] if '/' in slug else slug for slug in slugs]
return models.Q(slug__in=slugs)
def get_books(self):
if old_name != self.name or old_netloc != self.netloc:
for book in Book.objects.all():
source = book.extra_info.get('source_url', '')
- if self.netloc in source or (old_netloc != self.netloc
- and old_netloc in source):
+ if self.netloc in source or (old_netloc != self.netloc and old_netloc in source):
book.flush_includes()
return ret
('genre', _('genre')),
('theme', _('theme')),
('set', _('set')),
- ('thing', _('thing')), # things shown on pictures
+ ('thing', _('thing')), # things shown on pictures
)
name = models.CharField(_('name'), max_length=120, db_index=True)
slug = models.SlugField(_('slug'), max_length=120, db_index=True)
sort_key = models.CharField(_('sort key'), max_length=120, db_index=True)
- category = models.CharField(_('category'), max_length=50, blank=False, null=False,
- db_index=True, choices=TAG_CATEGORIES)
+ category = models.CharField(
+ _('category'), max_length=50, blank=False, null=False, db_index=True, choices=TAG_CATEGORIES)
description = models.TextField(_('description'), blank=True)
user = models.ForeignKey(User, blank=True, null=True)
@permalink
def get_absolute_url(self):
- return ('tagged_object_list', [self.url_chunk])
+ return 'tagged_object_list', [self.url_chunk]
@permalink
def get_absolute_gallery_url(self):
- return ('tagged_object_list_gallery', [self.url_chunk])
+ return 'tagged_object_list_gallery', [self.url_chunk]
@classmethod
@permalink
def create_url(cls, category, slug):
return ('catalogue.views.tagged_object_list', [
- '/'.join((cls.categories_dict[category], slug))
- ])
+ '/'.join((cls.categories_dict[category], slug))
+ ])
def has_description(self):
return len(self.description) > 0
@staticmethod
def get_tag_list(tags):
if isinstance(tags, basestring):
- if not tags: return []
+ if not tags:
+ return []
real_tags = []
ambiguous_slugs = []
category = None
try:
real_tags.append(Tag.objects.get(slug=name))
deprecated = True
- except Tag.MultipleObjectsReturned, e:
+ except Tag.MultipleObjectsReturned:
ambiguous_slugs.append(name)
if category:
for field_name, category in categories:
try:
tag_names = getattr(info, field_name)
- except:
+ except KeyError:
try:
tag_names = [getattr(info, category)]
- except:
+ except KeyError:
# For instance, Pictures do not have 'genre' field.
continue
for tag_name in tag_names:
@receiver(tags_updated)
def receive_tags_updated(sender, instance, affected_tags, **kwargs):
- categories = set(tag.category for tag in affected_tags
- if tag.category not in ('set', 'book'))
+ categories = set(tag.category for tag in affected_tags if tag.category not in ('set', 'book'))
if not categories:
return
class RegistrationForm(UserCreationForm):
def as_ul(self):
- "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
- return self._html_output(u'<li>%(errors)s%(label)s %(field)s<span class="help-text">%(help_text)s</span></li>', u'<li>%s</li>', '</li>', u' %s', False)
+ """Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."""
+ return self._html_output(
+ u'<li>%(errors)s%(label)s %(field)s<span class="help-text">%(help_text)s</span></li>', u'<li>%s</li>',
+ '</li>', u' %s', False)
class LoginForm(AuthenticationForm):
def as_ul(self):
- "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
- return self._html_output(u'<li>%(errors)s%(label)s %(field)s<span class="help-text">%(help_text)s</span></li>', u'<li>%s</li>', '</li>', u' %s', False)
+ """Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."""
+ return self._html_output(
+ u'<li>%(errors)s%(label)s %(field)s<span class="help-text">%(help_text)s</span></li>', u'<li>%s</li>',
+ '</li>', u' %s', False)
def iterable(obj):
# Specjalny przypadek "Dramat w twórczości Sofoklesa", wtedy gdy podane
# są tylko rodzaj literacki i autor
if 'kind' in self and 'author' in self and len(self) == 2:
- text = u'%s w twórczości %s' % (unicode(self['kind']),
- flection.get_case(unicode(self['author']), u'dopełniacz'))
+ text = u'%s w twórczości %s' % (
+ unicode(self['kind']), flection.get_case(unicode(self['author']), u'dopełniacz'))
return capfirst(text)
# Przypadki ogólniejsze
else:
return ''
+
@register.simple_tag
def audiobook_tree(book_list, books_by_parent):
text = "".join("<li><a class='open-player' href='%s'>%s</a>%s</li>" % (
- reverse("book_player", args=[book.slug]), book.title, audiobook_tree(books_by_parent.get(book, ()), books_by_parent)
- ) for book in book_list)
+ reverse("book_player", args=[book.slug]), book.title,
+ audiobook_tree(books_by_parent.get(book, ()), books_by_parent)
+ ) for book in book_list)
if text:
return "<ol>%s</ol>" % text
else:
return ''
+
@register.simple_tag
def book_tree_texml(book_list, books_by_parent, depth=1):
return "".join("""
"children": book_tree_csv(author, books_by_parent.get(book.id, ()), books_by_parent, depth + 1)
} for book in book_list)
+
@register.simple_tag
def all_editors(extra_info):
editors = []
one_tag = tags[0]
if category is not None:
- other = Tag.objects.filter(category=category).exclude(pk__in=[t.pk for t in tags]).exclude(pk__in=[t.pk for t in category_choices])
+ other = Tag.objects.filter(category=category).exclude(pk__in=[t.pk for t in tags])\
+ .exclude(pk__in=[t.pk for t in category_choices])
# Filter out empty tags.
ct = ContentType.objects.get_for_model(Picture if gallery else Book)
other = other.filter(items__content_type=ct).distinct()
return locals()
-
@register.inclusion_tag('catalogue/plain_list.html', takes_context=True)
-def plain_list(context, object_list, with_initials=True, by_author=False, choice=None, book=None, gallery=False, paged=True):
+def plain_list(context, object_list, with_initials=True, by_author=False, choice=None, book=None, gallery=False,
+ paged=True):
names = [('', [])]
last_initial = None
for obj in object_list:
return locals()
-
# TODO: These are no longer just books.
@register.inclusion_tag('catalogue/related_books.html', takes_context=True)
def related_books(context, instance, limit=6, random=1, taken=0):
- limit = limit - taken
+ limit -= taken
max_books = limit - random
is_picture = isinstance(instance, Picture)
def download_audio(book, daisy=True):
links = []
if book.has_media('mp3'):
- links.append("<a href='%s'>%s</a>" %
- (reverse('download_zip_mp3', args=[book.slug]),
- BookMedia.formats['mp3'].name))
+ links.append("<a href='%s'>%s</a>" % (
+ reverse('download_zip_mp3', args=[book.slug]), BookMedia.formats['mp3'].name))
if book.has_media('ogg'):
- links.append("<a href='%s'>%s</a>" %
- (reverse('download_zip_ogg', args=[book.slug]),
- BookMedia.formats['ogg'].name))
+ links.append("<a href='%s'>%s</a>" % (
+ reverse('download_zip_ogg', args=[book.slug]), BookMedia.formats['ogg'].name))
if daisy and book.has_media('daisy'):
for dsy in book.get_media('daisy'):
- links.append("<a href='%s'>%s</a>" %
- (dsy.file.url, BookMedia.formats['daisy'].name))
+ links.append("<a href='%s'>%s</a>" % (dsy.file.url, BookMedia.formats['daisy'].name))
return "".join(links)
@override_settings(
MEDIA_ROOT=tempfile.mkdtemp(prefix='djangotest_'),
- CATALOGUE_DONT_BUILD=set(['pdf', 'mobi', 'epub', 'txt', 'fb2', 'cover']),
+ CATALOGUE_DONT_BUILD={'pdf', 'mobi', 'epub', 'txt', 'fb2', 'cover'},
NO_SEARCH_INDEX=True,
CELERY_ALWAYS_EAGER=True,
CACHES={
# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
-from django.conf import settings
-
from django.core.files.base import ContentFile
from catalogue.test_utils import *
from catalogue import models
from nose.tools import raises
from os import path, makedirs
+
class BookImportLogicTests(WLTestCase):
def setUp(self):
self.expected_tags.sort()
def test_empty_book(self):
- BOOK_TEXT = "<utwor />"
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+ book_text = "<utwor />"
+ book = models.Book.from_text_and_meta(ContentFile(book_text), self.book_info)
self.assertEqual(book.title, "Default Book")
self.assertEqual(book.slug, "default-book")
self.assertEqual(book.gazeta_link, '')
self.assertEqual(book.description, '')
- tags = [ (tag.category, tag.slug) for tag in book.tags ]
+ tags = [(tag.category, tag.slug) for tag in book.tags]
tags.sort()
self.assertEqual(tags, self.expected_tags)
Should work like any other non-empty book.
"""
- BOOK_TEXT = """<utwor>
+ book_text = """<utwor>
<liryka_l>
<nazwa_utworu>Nic</nazwa_utworu>
</liryka_l></utwor>
"""
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+ book = models.Book.from_text_and_meta(ContentFile(book_text), self.book_info)
self.assertTrue(book.has_html_file())
def test_book_with_fragment(self):
- BOOK_TEXT = """<utwor>
+ book_text = """<utwor>
<opowiadanie>
<akap><begin id="m01" /><motyw id="m01">Love</motyw>Ala ma kota<end id="m01" /></akap>
</opowiadanie></utwor>
"""
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+ book = models.Book.from_text_and_meta(ContentFile(book_text), self.book_info)
self.assertTrue(book.has_html_file())
self.assertEqual(book.fragments.count(), 1)
self.assertEqual(book.fragments.all()[0].text, u'<p class="paragraph">Ala ma kota</p>\n')
- self.assert_(('theme', 'love') in [ (tag.category, tag.slug) for tag in book.fragments.all()[0].tags ])
+ self.assert_(('theme', 'love') in [(tag.category, tag.slug) for tag in book.fragments.all()[0].tags])
def test_book_with_empty_theme(self):
""" empty themes should be ignored """
- BOOK_TEXT = """<utwor>
+ book_text = """<utwor>
<opowiadanie>
<akap><begin id="m01" /><motyw id="m01"> , Love , , </motyw>Ala ma kota<end id="m01" /></akap>
</opowiadanie></utwor>
"""
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+ book = models.Book.from_text_and_meta(ContentFile(book_text), self.book_info)
self.assert_([('theme', 'love')],
- [ (tag.category, tag.slug) for tag in book.fragments.all()[0].tags.filter(category='theme') ])
+ [(tag.category, tag.slug) for tag in book.fragments.all()[0].tags.filter(category='theme')])
def test_book_with_no_theme(self):
""" fragments with no themes shouldn't be created at all """
- BOOK_TEXT = """<utwor>
+ book_text = """<utwor>
<opowiadanie>
<akap><begin id="m01" /><motyw id="m01"></motyw>Ala ma kota<end id="m01" /></akap>
</opowiadanie></utwor>
"""
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+ book = models.Book.from_text_and_meta(ContentFile(book_text), self.book_info)
self.assertEqual(book.fragments.count(), 0)
self.assertEqual(book.tags.filter(category='theme').count(), 0)
def test_book_with_invalid_slug(self):
""" Book with invalid characters in slug shouldn't be imported """
self.book_info.url = WLURI.from_slug(u"default_book")
- BOOK_TEXT = "<utwor />"
- models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+ book_text = "<utwor />"
+ models.Book.from_text_and_meta(ContentFile(book_text), self.book_info)
def test_book_replace_title(self):
- BOOK_TEXT = """<utwor />"""
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+ book_text = """<utwor />"""
+ models.Book.from_text_and_meta(ContentFile(book_text), self.book_info)
self.book_info.title = u"Extraordinary"
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info, overwrite=True)
+ book = models.Book.from_text_and_meta(ContentFile(book_text), self.book_info, overwrite=True)
- tags = [ (tag.category, tag.slug) for tag in book.tags ]
+ tags = [(tag.category, tag.slug) for tag in book.tags]
tags.sort()
self.assertEqual(tags, self.expected_tags)
def test_book_replace_author(self):
- BOOK_TEXT = """<utwor />"""
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+ book_text = """<utwor />"""
+ models.Book.from_text_and_meta(ContentFile(book_text), self.book_info)
self.book_info.author = PersonStub(("Hans", "Christian"), "Andersen")
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info, overwrite=True)
+ book = models.Book.from_text_and_meta(ContentFile(book_text), self.book_info, overwrite=True)
- tags = [ (tag.category, tag.slug) for tag in book.tags ]
+ tags = [(tag.category, tag.slug) for tag in book.tags]
tags.sort()
self.expected_tags.remove(('author', 'jim-lazy'))
models.Tag.objects.get(slug="jim-lazy", category="author")
def test_book_remove_fragment(self):
- BOOK_TEXT = """<utwor>
+ book_text = """<utwor>
<opowiadanie>
<akap>
<begin id="m01" /><motyw id="m01">Love</motyw>Ala ma kota<end id="m01" />
</akap>
</opowiadanie></utwor>
"""
- BOOK_TEXT_AFTER = """<utwor>
+ book_text_after = """<utwor>
<opowiadanie>
<akap>
<begin id="m01" /><motyw id="m01">Love</motyw>Ala ma kota<end id="m01" />
</opowiadanie></utwor>
"""
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+ book = models.Book.from_text_and_meta(ContentFile(book_text), self.book_info)
self.assertEqual(book.fragments.count(), 2)
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT_AFTER), self.book_info, overwrite=True)
+ book = models.Book.from_text_and_meta(ContentFile(book_text_after), self.book_info, overwrite=True)
self.assertEqual(book.fragments.count(), 1)
def test_multiple_tags(self):
- BOOK_TEXT = """<utwor />"""
+ book_text = """<utwor />"""
self.book_info.authors = self.book_info.author, PersonStub(("Joe",), "Dilligent"),
self.book_info.kinds = self.book_info.kind, 'Y-Kind',
self.book_info.genres = self.book_info.genre, 'Y-Genre',
])
self.expected_tags.sort()
- book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
- tags = [ (tag.category, tag.slug) for tag in book.tags ]
+ book = models.Book.from_text_and_meta(ContentFile(book_text), self.book_info)
+ tags = [(tag.category, tag.slug) for tag in book.tags]
tags.sort()
self.assertEqual(tags, self.expected_tags)
)
def test_child(self):
- TEXT = """<utwor />"""
- child = models.Book.from_text_and_meta(ContentFile(TEXT), self.child_info)
- parent = models.Book.from_text_and_meta(ContentFile(TEXT), self.parent_info)
+ text = """<utwor />"""
+ child = models.Book.from_text_and_meta(ContentFile(text), self.child_info)
+ parent = models.Book.from_text_and_meta(ContentFile(text), self.parent_info)
author = parent.tags.get(category='author')
books = self.client.get(author.get_absolute_url()).context['object_list']
- self.assertEqual(len(books), 1,
- "Only parent book should be visible on author's page")
+ self.assertEqual(len(books), 1, "Only parent book should be visible on author's page")
def test_child_replace(self):
- PARENT_TEXT = """<utwor />"""
- CHILD_TEXT = """<utwor>
+ parent_text = """<utwor />"""
+ child_text = """<utwor>
<opowiadanie>
<akap><begin id="m01" /><motyw id="m01">Pies</motyw>Ala ma kota<end id="m01" /></akap>
</opowiadanie></utwor>
"""
- child = models.Book.from_text_and_meta(ContentFile(CHILD_TEXT), self.child_info)
- parent = models.Book.from_text_and_meta(ContentFile(PARENT_TEXT), self.parent_info)
- CHILD_TEXT = """<utwor>
+ child = models.Book.from_text_and_meta(ContentFile(child_text), self.child_info)
+ parent = models.Book.from_text_and_meta(ContentFile(parent_text), self.parent_info)
+ child_text = """<utwor>
<opowiadanie>
<akap><begin id="m01" /><motyw id="m01">Kot</motyw>Ala ma kota<end id="m01" /></akap>
</opowiadanie></utwor>
"""
- child = models.Book.from_text_and_meta(ContentFile(CHILD_TEXT), self.child_info, overwrite=True)
+ child = models.Book.from_text_and_meta(ContentFile(child_text), self.child_info, overwrite=True)
themes = parent.related_themes()
- self.assertEqual(['Kot'], [tag.name for tag in themes],
- 'wrong related theme list')
+ self.assertEqual(['Kot'], [tag.name for tag in themes], 'wrong related theme list')
class TreeImportTest(WLTestCase):
def test_ok(self):
self.assertEqual(
- list(self.client.get('/katalog/gatunek/x-genre/'
- ).context['object_list']),
+ list(self.client.get('/katalog/gatunek/x-genre/').context['object_list']),
[self.parent],
u"There should be only parent on common tag page."
)
- pies = models.Tag.objects.get(slug='pies')
+ # pies = models.Tag.objects.get(slug='pies')
themes = self.parent.related_themes()
- self.assertEqual(len(themes), 1,
- u"There should be child theme in parent theme counter."
- )
+ self.assertEqual(len(themes), 1, u"There should be child theme in parent theme counter.")
# TODO: book_count is deprecated, update here.
- #~ epoch = models.Tag.objects.get(slug='x-epoch')
- #~ self.assertEqual(epoch.book_count, 1,
- #~ u"There should be only parent in common tag's counter."
- #~ )
+ # epoch = models.Tag.objects.get(slug='x-epoch')
+ # self.assertEqual(epoch.book_count, 1, u"There should be only parent in common tag's counter.")
def test_child_republish(self):
- CHILD_TEXT = """<utwor>
+ child_text = """<utwor>
<opowiadanie>
<akap><begin id="m01" /><motyw id="m01">Pies, Kot</motyw>
Ala ma kota<end id="m01" /></akap>
</opowiadanie></utwor>
"""
models.Book.from_text_and_meta(
- ContentFile(CHILD_TEXT), self.child_info, overwrite=True)
+ ContentFile(child_text), self.child_info, overwrite=True)
self.assertEqual(
- list(self.client.get('/katalog/gatunek/x-genre/'
- ).context['object_list']),
+ list(self.client.get('/katalog/gatunek/x-genre/').context['object_list']),
[self.parent],
u"There should only be parent on common tag page."
)
- pies = models.Tag.objects.get(slug='pies')
- kot = models.Tag.objects.get(slug='kot')
+ # pies = models.Tag.objects.get(slug='pies')
+ # kot = models.Tag.objects.get(slug='kot')
self.assertEqual(len(self.parent.related_themes()), 2,
- u"There should be child themes in parent theme counter."
- )
+ u"There should be child themes in parent theme counter.")
# TODO: book_count is deprecated, update here.
- #~ epoch = models.Tag.objects.get(slug='x-epoch')
- #~ self.assertEqual(epoch.book_count, 1,
- #~ u"There should only be parent in common tag's counter."
- #~ )
+ # epoch = models.Tag.objects.get(slug='x-epoch')
+ # self.assertEqual(epoch.book_count, 1, u"There should only be parent in common tag's counter.")
def test_book_change_child(self):
second_child_info = BookInfoStub(
author=PersonStub(("Joe",), "Doe"),
**info_args("Second Child")
)
- SECOND_CHILD_TEXT = """<utwor>
+ second_child_text = """<utwor>
<opowiadanie>
<akap><begin id="m01" /><motyw id="m01">Kot</motyw>
Ala ma kota<end id="m01" /></akap>
"""
# Import a second child.
second_child = models.Book.from_text_and_meta(
- ContentFile(SECOND_CHILD_TEXT), second_child_info)
+ ContentFile(second_child_text), second_child_info)
# The book has only this new child now.
self.book_info.parts = [second_child_info.url]
self.book = models.Book.from_text_and_meta(
ContentFile(self.BOOK_TEXT), self.book_info, overwrite=True)
self.assertEqual(
- set(self.client.get('/katalog/gatunek/x-genre/'
- ).context['object_list']),
- set([self.parent, self.child]),
- u"There should be parent and old child on common tag page."
- )
- kot = models.Tag.objects.get(slug='kot')
+ set(self.client.get('/katalog/gatunek/x-genre/').context['object_list']),
+ {self.parent, self.child},
+ u"There should be parent and old child on common tag page."
+ )
+ # kot = models.Tag.objects.get(slug='kot')
self.assertEqual(len(self.parent.related_themes()), 1,
- u"There should only be new child themes in parent theme counter."
- )
- epoch = models.Tag.objects.get(slug='x-epoch')
- # book_count deprecated, update test.
- #~ self.assertEqual(epoch.book_count, 2,
- #~ u"There should be parent and old child in common tag's counter."
- #~ )
+ u"There should only be new child themes in parent theme counter.")
+ # # book_count deprecated, update test.
+ # epoch = models.Tag.objects.get(slug='x-epoch')
+ # self.assertEqual(epoch.book_count, 2,
+ # u"There should be parent and old child in common tag's counter.")
self.assertEqual(
- list(self.client.get('/katalog/lektura/parent/motyw/kot/'
- ).context['fragments']),
- [second_child.fragments.all()[0]],
- u"There should be new child's fragments on parent's theme page."
- )
+ list(self.client.get('/katalog/lektura/parent/motyw/kot/').context['fragments']),
+ [second_child.fragments.all()[0]],
+ u"There should be new child's fragments on parent's theme page."
+ )
self.assertEqual(
- list(self.client.get('/katalog/lektura/parent/motyw/pies/'
- ).context['fragments']),
- [],
- u"There should be no old child's fragments on parent's theme page."
- )
+ list(self.client.get('/katalog/lektura/parent/motyw/pies/').context['fragments']),
+ [],
+ u"There should be no old child's fragments on parent's theme page."
+ )
class MultilingualBookImportTest(WLTestCase):
)
def test_multilingual_import(self):
- BOOK_TEXT = """<utwor><opowiadanie><akap>A</akap></opowiadanie></utwor>"""
+ book_text = """<utwor><opowiadanie><akap>A</akap></opowiadanie></utwor>"""
- book1 = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.pol_info)
- book2 = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.eng_info)
+ models.Book.from_text_and_meta(ContentFile(book_text), self.pol_info)
+ models.Book.from_text_and_meta(ContentFile(book_text), self.eng_info)
self.assertEqual(
- set([b.language for b in models.Book.objects.all()]),
- set(['pol', 'eng']),
- 'Books imported in wrong languages.'
- )
+ set([b.language for b in models.Book.objects.all()]),
+ {'pol', 'eng'},
+ 'Books imported in wrong languages.'
+ )
class BookImportGenerateTest(WLTestCase):
if not path.exists(path.dirname(absoulute_path)):
makedirs(path.dirname(absoulute_path))
- build_custom_pdf(self.book.id,
- customizations=['nofootnotes', '13pt', 'a4paper'], file_name=out)
+ build_custom_pdf(self.book.id, customizations=['nofootnotes', '13pt', 'a4paper'], file_name=out)
self.assertTrue(path.exists(absoulute_path))
# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
-from django.conf import settings
-from os.path import basename, exists, join, dirname
+from os.path import basename, exists
from django.core.files.base import ContentFile
from catalogue.test_utils import *
from catalogue import models, utils
+
class BookMediaTests(WLTestCase):
def setUp(self):
self.book = models.Book.objects.create(slug='test-book')
def test_diacritics(self):
- bm = models.BookMedia(book=self.book, type="ogg",
- name=u"Zażółć gęślą jaźń")
+ bm = models.BookMedia(book=self.book, type="ogg", name=u"Zażółć gęślą jaźń")
bm.file.save(None, self.file)
self.assertEqual(basename(bm.file.name), 'zazolc-gesla-jazn.ogg')
def test_long_name(self):
- bm = models.BookMedia(book=self.book, type="ogg",
- name="Some very very very very very very very very very very very very very very very very long file name")
+ bm = models.BookMedia(
+ book=self.book, type="ogg",
+ name="Some very very very very very very very very very very very very very very very very long file name")
bm.file.save(bm.name, self.file)
# reload to see what was really saved
File gets overwritten with same filename on update.
"""
- bm = models.BookMedia(book=self.book, type='ogg',
- name="Some media")
+ bm = models.BookMedia(book=self.book, type='ogg', name="Some media")
bm.file.save(None, self.file)
bm.file.save(None, self.file2)
File save doesn't clobber some other media with similar name.
"""
- bm = models.BookMedia(book=self.book, type='ogg',
- name=u"Tytul")
+ bm = models.BookMedia(book=self.book, type='ogg', name=u"Tytul")
bm.file.save(None, self.file)
- bm2 = models.BookMedia(book=self.book, type='ogg',
- name=u"Tytuł")
+ bm2 = models.BookMedia(book=self.book, type='ogg', name=u"Tytuł")
bm2.file.save(None, self.file2)
self.assertEqual(basename(bm.file.name), 'tytul.ogg')
self.assertNotEqual(basename(bm2.file.name), 'tytul.ogg')
# Now reimport parent.
parent_cover_changed.reset_mock()
- parent = Book.from_text_and_meta(ContentFile(self.TEXT), self.parent,
- overwrite=True)
+ parent = Book.from_text_and_meta(ContentFile(self.TEXT), self.parent, overwrite=True)
self.assertEqual(parent_cover_changed.call_count, 0)
# Now change cover in parent.
parent_cover_changed.reset_mock()
self.parent.cover_url = "http://example.com/other-cover.jpg"
- parent = Book.from_text_and_meta(ContentFile(self.TEXT), self.parent,
- overwrite=True)
+ parent = Book.from_text_and_meta(ContentFile(self.TEXT), self.parent, overwrite=True)
parent_cover_changed.assert_called_with(child)
@patch.object(Book, 'parent_cover_changed', autospec=True)
# Now import child and reimport parent.
child = Book.from_text_and_meta(ContentFile(self.TEXT), self.child)
self.parent.parts = parts
- parent = Book.from_text_and_meta(ContentFile(self.TEXT), self.parent,
- overwrite=True)
+ parent = Book.from_text_and_meta(ContentFile(self.TEXT), self.parent, overwrite=True)
parent_cover_changed.assert_called_with(child)
# Now remove the child.
parent_cover_changed.reset_mock()
self.parent.parts = []
- parent = Book.from_text_and_meta(ContentFile(self.TEXT), self.parent,
- overwrite=True)
+ parent = Book.from_text_and_meta(ContentFile(self.TEXT), self.parent, overwrite=True)
parent_cover_changed.assert_called_with(child)
from nose.tools import raises
+
class BasicSearchLogicTests(WLTestCase):
def setUp(self):
WLTestCase.setUp(self)
self.author_tag = models.Tag.objects.create(
- name=u'Adam Mickiewicz [SubWord]',
- category=u'author', slug="one")
+ name=u'Adam Mickiewicz [SubWord]',
+ category=u'author', slug="one")
self.unicode_tag = models.Tag.objects.create(
- name=u'Tadeusz Żeleński (Boy)',
- category=u'author', slug="two")
+ name=u'Tadeusz Żeleński (Boy)',
+ category=u'author', slug="two")
self.polish_tag = models.Tag.objects.create(
- name=u'ĘÓĄŚŁŻŹĆŃęóąśłżźćń',
- category=u'author', slug="three")
+ name=u'ĘÓĄŚŁŻŹĆŃęóąśłżźćń',
+ category=u'author', slug="three")
@raises(ValueError)
def test_empty_query(self):
""" empty tag should have no related tags """
cats = self.client.get('/katalog/autor/empty/').context['categories']
- self.assertEqual({k: v for (k, v) in cats.items() if v}, {},
- 'tags related to empty tag')
+ self.assertEqual({k: v for (k, v) in cats.items() if v}, {}, 'tags related to empty tag')
def test_has_related(self):
""" related own and descendants' tags should be generated """
self.assertTrue('Epoch' in [tag.name for tag in cats['epoch']],
'missing `epoch` related tag')
self.assertFalse(cats.get("kind", False),
- "There should be no child-only related `kind` tags")
+ "There should be no child-only related `kind` tags")
self.assertTrue("Genre" in [tag.name for tag in cats['genre']],
'missing `genre` related tag')
self.assertFalse("ChildGenre" in [tag.name for tag in cats['genre']],
- "There should be no child-only related `genre` tags")
+ "There should be no child-only related `genre` tags")
self.assertTrue("GchildGenre" in [tag.name for tag in cats['genre']],
"missing grandchild's related tag")
self.assertTrue('Theme' in [tag.name for tag in cats['theme']],
"missing related theme")
self.assertFalse('Child1Theme' in [tag.name for tag in cats['theme']],
- "There should be no child-only related `theme` tags")
+ "There should be no child-only related `theme` tags")
self.assertTrue('GChildTheme' in [tag.name for tag in cats['theme']],
"missing grandchild's related theme")
[('Epoch', 1)],
'wrong related tag epoch tag on tag page')
-
def test_siblings_tags_count(self):
""" if children have tags and parent hasn't, count the children """
cats = self.client.get('/katalog/epoka/epoch/').context['categories']
- self.assertTrue(('ChildKind', 2) in [(tag.name, tag.count) for tag in cats['kind']],
- 'wrong related kind tags on tag page, got: ' +
- unicode([(tag.name, tag.count) for tag in cats['kind']]))
+ self.assertTrue(
+ ('ChildKind', 2) in [(tag.name, tag.count) for tag in cats['kind']],
+ 'wrong related kind tags on tag page, got: ' +
+ unicode([(tag.name, tag.count) for tag in cats['kind']]))
# all occurencies of theme should be counted
self.assertTrue(('Theme', 4) in [(tag.name, tag.count) for tag in cats['theme']],
- 'wrong related theme count')
+ 'wrong related theme count')
def test_query_child_tag(self):
"""
"""
cats = self.client.get('/katalog/gatunek/childgenre/').context['categories']
self.assertTrue(('Epoch', 2) in [(tag.name, tag.count) for tag in cats['epoch']],
- 'wrong related kind tags on tag page, got: ' +
- unicode([(tag.name, tag.count) for tag in cats['epoch']]))
+ 'wrong related kind tags on tag page, got: ' +
+ unicode([(tag.name, tag.count) for tag in cats['epoch']]))
class CleanTagRelationTests(WLTestCase):
WLTestCase.setUp(self)
author = PersonStub(("Common",), "Man")
- book_info = BookInfoStub(author=author, genre="G", epoch='E', kind="K",
- **info_args(u"Book"))
+ book_info = BookInfoStub(author=author, genre="G", epoch='E', kind="K", **info_args(u"Book"))
book_text = """<utwor><opowiadanie><akap>
<begin id="m01" /><motyw id="m01">Theme</motyw>Ala ma kota
<end id="m01" />
WLTestCase.setUp(self)
author = PersonStub((), "Tag")
- self.book_info = BookInfoStub(author=author,
- genre="tag",
- epoch='tag',
- kind="tag",
- **info_args(u"tag"))
+ self.book_info = BookInfoStub(author=author, genre="tag", epoch='tag', kind="tag", **info_args(u"tag"))
self.book_text = """<utwor>
<opowiadanie>
<akap>
</utwor>
"""
-
def test_book_tags(self):
""" there should be all related tags in relevant categories """
book = models.Book.from_text_and_meta(ContentFile(self.book_text), self.book_info)
def test_qualified_url(self):
models.Book.from_text_and_meta(ContentFile(self.book_text), self.book_info)
- categories = {'author': 'autor', 'theme': 'motyw', 'epoch': 'epoka', 'kind':'rodzaj', 'genre':'gatunek'}
+ categories = {'author': 'autor', 'theme': 'motyw', 'epoch': 'epoka', 'kind': 'rodzaj', 'genre': 'gatunek'}
for cat, localcat in categories.iteritems():
context = self.client.get('/katalog/%s/tag/' % localcat).context
self.assertEqual(1, len(context['object_list']))
author2 = PersonStub(("Jim",), "Lazy")
child_info = BookInfoStub(authors=(author1, author2), genre="ChildGenre", epoch='Epoch', kind="ChildKind",
- **info_args(u"Child"))
+ **info_args(u"Child"))
parent_info = BookInfoStub(author=author1, genre="Genre", epoch='Epoch', kind="Kind",
parts=[child_info.url],
**info_args(u"Parent"))
authors = PersonStub(("Common",), "Man"), PersonStub(("Jane",), "Doe")
child_info = BookInfoStub(authors=authors, genre="Genre", epoch='Epoch', kind="Kind",
- **info_args(u"Child"))
+ **info_args(u"Child"))
parent_info = BookInfoStub(authors=authors, genre="Genre", epoch='Epoch', kind="Kind",
parts=[child_info.url],
**info_args(u"Parent"))
for url in urls:
print(url)
status = self.client.get(prefix + url).status_code
- self.assertEqual(status, expected_status,
- "Wrong status code for '%s'. Expected %d, got %d." % (
- prefix + url, expected_status, status))
+ self.assertEqual(
+ status, expected_status,
+ "Wrong status code for '%s'. Expected %d, got %d." % (prefix + url, expected_status, status))
from modeltranslation.translator import translator, TranslationOptions
from catalogue.models import Collection, Tag, Source
+
class TagTranslationOptions(TranslationOptions):
fields = ('name', 'description', 'wiki_link')
+
class CollectionTranslationOptions(TranslationOptions):
fields = ('title', 'description')
+
class SourceTranslationOptions(TranslationOptions):
fields = ('name',)
SLUG = r'[a-z0-9-]*'
-urlpatterns = patterns('picture.views',
+urlpatterns = patterns(
+ 'picture.views',
# pictures - currently pictures are coupled with catalogue, hence the url is here
url(r'^obraz/$', 'picture_list_thumb', name='picture_list_thumb'),
url(r'^obraz/(?P<slug>%s).html$' % SLUG, 'picture_viewer', name='picture_viewer'),
url(r'^pa/(?P<pk>\d+)/short\.(?P<lang>.+)\.html', 'picturearea_short', name='picture_area_short'),
)
-urlpatterns += patterns('',
+urlpatterns += patterns(
+ '',
# old search page - redirected
url(r'^szukaj/$', RedirectView.as_view(
url='/szukaj/', query_string=True, permanent=True)),
)
-urlpatterns += patterns('catalogue.views',
+urlpatterns += patterns(
+ 'catalogue.views',
url(r'^$', 'catalogue', name='catalogue'),
url(r'^autor/$', 'tag_catalogue', {'category': 'author'}, name='author_catalogue'),
template_name='catalogue/recent_list.html'), name='recent_list'),
url(r'^nowe/audiobooki/$', ListView.as_view(
queryset=Book.objects.filter(media__type='ogg').annotate(m=Max('media__uploaded_at')).order_by('-m'),
- template_name='catalogue/recent_audiobooks_list.html'), name='recent_audiobooks_list'),
+ template_name='catalogue/recent_audiobooks_list.html'), name='recent_audiobooks_list'),
url(r'^nowe/daisy/$', ListView.as_view(
queryset=Book.objects.filter(media__type='daisy').annotate(m=Max('media__uploaded_at')).order_by('-m'),
- template_name='catalogue/recent_daisy_list.html'), name='recent_daisy_list'),
+ template_name='catalogue/recent_daisy_list.html'), name='recent_daisy_list'),
url(r'^custompdf/(?P<slug>%s)/$' % SLUG, CustomPDFFormView(), name='custom_pdf_form'),
# Includes.
url(r'^b/(?P<pk>\d+)/mini\.(?P<lang>.+)\.html', 'book_mini', name='catalogue_book_mini'),
- url(r'^b/(?P<pk>\d+)/mini_nolink\.(?P<lang>.+)\.html', 'book_mini', {'with_link': False}, name='catalogue_book_mini_nolink'),
+ url(r'^b/(?P<pk>\d+)/mini_nolink\.(?P<lang>.+)\.html', 'book_mini', {'with_link': False},
+ name='catalogue_book_mini_nolink'),
url(r'^b/(?P<pk>\d+)/short\.(?P<lang>.+)\.html', 'book_short', name='catalogue_book_short'),
url(r'^b/(?P<pk>\d+)/wide\.(?P<lang>.+)\.html', 'book_wide', name='catalogue_book_wide'),
url(r'^f/(?P<pk>\d+)/promo\.(?P<lang>.+)\.html', 'fragment_promo', name='catalogue_fragment_promo'),
url(r'^c/(?P<pk>.+)/box\.(?P<lang>.+)\.html', 'collection_box', name='catalogue_collection_box'),
# This should be the last pattern.
- url(r'^galeria/(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', {'gallery': True}, name='tagged_object_list_gallery'),
+ url(r'^galeria/(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', {'gallery': True},
+ name='tagged_object_list_gallery'),
url(r'^(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', name='tagged_object_list'),
)
def get_random_hash(seed):
- sha_digest = hashlib.sha1('%s%s%s%s' %
- (randrange(0, MAX_SESSION_KEY), time.time(), unicode(seed).encode('utf-8', 'replace'),
- settings.SECRET_KEY)).digest()
+ sha_digest = hashlib.sha1('%s%s%s%s' % (
+ randrange(0, MAX_SESSION_KEY), time.time(), unicode(seed).encode('utf-8', 'replace'), settings.SECRET_KEY)
+ ).digest()
return urlsafe_b64encode(sha_digest).replace('=', '').replace('_', '-').lower()
self.lock.close()
-#@task
+# @task
def create_zip(paths, zip_slug):
"""
Creates a zip in MEDIA_ROOT/zip directory containing files from path.
for chunk in read_chunks(f):
self.write(chunk)
+
class MultiQuerySet(object):
def __init__(self, *args, **kwargs):
self.querysets = args
(offset, stop, step) = item.indices(self.count())
except AttributeError:
# it's not a slice - make it one
- return self[item : item + 1][0]
+ return self[item:item + 1][0]
items = []
total_len = stop - offset
for qs in self.querysets:
stop = total_len - len(items)
continue
+
class SortedMultiQuerySet(MultiQuerySet):
def __init__(self, *args, **kwargs):
self.order_by = kwargs.pop('order_by', None)
(offset, stop, step) = item.indices(self.count())
except AttributeError:
# it's not a slice - make it one
- return self[item : item + 1][0]
+ return self[item:item + 1][0]
items = []
total_len = stop - offset
skipped = 0
candidate = competitor
candidate_i = i
except IndexError:
- continue # continue next sort_head
+ continue # continue next sort_head
# we have no more elements:
if candidate is None:
break
sort_heads[candidate_i] += 1
if skipped < offset:
skipped += 1
- continue # continue next item
+ continue # continue next item
items.append(candidate)
return items
except ValueError:
pass
else:
- # SGML: An end tag closes, back to the matching start tag, all unclosed intervening start tags with omitted end tags
+ # SGML: An end tag closes, back to the matching start tag,
+ # all unclosed intervening start tags with omitted end tags
open_tags = open_tags[i+1:]
else:
# Add it to the start of the open tags list
def __getattribute__(self, name):
if name.startswith('_'):
return object.__getattribute__(self, name)
- value = getattr(settings,
- "%s_%s" % (self._prefix, name),
- object.__getattribute__(self, name))
+ value = getattr(settings, "%s_%s" % (self._prefix, name), object.__getattribute__(self, name))
more = "_more_%s" % name
if hasattr(self, more):
value = getattr(self, more)(value)
from pdcounter import models as pdcounter_models
from pdcounter import views as pdcounter_views
from picture.models import Picture, PictureArea
-from picture.views import picture_list_thumb
from ssify import ssi_included, ssi_expect, SsiVariable as V
from suggest.forms import PublishingSuggestForm
from catalogue import constants
from catalogue.helpers import get_top_level_related_tags
from catalogue import models
from catalogue.utils import split_tags
-from catalogue.templatetags.catalogue_tags import tag_list, collection_list
staff_required = user_passes_test(lambda user: user.is_staff)
return render(request, 'catalogue/catalogue.html', locals())
-def book_list(request, filter=None, get_filter=None,
- template_name='catalogue/book_list.html',
- nav_template_name='catalogue/snippets/book_list_nav.html',
- list_template_name='catalogue/snippets/book_list.html',
- context=None,
- ):
+def book_list(request, filter=None, get_filter=None, template_name='catalogue/book_list.html',
+ nav_template_name='catalogue/snippets/book_list_nav.html',
+ list_template_name='catalogue/snippets/book_list.html', context=None):
""" generates a listing of all books, optionally filtered with a test function """
if get_filter:
filter = get_filter()
books_nav.setdefault(tag.sort_key[0], []).append(tag)
rendered_nav = render_to_string(nav_template_name, locals())
rendered_book_list = render_to_string(list_template_name, locals())
- return render_to_response(template_name, locals(),
- context_instance=RequestContext(request))
+ return render_to_response(template_name, locals(), context_instance=RequestContext(request))
def audiobook_list(request):
def collection(request, slug):
coll = get_object_or_404(models.Collection, slug=slug)
- return render(request, 'catalogue/collection.html',
- {'collection': coll})
+ return render(request, 'catalogue/collection.html', {'collection': coll})
def differentiate_tags(request, tags, ambiguous_slugs):
'url_args': '/'.join((beginning, tag.url_chunk, unparsed)).strip('/'),
'tags': [tag]
})
- return render_to_response('catalogue/differentiate_tags.html',
- {'tags': tags, 'options': options, 'unparsed': ambiguous_slugs[1:]},
- context_instance=RequestContext(request))
+ return render_to_response(
+ 'catalogue/differentiate_tags.html', {'tags': tags, 'options': options, 'unparsed': ambiguous_slugs[1:]},
+ context_instance=RequestContext(request))
# TODO: Rewrite this hellish piece of code which tries to do everything
# Ask the user to disambiguate
return differentiate_tags(request, e.tags, e.ambiguous_slugs)
except models.Tag.UrlDeprecationWarning, e:
- return HttpResponsePermanentRedirect(reverse('tagged_object_list', args=['/'.join(tag.url_chunk for tag in e.tags)]))
+ return HttpResponsePermanentRedirect(
+ reverse('tagged_object_list', args=['/'.join(tag.url_chunk for tag in e.tags)]))
try:
if len(tags) > settings.MAX_TAG_LIST:
fragments = fragments.filter(Q(book__in=books) | Q(book__ancestor__in=books))
categories = split_tags(
- models.Tag.objects.usage_for_queryset(fragments, counts=True
- ).exclude(pk__in=tags_pks),
- )
+ models.Tag.objects.usage_for_queryset(fragments, counts=True).exclude(pk__in=tags_pks),
+ )
objects = fragments
else:
if not gallery and not objects and len(tags) == 1:
tag = tags[0]
if (tag.category in ('theme', 'thing') and PictureArea.tagged.with_any([tag]).exists() or
- Picture.tagged.with_any([tag]).exists()):
- return redirect('tagged_object_list_gallery', raw_tags, permanent=False)
+ Picture.tagged.with_any([tag]).exists()):
+ return redirect('tagged_object_list_gallery', raw_tags, permanent=False)
- return render_to_response('catalogue/tagged_object_list.html',
+ return render_to_response(
+ 'catalogue/tagged_object_list.html',
{
'object_list': objects,
'categories': categories,
fragments = models.Fragment.tagged.with_all([theme]).filter(
Q(book=book) | Q(book__ancestor=book))
- return render_to_response('catalogue/book_fragments.html', locals(),
- context_instance=RequestContext(request))
+ return render_to_response('catalogue/book_fragments.html', locals(), context_instance=RequestContext(request))
def book_detail(request, slug):
tags = book.tags.exclude(category__in=('set', 'theme'))
book_children = book.children.all().order_by('parent_number', 'sort_key')
- return render_to_response('catalogue/book_detail.html', locals(),
- context_instance=RequestContext(request))
+ return render_to_response('catalogue/book_detail.html', locals(), context_instance=RequestContext(request))
def get_audiobooks(book):
extra_info = book.extra_info
- return render_to_response('catalogue/player.html', locals(),
- context_instance=RequestContext(request))
+ return render_to_response('catalogue/player.html', locals(), context_instance=RequestContext(request))
def book_text(request, slug):
if not book.has_html_file():
raise Http404
- return render_to_response('catalogue/book_text.html', locals(),
- context_instance=RequestContext(request))
+ return render_to_response('catalogue/book_text.html', locals(), context_instance=RequestContext(request))
# ==========
should be locale-aware """
names = {
- u'a':u'aąĄ', u'c':u'cćĆ', u'e':u'eęĘ', u'l': u'lłŁ', u'n':u'nńŃ', u'o':u'oóÓ', u's':u'sśŚ', u'z':u'zźżŹŻ',
- u'ą':u'ąĄ', u'ć':u'ćĆ', u'ę':u'ęĘ', u'ł': u'łŁ', u'ń':u'ńŃ', u'ó':u'óÓ', u'ś':u'śŚ', u'ź':u'źŹ', u'ż':u'żŻ'
+ u'a': u'aąĄ', u'c': u'cćĆ', u'e': u'eęĘ', u'l': u'lłŁ', u'n': u'nńŃ', u'o': u'oóÓ', u's': u'sśŚ',
+ u'z': u'zźżŹŻ',
+ u'ą': u'ąĄ', u'ć': u'ćĆ', u'ę': u'ęĘ', u'ł': u'łŁ', u'ń': u'ńŃ', u'ó': u'óÓ', u'ś': u'śŚ', u'ź': u'źŹ',
+ u'ż': u'żŻ'
}
+
def repl(m):
l = m.group()
return u"(%s)" % '|'.join(names[l])
+
return re.sub(u'[%s]' % (u''.join(names.keys())), repl, query)
+
def unicode_re_escape(query):
""" Unicode-friendly version of re.escape """
return re.sub(r'(?u)(\W)', r'\\\1', query)
+
def _word_starts_with(name, prefix):
"""returns a Q object getting models having `name` contain a word
starting with `prefix`
SQLite in Django uses Python re module
"""
- kwargs = {}
- kwargs['%s__iregex' % name] = _word_starts_with_regexp(prefix)
+ kwargs = {'%s__iregex' % name: _word_starts_with_regexp(prefix)}
return Q(**kwargs)
_word_starts_with = _sqlite_word_starts_with
-class App():
+class App:
def __init__(self, name, view):
self.name = name
self._view = view
self.lower = name.lower()
self.category = 'application'
+
def view(self):
return reverse(*self._view)
tags = tags.exclude(category='set')
prefix_regexp = re.compile(_word_starts_with_regexp(prefix))
- return list(books) + list(tags) + [app for app in _apps if prefix_regexp.search(app.lower)] + list(book_stubs) + list(authors)
+ return list(books) + list(tags) + [app for app in _apps if prefix_regexp.search(app.lower)] + list(book_stubs) + \
+ list(authors)
def _get_result_link(match, tag_list):
if isinstance(match, models.Tag):
return reverse('catalogue.views.tagged_object_list',
- kwargs={'tags': '/'.join(tag.url_chunk for tag in tag_list + [match])}
- )
+ kwargs={'tags': '/'.join(tag.url_chunk for tag in tag_list + [match])})
elif isinstance(match, App):
return match.view()
else:
authors = set(match.name.lower() for match in result
if isinstance(match, models.Tag) and match.category == 'author')
result = tuple(res for res in result if not (
- (isinstance(res, pdcounter_models.BookStub) and res.pretty_title().lower() in book_titles)
- or (isinstance(res, pdcounter_models.Author) and res.name.lower() in authors)
+ (isinstance(res, pdcounter_models.BookStub) and res.pretty_title().lower() in book_titles) or
+ (isinstance(res, pdcounter_models.Author) and res.name.lower() in authors)
))
exact_matches = tuple(res for res in result if res.name.lower() == query)
try:
tag_list = models.Tag.get_tag_list(tags)
- except:
+ except (models.Tag.DoesNotExist, models.Tag.MultipleObjectsReturned, models.Tag.UrlDeprecationWarning):
tag_list = []
try:
result = find_best_matches(prefix, request.user)
except ValueError:
- return render_to_response('catalogue/search_too_short.html', {'tags':tag_list, 'prefix':prefix},
+ return render_to_response(
+ 'catalogue/search_too_short.html', {'tags': tag_list, 'prefix': prefix},
context_instance=RequestContext(request))
if len(result) == 1:
return HttpResponseRedirect(_get_result_link(result[0], tag_list))
elif len(result) > 1:
- return render_to_response('catalogue/search_multiple_hits.html',
- {'tags':tag_list, 'prefix':prefix, 'results':((x, _get_result_link(x, tag_list), _get_result_type(x)) for x in result)},
+ return render_to_response(
+ 'catalogue/search_multiple_hits.html',
+ {
+ 'tags': tag_list, 'prefix': prefix,
+ 'results': ((x, _get_result_link(x, tag_list), _get_result_type(x)) for x in result)
+ },
context_instance=RequestContext(request))
else:
form = PublishingSuggestForm(initial={"books": prefix + ", "})
- return render_to_response('catalogue/search_no_hits.html',
- {'tags':tag_list, 'prefix':prefix, "pubsuggest_form": form},
+ return render_to_response(
+ 'catalogue/search_no_hits.html',
+ {'tags': tag_list, 'prefix': prefix, "pubsuggest_form": form},
context_instance=RequestContext(request))
tags_list = []
result = ""
for tag in _tags_starting_with(prefix, request.user):
- if not tag.name in tags_list:
+ if tag.name not in tags_list:
result += "\n" + tag.name
tags_list.append(tag.name)
return HttpResponse(result)
+
def json_tags_starting_with(request, callback=None):
# Callback for JSONP
prefix = request.GET.get('q', '')
return HttpResponse('')
tags_list = []
for tag in _tags_starting_with(prefix, request.user):
- if not tag.name in tags_list:
+ if tag.name not in tags_list:
tags_list.append(tag.name)
if request.GET.get('mozhint', ''):
result = [prefix, tags_list]
info = sys.exc_info()
exception = pprint.pformat(info[1])
tb = '\n'.join(traceback.format_tb(info[2]))
- return HttpResponse(_("An error occurred: %(exception)s\n\n%(tb)s") % {'exception':exception, 'tb':tb}, mimetype='text/plain')
+ return HttpResponse(
+ _("An error occurred: %(exception)s\n\n%(tb)s") % {'exception': exception, 'tb': tb},
+ mimetype='text/plain')
return HttpResponse(_("Book imported successfully"))
else:
return HttpResponse(_("Error importing file: %r") % book_import_form.errors)
# info views for API
-def book_info(request, id, lang='pl'):
- book = get_object_or_404(models.Book, id=id)
+def book_info(request, book_id, lang='pl'):
+ book = get_object_or_404(models.Book, id=book_id)
# set language by hand
translation.activate(lang)
- return render_to_response('catalogue/book_info.html', locals(),
- context_instance=RequestContext(request))
+ return render_to_response('catalogue/book_info.html', locals(), context_instance=RequestContext(request))
-def tag_info(request, id):
- tag = get_object_or_404(models.Tag, id=id)
+def tag_info(request, tag_id):
+ tag = get_object_or_404(models.Tag, id=tag_id)
return HttpResponse(tag.description)
def download_zip(request, format, slug=None):
- url = None
if format in models.Book.ebook_formats:
url = models.Book.zip_format(format)
elif format in ('mp3', 'ogg') and slug is not None:
@ssi_included
def book_mini(request, pk, with_link=True):
book = get_object_or_404(models.Book, pk=pk)
- author_str = ", ".join(tag.name
- for tag in book.tags.filter(category='author'))
+ author_str = ", ".join(tag.name for tag in book.tags.filter(category='author'))
return render(request, 'catalogue/book_mini_box.html', {
'book': book,
'author_str': author_str,
})
-@ssi_included(get_ssi_vars=lambda pk: book_short.get_ssi_vars(pk) +
+@ssi_included(
+ get_ssi_vars=lambda pk: book_short.get_ssi_vars(pk) +
(lambda ipk: (
('social_tags.choose_cite', [ipk]),
('catalogue_tags.choose_fragment', [ipk], {
@ssi_included
def fragment_short(request, pk):
fragment = get_object_or_404(models.Fragment, pk=pk)
- return render(request, 'catalogue/fragment_short.html',
- {'fragment': fragment})
+ return render(request, 'catalogue/fragment_short.html', {'fragment': fragment})
@ssi_included
def fragment_promo(request, pk):
fragment = get_object_or_404(models.Fragment, pk=pk)
- return render(request, 'catalogue/fragment_promo.html', {
- 'fragment': fragment
- })
+ return render(request, 'catalogue/fragment_promo.html', {'fragment': fragment})
@ssi_included
+# -*- 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.contrib import admin
from chunks.models import Chunk, Attachment
+# -*- 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.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
A Chunk is a piece of content associated with a unique key that can be inserted into
any template with the use of a special template tag.
"""
- key = models.CharField(_('key'), help_text=_('A unique name for this chunk of content'), primary_key=True, max_length=255)
+ key = models.CharField(_('key'), help_text=_('A unique name for this chunk of content'), primary_key=True,
+ max_length=255)
description = models.CharField(_('description'), blank=True, max_length=255)
content = models.TextField(_('content'), blank=True)
for lang in [lc for (lc, _ln) in settings.LANGUAGES]])
-
class Attachment(models.Model):
key = models.CharField(_('key'), help_text=_('A unique name for this attachment'), primary_key=True, max_length=255)
attachment = models.FileField(upload_to='chunks/attachment')
from modeltranslation.translator import translator, TranslationOptions
from chunks.models import Chunk
+
class ChunkTranslationOptions(TranslationOptions):
fields = ('content',)
#
from django.conf.urls import patterns, url
-urlpatterns = patterns('chunks.views',
+urlpatterns = patterns(
+ 'chunks.views',
url(r'^chunk/(?P<key>.+)\.(?P<lang>.+)\.html$', 'chunk', name='chunk'),
)
from ssify import ssi_included
from .models import Chunk
+
@ssi_included
def chunk(request, key):
chunk, created = Chunk.objects.get_or_create(key=key)
from sortify import sortify
from celery.utils.log import get_task_logger
-task_logger = get_task_logger(__name__)
-
from catalogue.models import Book
+task_logger = get_task_logger(__name__)
+
class Qualifier(models.Model):
qualifier = models.CharField(max_length=128, db_index=True, unique=True)
sort_key = sortify(text_str).strip()[:128]
language = book.language
- note = None
- notes = Note.objects.filter(sort_key=sort_key,
- fn_type=fn_type,
- language=language, html=html_str)
+ notes = Note.objects.filter(sort_key=sort_key, fn_type=fn_type, language=language, html=html_str)
if notes:
note = notes[0]
else:
)
def test_book_with_footnote(self):
- BOOK_TEXT = """<utwor>
+ 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>
</opowiadanie></utwor>
"""
- book = Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+ book = Book.from_text_and_meta(ContentFile(book_text), self.book_info)
self.assertEqual(
len(self.client.get('/przypisy/').context['object_list']),
len(self.client.get('/przypisy/?qual=techn.').context['object_list']),
1,
'There should be a note qualified with \'techn.\' qualifier.')
-
from django.conf.urls import patterns, url
from dictionary.views import NotesView
-urlpatterns = patterns('dictionary.views',
+urlpatterns = patterns(
+ 'dictionary.views',
url(r'^$', NotesView.as_view(), name='dictionary_notes'),
)
-
#
from django.views.generic.list import ListView
from django.conf import settings
-from django.db.models import Count, Q
+from django.db.models import Q
from catalogue.constants import LANGUAGES_3TO2
from .constants import FN_TYPES
from .models import Note, Qualifier
class NotesView(ListView):
+ def __init__(self, **kwargs):
+ super(NotesView, self).__init__(**kwargs)
+ self.qualifier = None
+ self.qualifiers = None
+ self.language = None
+ self.languages = None
+ self.fn_type = None
+ self.fn_types = None
+ self.letter = None
+ self.letters = None
+
def get_queryset(self):
objects = Note.objects.all()
filters = {}
try:
self.qualifier = Qualifier.objects.get(qualifier=self.request.GET.get('qual'))
except Qualifier.DoesNotExist:
- self.qualifier = None
+ pass
else:
filters['qualifier'] = Q(qualifiers=self.qualifier)
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]")
+ # 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)
+ # filters['letter'] = Q(sort_key__startswith=self.letter)
self.letters = ["0-9"] + [chr(a) for a in range(ord('a'), ord('z')+1)]
list_filter = ['offer']
-
class PayedFilter(admin.SimpleListFilter):
title = _('payment complete')
parameter_name = 'payed'
+
def lookups(self, request, model_admin):
return (
('yes', _('Yes')),
('no', _('No')),
)
+
def queryset(self, request, queryset):
if self.value() == 'yes':
return queryset.exclude(payed_at=None)
elif self.value() == 'no':
return queryset.filter(payed_at=None)
+
class PerksFilter(admin.SimpleListFilter):
title = _('perks')
parameter_name = 'perks'
+
def lookups(self, request, model_admin):
return (
('yes', _('Yes')),
('no', _('No')),
)
+
def queryset(self, request, queryset):
if self.value() == 'yes':
return queryset.exclude(perks=None)
list_filter = [PayedFilter, 'offer', PerksFilter]
-
class SpentAdmin(admin.ModelAdmin):
model = Spent
list_display = ['book', 'amount', 'timestamp']
class FundingForm(forms.Form):
required_css_class = 'required'
- amount = forms.DecimalField(label=_("Amount"), decimal_places=2,
- widget=PerksAmountWidget())
- name = forms.CharField(label=_("Name"), required=False,
- help_text=_("Optional name for public list of contributors"))
- email = forms.EmailField(label=_("Contact e-mail"),
- help_text=_("We'll use it to contact you about the <strong>details needed for your perks</strong>,<br/>"
+ amount = forms.DecimalField(label=_("Amount"), decimal_places=2, widget=PerksAmountWidget())
+ name = forms.CharField(
+ label=_("Name"), required=False, help_text=_("Optional name for public list of contributors"))
+ email = forms.EmailField(
+ label=_("Contact e-mail"),
+ help_text=_(
+ "We'll use it to contact you about the <strong>details needed for your perks</strong>,<br/>"
"and to send you updates about your payment and the fundraiser status (which you can always turn off).<br/>"
"Your e-mail won't be publicised."), required=False)
)
funding.perks = funding.offer.get_perks(funding.amount)
return funding
-
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
- make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
- help='Suppress output'),
+ make_option('-q', '--quiet', action='store_false', dest='verbose', default=True, help='Suppress output'),
)
help = 'Sends relevant funding notifications.'
from django.core.urlresolvers import reverse
from django.core.mail import send_mail
from django.db import models
+from django.dispatch import receiver
from django.template.loader import render_to_string
from django.utils.timezone import utc
from django.utils.translation import ugettext_lazy as _, override
start = models.DateField(_('start'), db_index=True)
end = models.DateField(_('end'), db_index=True)
redakcja_url = models.URLField(_('redakcja URL'), blank=True)
- book = models.ForeignKey(Book, null=True, blank=True,
- help_text=_('Published book.'))
+ book = models.ForeignKey(Book, null=True, blank=True, help_text=_('Published book.'))
cover = models.ImageField(_('Cover'), upload_to='funding/covers')
poll = models.ForeignKey(Poll, help_text=_('Poll'), null=True, blank=True, on_delete=models.SET_NULL)
return reverse('funding_offer', args=[self.slug])
def save(self, *args, **kw):
- published_now = (self.book_id is not None and
- self.pk is not None and
+ published_now = (
+ self.book_id is not None and self.pk is not None and
type(self).objects.values('book').get(pk=self.pk)['book'] != self.book_id)
retval = super(Offer, self).save(*args, **kw)
self.flush_includes()
)
def notify_end(self, force=False):
- if not force and self.notified_end: return
+ if not force and self.notified_end:
+ return
assert not self.is_current()
self.notify_all(
_('The fundraiser has ended!'),
self.save()
def notify_near(self, force=False):
- if not force and self.notified_near: return
+ if not force and self.notified_near:
+ return
assert self.is_current()
sum_ = self.sum()
need = self.target - sum_
return ", ".join(perk.name for perk in self.perks.all())
def get_disable_notifications_url(self):
- return "%s?%s" % (reverse("funding_disable_notifications"),
+ return "%s?%s" % (
+ reverse("funding_disable_notifications"),
urlencode({
'email': self.email,
'key': self.notify_key,
return ret
@classmethod
- def notify_funders(cls, subject, template_name, extra_context=None,
- query_filter=None, payed_only=True):
+ def notify_funders(cls, subject, template_name, extra_context=None, query_filter=None, payed_only=True):
funders = cls.objects.exclude(email="").filter(notifications=True)
if payed_only:
funders = funders.exclude(payed_at=None)
if extra_context:
context.update(extra_context)
with override(self.language_code or app_settings.DEFAULT_LANGUAGE):
- send_mail(subject,
- render_to_string(template_name, context),
- settings.CONTACT_EMAIL,
- [self.email],
- fail_silently=False
- )
+ send_mail(
+ subject, render_to_string(template_name, context), settings.CONTACT_EMAIL, [self.email],
+ fail_silently=False)
def disable_notifications(self):
"""Disables all notifications for this e-mail address."""
return u"Spent: %s" % unicode(self.book)
+@receiver(getpaid.signals.new_payment_query)
def new_payment_query_listener(sender, order=None, payment=None, **kwargs):
""" Set payment details for getpaid. """
payment.amount = order.amount
payment.currency = 'PLN'
-getpaid.signals.new_payment_query.connect(new_payment_query_listener)
+@receiver(getpaid.signals.user_data_query)
def user_data_query_listener(sender, order, user_data, **kwargs):
""" Set user data for payment. """
user_data['email'] = order.email
-getpaid.signals.user_data_query.connect(user_data_query_listener)
+
+@receiver(getpaid.signals.payment_status_changed)
def payment_status_changed_listener(sender, instance, old_status, new_status, **kwargs):
""" React to status changes from getpaid. """
if old_status != 'paid' and new_status == 'paid':
_('Thank you for your support!'),
'funding/email/thanks.txt'
)
-getpaid.signals.payment_status_changed.connect(payment_status_changed_listener)
set())
self.assertEqual(
set(self.offer1.get_perks(70)),
- set([perk, perk1]))
+ {perk, perk1})
class FundingTest(TestCase):
self.assertEqual(Offer.current(), self.offer_current)
self.assertEqual(
set(Offer.past()),
- set([self.offer_past])
+ {self.offer_past}
)
self.assertEqual(
set(Offer.public()),
- set([self.offer_past, self.offer_current])
+ {self.offer_past, self.offer_current}
)
def test_interrupt(self):
self.assertEqual(Offer.current(), offer_interrupt)
self.assertEqual(
set(Offer.past()),
- set([self.offer_past, self.offer_current])
+ {self.offer_past, self.offer_current}
)
self.assertEqual(
set(Offer.public()),
- set([self.offer_past, self.offer_current, offer_interrupt])
+ {self.offer_past, self.offer_current, offer_interrupt}
)
-
#
from django.conf.urls import patterns, url, include
-from .views import (WLFundView, OfferDetailView, OfferListView,
- ThanksView, NoThanksView, CurrentView, DisableNotifications)
+from .views import WLFundView, OfferDetailView, OfferListView, ThanksView, NoThanksView, CurrentView, \
+ DisableNotifications
-urlpatterns = patterns('funding.views',
-
+urlpatterns = patterns(
+ 'funding.views',
url(r'^$', CurrentView.as_view(), name='funding_current'),
url(r'^teraz/$', CurrentView.as_view()),
url(r'^teraz/(?P<slug>[^/]+)/$', CurrentView.as_view(), name='funding_current'),
"".join(set(string.punctuation) - set('\\'))
)
+
def replace_char(m):
char = m.group()
return char_map.get(char, '')
+
def sanitize_payment_title(value):
- return re.sub('[^%s]{1}' % sane_in_payu_title, replace_char, unicode(value))
+ return re.sub('[^%s]' % sane_in_payu_title, replace_char, unicode(value))
else:
return form_class(self.object, initial={'amount': app_settings.DEFAULT_AMOUNT})
- def get_context_data(self, *args, **kwargs):
- ctx = super(OfferDetailView, self).get_context_data(*args, **kwargs)
+ def get_context_data(self, **kwargs):
+ ctx = super(OfferDetailView, self).get_context_data(**kwargs)
ctx['object'] = self.object
ctx['page'] = self.request.GET.get('page', 1)
if self.object.is_current():
class OfferListView(ListView):
queryset = Offer.public()
- def get_context_data(self, *args, **kwargs):
- ctx = super(OfferListView, self).get_context_data(*args, **kwargs)
+ def get_context_data(self, **kwargs):
+ ctx = super(OfferListView, self).get_context_data(**kwargs)
ctx['funding_no_show_current'] = True
return ctx
class ThanksView(TemplateView):
template_name = "funding/thanks.html"
- def get_context_data(self, *args, **kwargs):
- ctx = super(ThanksView, self).get_context_data(*args, **kwargs)
+ def get_context_data(self, **kwargs):
+ ctx = super(ThanksView, self).get_context_data(**kwargs)
ctx['offer'] = Offer.current()
ctx['funding_no_show_current'] = True
return ctx
@csrf_exempt
def dispatch(self, request):
- self.object = get_object_or_404(Funding,
- email=request.GET.get('email'), notify_key=request.GET.get('key'))
+ self.object = get_object_or_404(Funding, email=request.GET.get('email'), notify_key=request.GET.get('key'))
return super(DisableNotifications, self).dispatch(request)
def post(self, *args, **kwargs):
@ssi_included(patch_response=[ssi_cache_control(must_revalidate=True)])
def top_bar(request, pk):
- return offer_bar(request, pk,
- link=True, closeable=True, add_class="funding-top-header")
+ return offer_bar(request, pk, link=True, closeable=True, add_class="funding-top-header")
@ssi_included(patch_response=[ssi_cache_control(must_revalidate=True)])
def list_bar(request, pk):
- return offer_bar(request, pk,
- link=True, show_title_calling=False)
+ return offer_bar(request, pk, link=True, show_title_calling=False)
@ssi_included(patch_response=[ssi_cache_control(must_revalidate=True)])
def detail_bar(request, pk):
- return offer_bar(request, pk,
- show_title=False)
+ return offer_bar(request, pk, show_title=False)
@ssi_included(patch_response=[ssi_cache_control(must_revalidate=True)])
def offer_status(request, pk):
offer = get_object_or_404(Offer, pk=pk)
- return render(request, "funding/includes/offer_status.html", {
- 'offer': offer,
- })
+ return render(request, "funding/includes/offer_status.html", {'offer': offer})
@ssi_included(patch_response=[ssi_cache_control(must_revalidate=True)])
def offer_status_more(request, pk):
offer = get_object_or_404(Offer, pk=pk)
- return render(request, "funding/includes/offer_status_more.html", {
- 'offer': offer,
- })
+ return render(request, "funding/includes/offer_status_more.html", {'offer': offer})
@ssi_included(patch_response=[ssi_cache_control(must_revalidate=True)])
# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
-from decimal import Decimal
+from decimal import Decimal, DecimalException
from django import forms
from django.template.loader import render_to_string
class PerksAmountWidget(forms.Textarea):
- def perks_input_name(self, name):
+ @staticmethod
+ def perks_input_name(name):
return "_%s_perk" % name
def render(self, name, value, attrs=None):
try:
value = Decimal(value)
- except:
+ except DecimalException:
pass
perks = list(self.form_instance.offer.get_perks())
perk_chosen = False
from modeltranslation.admin import TranslationAdmin
from infopages.models import InfoPage
+
class InfoPageAdmin(TranslationAdmin):
list_display = ('title', 'slug', 'main_page')
from django.db import models
from django.utils.translation import ugettext_lazy as _
+
class InfoPage(models.Model):
"""An InfoPage is used to display a two-column flatpage."""
@models.permalink
def get_absolute_url(self):
- return ('infopage', [self.slug])
+ return 'infopage', [self.slug]
from modeltranslation.translator import translator, TranslationOptions
from infopages.models import InfoPage
+
class InfoPageTranslationOptions(TranslationOptions):
fields = ('title', 'left_column', 'right_column')
from django.conf.urls import patterns, url
-urlpatterns = patterns('infopages.views',
+urlpatterns = patterns(
+ 'infopages.views',
url(r'^(?P<slug>[a-zA-Z0-9_-]+)/$', 'infopage', name='infopage'),
)
-
except TemplateSyntaxError:
left_column = ''
- return render_to_response('infopages/infopage.html', locals(),
- context_instance=RequestContext(request))
+ return render_to_response('infopages/infopage.html', locals(), context_instance=RequestContext(request))
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('-t', '--tags', dest='tags', metavar='SLUG,...',
- help='Use only books tagged with this tags'),
+ help='Use only books tagged with this tags'),
make_option('-i', '--include', dest='include', metavar='SLUG,...',
- help='Include specific books by slug'),
+ help='Include specific books by slug'),
make_option('-e', '--exclude', dest='exclude', metavar='SLUG,...',
- help='Exclude specific books by slug')
+ help='Exclude specific books by slug')
)
help = 'Prepare data for Lesmianator.'
try:
path = settings.LESMIANATOR_PICKLE
- except:
+ except AttributeError:
print self.style.ERROR('LESMIANATOR_PICKLE not set in the settings.')
return
try:
dump(lesmianator, open(path, 'w'))
- except:
+ except IOError:
print self.style.ERROR("Couldn't write to $s" % path)
return
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
import cPickle
+from cPickle import PickleError
from datetime import datetime
from random import randint
from StringIO import StringIO
f = open(settings.LESMIANATOR_PICKLE)
global_dictionary = cPickle.load(f)
f.close()
- except:
+ except (IOError, AttributeError, PickleError):
global_dictionary = {}
def visit(self):
@classmethod
def get(cls, sth):
object_type = ContentType.objects.get_for_model(sth)
- should_keys = set([sth.id])
+ should_keys = {sth.id}
if isinstance(sth, Tag):
should_keys = set(b.pk for b in Book.tagged.with_any((sth,)).iterator())
try:
c.pickle.save(sth.slug+'.p', ContentFile(cPickle.dumps((should_keys, conts))))
c.save()
return conts
-
-
#
from django.conf.urls import url, patterns
-urlpatterns = patterns('lesmianator.views',
+urlpatterns = patterns(
+ 'lesmianator.views',
url(r'^$', 'main_page', name='lesmianator'),
url(r'^wiersz/$', 'new_poem', name='new_poem'),
url(r'^lektura/(?P<slug>[a-z0-9-]+)/$', 'poem_from_book', name='poem_from_book'),
url(r'^polka/(?P<shelf>[a-zA-Z0-9-]+)/$', 'poem_from_set', name='poem_from_set'),
url(r'^wiersz/(?P<poem>[a-zA-Z0-9-]+)/$', 'get_poem', name='get_poem'),
)
-
last = Poem.objects.all().order_by('-created_at')[:10]
shelves = Tag.objects.filter(user__username='lesmianator')
- return render_to_response('lesmianator/lesmianator.html',
- {"last": last, "shelves": shelves},
- context_instance=RequestContext(request))
+ return render_to_response(
+ 'lesmianator/lesmianator.html',
+ {"last": last, "shelves": shelves},
+ context_instance=RequestContext(request))
@cache.never_cache
p = Poem(slug=get_random_hash(text), text=text, created_by=user)
p.save()
- return render_to_response('lesmianator/poem.html',
- {"poem": p},
- context_instance=RequestContext(request))
+ return render_to_response(
+ 'lesmianator/poem.html',
+ {"poem": p},
+ context_instance=RequestContext(request))
@cache.never_cache
p.created_from = [book.id]
p.save()
- return render_to_response('lesmianator/poem.html',
- {"poem": p, "books": [book], "book": book},
- context_instance=RequestContext(request))
+ return render_to_response(
+ 'lesmianator/poem.html',
+ {"poem": p, "books": [book], "book": book},
+ context_instance=RequestContext(request))
@cache.never_cache
book = books[0] if len(books) == 1 else None
- return render_to_response('lesmianator/poem.html',
- {"poem": p, "shelf": tag, "books": books, "book": book},
- context_instance=RequestContext(request))
+ return render_to_response(
+ 'lesmianator/poem.html',
+ {"poem": p, "shelf": tag, "books": books, "book": book},
+ context_instance=RequestContext(request))
+
def get_poem(request, poem):
p = get_object_or_404(Poem, slug=poem)
else:
books = book = None
- return render_to_response('lesmianator/poem.html',
- {"poem": p, "books": books, "book": book},
- context_instance=RequestContext(request))
-
-
+ return render_to_response(
+ 'lesmianator/poem.html',
+ {"poem": p, "books": books, "book": book},
+ context_instance=RequestContext(request))
\r
@models.permalink\r
def get_absolute_url(self):\r
- return ('libraries_catalog_view', [self.slug])\r
+ return 'libraries_catalog_view', [self.slug]\r
\r
\r
class Library(models.Model):\r
"""Represent a single library in the libraries dictionary"""\r
\r
- name = models.CharField(_('name'), max_length=120, blank=True)
- slug = models.SlugField(_('slug'), max_length=120, unique=True, db_index=True, null=True)
+ name = models.CharField(_('name'), max_length=120, blank=True)\r
+ slug = models.SlugField(_('slug'), max_length=120, unique=True, db_index=True, null=True)\r
catalog = models.ForeignKey(Catalog, null=False, related_name='libraries', on_delete=models.PROTECT)\r
- url = models.CharField(_('url'), max_length=120, blank=True)
+ url = models.CharField(_('url'), max_length=120, blank=True)\r
description = models.TextField(_('description'), blank=True)\r
\r
class Meta:\r
from django.conf.urls import patterns, url
-urlpatterns = patterns('libraries.views',
+urlpatterns = patterns(
+ 'libraries.views',
url(r'^$', 'main_view', name='libraries_main_view'),
url(r'^(?P<slug>[a-zA-Z0-9_-]+)$', 'catalog_view', name='libraries_catalog_view'),
url(r'^(?P<catalog_slug>[a-zA-Z0-9_-]+)/(?P<slug>[a-zA-Z0-9_-]+)$', 'library_view', name='libraries_library_view'),
context['catalogs'] = Catalog.objects.all()\r
return render_to_response('libraries/main_view.html', context_instance=context)\r
\r
+\r
def catalog_view(request, slug):\r
context = RequestContext(request)\r
context['catalog'] = get_object_or_404(Catalog.objects.filter(slug=slug).select_related())\r
return render_to_response('libraries/catalog_view.html', context_instance=context)\r
- \r
+\r
+\r
def library_view(request, catalog_slug, slug):\r
context = RequestContext(request)\r
context['library'] = get_object_or_404(Library.objects.filter(slug=slug).filter(catalog__slug=catalog_slug))\r
"""
def _media(self):
from django.conf import settings
- js = ['js/SelectBox.js' , 'js/SelectFilter2.js']
+ js = ['js/SelectBox.js', 'js/SelectFilter2.js']
return forms.Media(js=['%sadmin/%s' % (settings.STATIC_URL, url) for url in js])
media = property(_media)
def render(self, name, value, attrs=None, choices=()):
from django.conf import settings
- output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
- output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
+ output = [
+ super(FilteredSelectMultiple, self).render(name, value, attrs, choices),
+ u'<script type="text/javascript">addEvent(window, "load", function(e) {',
+ u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % (
+ name, self.verbose_name.replace('"', '\\"'),
+ int(self.is_stacked), settings.STATIC_URL + "admin/")
+ ]
# TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically.
- output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % \
- (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.STATIC_URL + "admin/"))
return mark_safe(u''.join(output))
class TaggableModelForm(forms.ModelForm):
- tags = forms.MultipleChoiceField(label=_('tags').capitalize(), required=False, widget=FilteredSelectMultiple(_('tags'), False))
+ tags = forms.MultipleChoiceField(
+ label=_('tags').capitalize(), required=False,
+ widget=FilteredSelectMultiple(_('tags'), is_stacked=False))
def __init__(self, *args, **kwargs):
if 'instance' in kwargs:
form = super(TaggableModelAdmin, self).get_form(request, obj)
form.tag_model = self.tag_model
return form
-
def __del__(self, instance):
self.tag_model.objects.update_tags(instance, [])
-
qn = connection.ops.quote_name
-
tags_updated = Signal(providing_args=["affected_tags"])
+
def get_queryset_and_model(queryset_or_model):
"""
Given a ``QuerySet`` or a ``Model``, returns a two-tuple of
try:
return queryset_or_model, queryset_or_model.model
except AttributeError:
- return queryset_or_model._default_manager.all(), queryset_or_model
+ return queryset_or_model.objects.all(), queryset_or_model
############
updated_tags = self.model.get_tag_list(tags)
# Remove tags which no longer apply
- tags_for_removal = [tag for tag in current_tags \
- if tag not in updated_tags]
+ tags_for_removal = [tag for tag in current_tags if tag not in updated_tags]
if len(tags_for_removal):
- self.intermediary_table_model._default_manager.filter(content_type__pk=content_type.pk,
- object_id=obj.pk,
- tag__in=tags_for_removal).delete()
+ self.intermediary_table_model.objects.filter(
+ content_type__pk=content_type.pk,
+ object_id=obj.pk,
+ tag__in=tags_for_removal).delete()
# Add new tags
tags_to_add = [tag for tag in updated_tags
if tag not in current_tags]
for tag in tags_to_add:
if tag not in current_tags:
- self.intermediary_table_model._default_manager.create(tag=tag, content_object=obj)
+ self.intermediary_table_model.objects.create(tag=tag, content_object=obj)
tags_updated.send(sender=type(obj), instance=obj, affected_tags=tags_to_add + tags_for_removal)
Remove tag from an object.
"""
content_type = ContentType.objects.get_for_model(obj)
- self.intermediary_table_model._default_manager.filter(content_type__pk=content_type.pk,
- object_id=obj.pk, tag=tag).delete()
+ self.intermediary_table_model.objects.filter(
+ content_type__pk=content_type.pk, object_id=obj.pk, tag=tag).delete()
def get_for_object(self, obj):
"""
``filters`` argument.
"""
# TODO: Do we really need this filters stuff?
- if filters is None: filters = {}
+ if filters is None:
+ filters = {}
- queryset = model._default_manager.filter()
+ queryset = model.objects.filter()
for f in filters.items():
queryset.query.add_filter(f)
usage = self.usage_for_queryset(queryset, counts)
each tag, indicating how many times it has been used against
the Model class in question.
"""
- usage = self.model._default_manager.filter(
+ usage = self.model.objects.filter(
items__content_type=ContentType.objects.get_for_model(queryset.model),
- items__object_id__in=queryset
- )
+ items__object_id__in=queryset)
if counts:
usage = usage.annotate(count=models.Count('id'))
else:
class TagMeta(ModelBase):
- "Metaclass for tag models (models inheriting from TagBase)."
+ """Metaclass for tag models (models inheriting from TagBase)."""
def __new__(mcs, name, bases, attrs):
model = super(TagMeta, mcs).__new__(mcs, name, bases, attrs)
if not model._meta.abstract:
return [tag_list]
else:
return tag_list
-
from django.views.generic import ListView
-def tagged_object_list(request, queryset_or_model=None, tag_model=None, tags=None,
- related_tags=False, related_tag_counts=True, **kwargs):
+def tagged_object_list(request, queryset_or_model=None, tag_model=None, tags=None, related_tags=False,
+ related_tag_counts=True, **kwargs):
"""
A thin wrapper around
``django.views.generic.list_detail.object_list`` which creates a
if tag_instances is None:
raise Http404(_('No tags found matching "%s".') % tags)
queryset = tag_model.intermediary_table_model.objects.get_by_model(queryset_or_model, tag_instances)
- if not kwargs.has_key('extra_context'):
+ if 'extra_context' not in kwargs:
kwargs['extra_context'] = {}
kwargs['extra_context']['tags'] = tag_instances
if related_tags:
kwargs['extra_context']['related_tags'] = \
- tag_model.objects.related_for_model(tag_instances, queryset_or_model,
- counts=related_tag_counts)
+ tag_model.objects.related_for_model(tag_instances, queryset_or_model, counts=related_tag_counts)
return ListView.as_view(queryset=queryset)(request, **kwargs)
-
from api.models import Deleted
from api.handlers import WL_BASE
from librarian import WLURI
-from django.contrib.contenttypes.models import ContentType
from datetime import datetime
from lxml import etree
from django.conf import settings
from django.utils import timezone
-make_time_naive = lambda d: timezone.localtime(d).replace(tzinfo=None)
+def make_time_naive(d):
+ return timezone.localtime(d).replace(tzinfo=None)
WL_DC_READER_XPATH = '(.|*)/rdf:RDF/rdf:Description/%s/text()'
wl_dc_reader = metadata.MetadataReader(
fields={
- 'title': ('textList', WL_DC_READER_XPATH % 'dc:title'),
- 'creator': ('textList', WL_DC_READER_XPATH % 'dc:creator'),
- 'subject': ('textList', (WL_DC_READER_XPATH + ' | ' + WL_DC_READER_XPATH + ' | ' + WL_DC_READER_XPATH) %
- ('dc:subject.period', 'dc:subject.type', 'dc:subject.genre')),
- 'description': ('textList', WL_DC_READER_XPATH % 'dc:description'),
- 'publisher': ('textList', WL_DC_READER_XPATH % 'dc:publisher'),
- 'contributor': ('textList', (WL_DC_READER_XPATH + ' | ' + WL_DC_READER_XPATH + ' | ' + WL_DC_READER_XPATH) %
- ('dc:contributor.editor', 'dc:contributor.translator', 'dc:contributor.technical_editor')),
- 'date': ('textList', WL_DC_READER_XPATH % 'dc:date'),
- 'type': ('textList', WL_DC_READER_XPATH % 'dc:type'),
- 'format': ('textList', WL_DC_READER_XPATH % 'dc:format'),
- 'identifier': ('textList', WL_DC_READER_XPATH % 'dc:identifier.url'),
- 'source': ('textList', WL_DC_READER_XPATH % 'dc:source'),
- 'language': ('textList', WL_DC_READER_XPATH % 'dc:language'),
- #'isPartOf': ('textList', 'rdf:RDF/rdf:Description/dc:relation.isPartOf/text()'),
- 'hasPart': ('textList', WL_DC_READER_XPATH % 'dc:relation.hasPart'),
- # 'relation': ('textList', 'rdf:RDF/rdf:Description/dc:relation/text()'),
- # 'coverage': ('textList', 'rdf:RDF/rdf:Description/dc:coverage/text()'),
- 'rights': ('textList', WL_DC_READER_XPATH % 'dc:rights')
+ 'title': ('textList', WL_DC_READER_XPATH % 'dc:title'),
+ 'creator': ('textList', WL_DC_READER_XPATH % 'dc:creator'),
+ 'subject': ('textList', (WL_DC_READER_XPATH + ' | ' + WL_DC_READER_XPATH + ' | ' + WL_DC_READER_XPATH) %
+ ('dc:subject.period', 'dc:subject.type', 'dc:subject.genre')),
+ 'description': ('textList', WL_DC_READER_XPATH % 'dc:description'),
+ 'publisher': ('textList', WL_DC_READER_XPATH % 'dc:publisher'),
+ 'contributor': ('textList', (WL_DC_READER_XPATH + ' | ' + WL_DC_READER_XPATH + ' | ' + WL_DC_READER_XPATH) %
+ ('dc:contributor.editor', 'dc:contributor.translator', 'dc:contributor.technical_editor')),
+ 'date': ('textList', WL_DC_READER_XPATH % 'dc:date'),
+ 'type': ('textList', WL_DC_READER_XPATH % 'dc:type'),
+ 'format': ('textList', WL_DC_READER_XPATH % 'dc:format'),
+ 'identifier': ('textList', WL_DC_READER_XPATH % 'dc:identifier.url'),
+ 'source': ('textList', WL_DC_READER_XPATH % 'dc:source'),
+ 'language': ('textList', WL_DC_READER_XPATH % 'dc:language'),
+ # 'isPartOf': ('textList', 'rdf:RDF/rdf:Description/dc:relation.isPartOf/text()'),
+ 'hasPart': ('textList', WL_DC_READER_XPATH % 'dc:relation.hasPart'),
+ # 'relation': ('textList', 'rdf:RDF/rdf:Description/dc:relation/text()'),
+ # 'coverage': ('textList', 'rdf:RDF/rdf:Description/dc:coverage/text()'),
+ 'rights': ('textList', WL_DC_READER_XPATH % 'dc:rights')
},
namespaces={
- 'dc': 'http://purl.org/dc/elements/1.1/',
- 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'}
+ 'dc': 'http://purl.org/dc/elements/1.1/',
+ 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'}
)
return '{%s}%s' % (NS_DCTERMS, name)
+# WTF
class Catalogue(common.ResumptionOAIPMH):
TAG_CATEGORIES = ['author', 'epoch', 'kind', 'genre']
year_zero = timezone.make_aware(datetime(1990, 1, 1, 0, 0, 0), timezone.utc)
try:
- earliest_change = \
- Book.objects.order_by('changed_at')[0].changed_at
- except: earliest_change = year_zero
+ earliest_change = Book.objects.order_by('changed_at')[0].changed_at
+ except IndexError:
+ earliest_change = year_zero
try:
earliest_delete = \
Deleted.objects.exclude(slug__exact=u'').ordery_by('deleted_at')[0].deleted_at
- except: earliest_delete = year_zero
-
- self.earliest_datestamp = earliest_change <= earliest_delete and \
- earliest_change or earliest_delete
-
- def metadata(self, book):
- try:
- xml = etree.parse(book.xml_file)
- finally:
- book.xml_file.close()
- md = wl_dc_reader(xml)
- m = md.getMap()
- if book.parent:
- m['isPartOf'] = [str(WLURI.from_slug(book.parent.slug))]
- return m
-
- def record_for_book(self, book, headers_only=False):
- meta = None
- identifier = self.slug_to_identifier(book.slug)
- if isinstance(book, Book):
- # setSpec = map(self.tag_to_setspec, book.tags.filter(category__in=self.TAG_CATEGORIES))
- header = common.Header(identifier, make_time_naive(book.changed_at), [], False)
- if not headers_only:
- meta = common.Metadata(self.metadata(book))
- about = None
- elif isinstance(book, Deleted):
- header = common.Header(identifier, make_time_naive(book.deleted_at), [], True)
- if not headers_only:
- meta = common.Metadata({})
- about = None
- if headers_only:
- return header
- return header, meta, about
-
- def identify(self, **kw):
- ident = common.Identify(
- 'Wolne Lektury', # generate
- '%s/oaipmh' % unicode(WL_BASE), # generate
- '2.0', # version
- [m[1] for m in settings.MANAGERS], # adminEmails
- make_time_naive(self.earliest_datestamp), # earliest datestamp of any change
- 'persistent', # deletedRecord
- 'YYYY-MM-DDThh:mm:ssZ', # granularity
- ['identity'], # compression
- [] # descriptions
- )
- return ident
-
- def books(self, tag, from_, until):
- if tag:
- # we do not support sets, since they are problematic for deleted books.
- raise error.NoSetHierarchyError("Wolne Lektury does not support sets.")
- # books = Book.tagged.with_all([tag])
- else:
- books = Book.objects.all()
- deleted = Deleted.objects.exclude(slug__exact=u'')
-
- books = books.order_by('changed_at')
- deleted = deleted.order_by('deleted_at')
- if from_:
- books = books.filter(changed_at__gte=from_)
- deleted = deleted.filter(deleted_at__gte=from_)
- if until:
- books = books.filter(changed_at__lte=until)
- deleted = deleted.filter(deleted_at__lte=until)
- return list(books) + list(deleted)
-
- @staticmethod
- def tag_to_setspec(tag):
- return "%s:%s" % (tag.category, tag.slug)
-
- @staticmethod
- def setspec_to_tag(s):
- if not s: return None
- cs = s.split(':')
- if len(cs) == 2:
- if not cs[0] in Catalogue.TAG_CATEGORIES:
- raise error.NoSetHierarchyError("No category part in set")
- tag = Tag.objects.get(slug=cs[1], category=cs[0])
- return tag
- raise error.NoSetHierarchyError("Setspec should have two components: category:slug")
-
- def getRecord(self, **kw):
- """
-Returns (header, metadata, about) for given record.
- """
- slug = self.identifier_to_slug(kw['identifier'])
- try:
- book = Book.objects.get(slug=slug)
- return self.record_for_book(book)
- except Book.DoesNotExist:
- book_type = ContentType.objects.get_for_model(Book)
- try:
- deleted_book = Deleted.objects.get(content_type=book_type,
- slug=slug)
- except:
- raise error.IdDoesNotExistError("No item for this identifier")
- return self.record_for_book(deleted_book)
-
- def validate_kw(self, kw):
- if 'resumptionToken' in kw:
- raise error.BadResumptionTokenError("No resumption token support at this point")
- if 'metadataPrefix' in kw and not self.metadata_registry.hasWriter(kw['metadataPrefix']):
- raise error.CannotDisseminateFormatError("This format is not supported")
-
- def identifier_to_slug(self, ident):
- return ident.split(':')[-1]
-
- def slug_to_identifier(self, slug):
- return self.oai_id % slug
-
- def listIdentifiers(self, **kw):
- self.validate_kw(kw)
- records = [self.record_for_book(book, headers_only=True) for
- book in self.books(None,
- kw.get('from_', None),
- kw.get('until', None))]
- return records, None
-
- def listRecords(self, **kw):
- """
-can get a resumptionToken kw.
-returns result, token
- """
- self.validate_kw(kw)
- records = [self.record_for_book(book) for
- book in self.books(None,
- kw.get('from_', None),
- kw.get('until', None))]
-
- return records, None
-
- def listMetadataFormats(self, **kw):
- formats = [
- ('oai_dc',
- 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
- server.NS_OAIDC),
- ('qdc',
- 'http://dublincore.org/schemas/xmls/qdc/2006/01/06/dcterms.xsd',
- NS_DCTERMS)]
- if 'identifier' in kw:
- slug = self.identifier_to_slug(kw['identifier'])
- try:
- b = Book.objects.get(slug=slug)
- return formats
- except:
- try:
- d = Deleted.objects.get(slug=slug)
- return []
- except:
- raise error.IdDoesNotExistError("This id does not exist")
- else:
- return formats
-
- def listSets(self, **kw):
- raise error.NoSetHierarchyError("Wolne Lektury does not support sets.")
- # tags = []
- # for category in Catalogue.TAG_CATEGORIES:
- # for tag in Tag.objects.filter(category=category):
- # tags.append(("%s:%s" % (tag.category, tag.slug),
- # tag.name,
- # tag.description))
- # return tags, None
+ except IndexError:
+ earliest_delete = year_zero
+
+ self.earliest_datestamp = earliest_change if earliest_change <= earliest_delete else earliest_delete
+
+ # @staticmethod
+ # def metadata(book):
+ # try:
+ # xml = etree.parse(book.xml_file)
+ # finally:
+ # book.xml_file.close()
+ # md = wl_dc_reader(xml)
+ # m = md.getMap()
+ # if book.parent:
+ # m['isPartOf'] = [str(WLURI.from_slug(book.parent.slug))]
+ # return m
+
+ # WTF
+ # def record_for_book(self, book, headers_only=False):
+ # meta = None
+ # identifier = self.slug_to_identifier(book.slug)
+ # if isinstance(book, Book):
+ # # setSpec = map(self.tag_to_setspec, book.tags.filter(category__in=self.TAG_CATEGORIES))
+ # header = common.Header(identifier, make_time_naive(book.changed_at), [], False)
+ # if not headers_only:
+ # meta = common.Metadata(self.metadata(book))
+ # about = None
+ # elif isinstance(book, Deleted):
+ # header = common.Header(identifier, make_time_naive(book.deleted_at), [], True)
+ # if not headers_only:
+ # meta = common.Metadata({})
+ # about = None
+ # if headers_only:
+ # return header
+ # return header, meta, about
+
+ # def identify(self, **kw):
+ # ident = common.Identify(
+ # 'Wolne Lektury', # generate
+ # '%s/oaipmh' % unicode(WL_BASE), # generate
+ # '2.0', # version
+ # [m[1] for m in settings.MANAGERS], # adminEmails
+ # make_time_naive(self.earliest_datestamp), # earliest datestamp of any change
+ # 'persistent', # deletedRecord
+ # 'YYYY-MM-DDThh:mm:ssZ', # granularity
+ # ['identity'], # compression
+ # [] # descriptions
+ # )
+ # return ident
+
+ # def books(self, tag, from_, until):
+ # if tag:
+ # # we do not support sets, since they are problematic for deleted books.
+ # raise error.NoSetHierarchyError("Wolne Lektury does not support sets.")
+ # # books = Book.tagged.with_all([tag])
+ # else:
+ # books = Book.objects.all()
+ # deleted = Deleted.objects.exclude(slug__exact=u'')
+ #
+ # books = books.order_by('changed_at')
+ # deleted = deleted.order_by('deleted_at')
+ # if from_:
+ # books = books.filter(changed_at__gte=from_)
+ # deleted = deleted.filter(deleted_at__gte=from_)
+ # if until:
+ # books = books.filter(changed_at__lte=until)
+ # deleted = deleted.filter(deleted_at__lte=until)
+ # return list(books) + list(deleted)
+
+ # @staticmethod
+ # def tag_to_setspec(tag):
+ # return "%s:%s" % (tag.category, tag.slug)
+
+ # @staticmethod
+ # def setspec_to_tag(s):
+ # if not s: return None
+ # cs = s.split(':')
+ # if len(cs) == 2:
+ # if not cs[0] in Catalogue.TAG_CATEGORIES:
+ # raise error.NoSetHierarchyError("No category part in set")
+ # tag = Tag.objects.get(slug=cs[1], category=cs[0])
+ # return tag
+ # raise error.NoSetHierarchyError("Setspec should have two components: category:slug")
+
+ # def getRecord(self, **kw):
+ # """Returns (header, metadata, about) for given record."""
+ # slug = self.identifier_to_slug(kw['identifier'])
+ # try:
+ # book = Book.objects.get(slug=slug)
+ # return self.record_for_book(book)
+ # except Book.DoesNotExist:
+ # book_type = ContentType.objects.get_for_model(Book)
+ # try:
+ # deleted_book = Deleted.objects.get(content_type=book_type,
+ # slug=slug)
+ # except:
+ # raise error.IdDoesNotExistError("No item for this identifier")
+ # return self.record_for_book(deleted_book)
+
+ # def validate_kw(self, kw):
+ # if 'resumptionToken' in kw:
+ # raise error.BadResumptionTokenError("No resumption token support at this point")
+ # if 'metadataPrefix' in kw and not self.metadata_registry.hasWriter(kw['metadataPrefix']):
+ # raise error.CannotDisseminateFormatError("This format is not supported")
+
+ # def identifier_to_slug(self, ident):
+ # return ident.split(':')[-1]
+
+ # def slug_to_identifier(self, slug):
+ # return self.oai_id % slug
+
+ # def listIdentifiers(self, **kw):
+ # self.validate_kw(kw)
+ # records = [self.record_for_book(book, headers_only=True) for
+ # book in self.books(None,
+ # kw.get('from_', None),
+ # kw.get('until', None))]
+ # return records, None
+
+ # def listRecords(self, **kw):
+ # """
+ # can get a resumptionToken kw.
+ # returns result, token
+ # """
+ # self.validate_kw(kw)
+ # records = [self.record_for_book(book) for
+ # book in self.books(None,
+ # kw.get('from_', None),
+ # kw.get('until', None))]
+ #
+ # return records, None
+
+ # def listMetadataFormats(self, **kw):
+ # formats = [
+ # ('oai_dc',
+ # 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
+ # server.NS_OAIDC),
+ # ('qdc',
+ # 'http://dublincore.org/schemas/xmls/qdc/2006/01/06/dcterms.xsd',
+ # NS_DCTERMS)]
+ # if 'identifier' in kw:
+ # slug = self.identifier_to_slug(kw['identifier'])
+ # try:
+ # b = Book.objects.get(slug=slug)
+ # return formats
+ # except:
+ # try:
+ # d = Deleted.objects.get(slug=slug)
+ # return []
+ # except:
+ # raise error.IdDoesNotExistError("This id does not exist")
+ # else:
+ # return formats
+
+ # def listSets(self, **kw):
+ # raise error.NoSetHierarchyError("Wolne Lektury does not support sets.")
+ # # tags = []
+ # # for category in Catalogue.TAG_CATEGORIES:
+ # # for tag in Tag.objects.filter(category=category):
+ # # tags.append(("%s:%s" % (tag.category, tag.slug),
+ # # tag.name,
+ # # tag.description))
+ # # return tags, None
from oaipmh.server import *
from os import path
from oaipmh.metadata import MetadataRegistry
-from lxml import etree
class BookMetadataTest(WLTestCase):
nsmap = {'oai_dc': NS_OAIDC, 'dc': NS_DC, 'xsi': NS_XSI}
self.xml = XMLTreeServer(self.catalogue, mr, nsmap)
- def test_get_record(self):
- self.xml.getRecord(identifier='lubie-kiedy-kobieta',
- metadataPrefix='oai_dc')
- self.xml.listRecords(metadataPrefix='oai_dc')
-
- def test_selecting(self):
- records, token = self.catalogue.listRecords(**{'set': 'epoch:starozytnosc'})
+ # def test_get_record(self):
+ # self.xml.getRecord(identifier='lubie-kiedy-kobieta', metadataPrefix='oai_dc')
+ # self.xml.listRecords(metadataPrefix='oai_dc')
+ #
+ # def test_selecting(self):
+ # records, token = self.catalogue.listRecords(**{'set': 'epoch:starozytnosc'})
from unittest import skipIf
from lxml import etree
from django.conf import settings
-from django.core.files.base import ContentFile
import catalogue
-from catalogue.test_utils import (BookInfoStub, PersonStub, info_args,
- WLTestCase, get_fixture)
+from catalogue.test_utils import WLTestCase, get_fixture
from catalogue.models import Book
from librarian import WLURI, XMLNamespace
-from search.index import Index, Search
+from search.index import Index
AtomNS = XMLNamespace("http://www.w3.org/2005/Atom")
-@skipIf(getattr(settings, 'NO_SEARCH_INDEX', False),
- u'Requires search server and NO_SEARCH_INDEX=False.')
+@skipIf(getattr(settings, 'NO_SEARCH_INDEX', False), u'Requires search server and NO_SEARCH_INDEX=False.')
class OpdsSearchTests(WLTestCase):
"""Tests search feed in OPDS.."""
def setUp(self):
self.client.get('/opds/search/?%s' % query).content)
elem_ids = tree.findall('.//%s/%s' % (AtomNS('entry'), AtomNS('id')))
slugs = [WLURI(elem.text).slug for elem in elem_ids]
- self.assertEqual(set(slugs), set(b.slug for b in books),
- u"OPDS search '%s' failed." % query)
+ self.assertEqual(set(slugs), set(b.slug for b in books), u"OPDS search '%s' failed." % query)
def test_opds_search_simple(self):
"""Do a simple q= test, also emulate dumb OPDS clients."""
- both = set([self.do_doktora, self.do_anusie])
+ both = {self.do_doktora, self.do_anusie}
self.assert_finds('q=fraszka', both)
self.assert_finds('q=fraszka&author={opds:author}', both)
def test_opds_search_title(self):
"""Search by title."""
- both = set([self.do_doktora, self.do_anusie])
+ both = {self.do_doktora, self.do_anusie}
self.assert_finds('title=fraszka', both)
self.assert_finds('title=fraszka', both)
self.assert_finds('q=title:doktora', [self.do_doktora])
from opds.views import RootFeed, ByCategoryFeed, ByTagFeed, UserFeed, UserSetFeed, SearchFeed
-urlpatterns = patterns('opds.views',
+urlpatterns = patterns(
+ 'opds.views',
url(r'^$', RootFeed(), name="opds_authors"),
url(r'^search/$', SearchFeed(), name="opds_search"),
url(r'^user/$', UserFeed(), name="opds_user"),
import logging
import re
-log = logging.getLogger('opds')
-
from stats.utils import piwik_track
+log = logging.getLogger('opds')
+
_root_feeds = (
{
u"category": u"",
current_domain = lazy(lambda: Site.objects.get_current().domain, str)()
+
+
def full_url(url):
return urljoin("http://%s" % current_domain, url)
_book_parent_img = lazy(lambda: full_url(os.path.join(settings.STATIC_URL, "img/book-parent.png")), str)()
try:
_book_parent_img_size = unicode(os.path.getsize(os.path.join(settings.STATIC_ROOT, "img/book-parent.png")))
- except:
+ except IOError:
_book_parent_img_size = ''
_book_img = lazy(lambda: full_url(os.path.join(settings.STATIC_URL, "img/book.png")), str)()
try:
_book_img_size = unicode(os.path.getsize(os.path.join(settings.STATIC_ROOT, "img/book.png")))
- except:
+ except IOError:
_book_img_size = ''
-
def add_root_elements(self, handler):
super(OPDSFeed, self).add_root_elements(handler)
handler.addQuickElement(u"link", None,
u"rel": u"search",
u"type": u"application/opensearchdescription+xml"})
-
def add_item_elements(self, handler, item):
""" modified from Atom1Feed.add_item_elements """
handler.addQuickElement(u"title", item['title'])
# add a OPDS Navigation link if there's no enclosure
if item['enclosure'] is None:
- handler.addQuickElement(u"link", u"", {u"href": item['link'], u"rel": u"subsection", u"type": u"application/atom+xml"})
+ handler.addQuickElement(
+ u"link", u"", {u"href": item['link'], u"rel": u"subsection", u"type": u"application/atom+xml"})
# add a "green book" icon
- handler.addQuickElement(u"link", '',
- {u"rel": u"http://opds-spec.org/thumbnail",
- u"href": self._book_parent_img,
- u"length": self._book_parent_img_size,
- u"type": u"image/png"})
+ handler.addQuickElement(
+ u"link", '',
+ {
+ u"rel": u"http://opds-spec.org/thumbnail",
+ u"href": self._book_parent_img,
+ u"length": self._book_parent_img_size,
+ u"type": u"image/png",
+ })
if item['pubdate'] is not None:
# FIXME: rfc3339_date is undefined, is this ever run?
handler.addQuickElement(u"updated", rfc3339_date(item['pubdate']).decode('utf-8'))
# Enclosure as OPDS Acquisition Link
if item['enclosure'] is not None:
- handler.addQuickElement(u"link", '',
- {u"rel": u"http://opds-spec.org/acquisition",
- u"href": item['enclosure'].url,
- u"length": item['enclosure'].length,
- u"type": item['enclosure'].mime_type})
+ handler.addQuickElement(
+ u"link", '',
+ {
+ u"rel": u"http://opds-spec.org/acquisition",
+ u"href": item['enclosure'].url,
+ u"length": item['enclosure'].length,
+ u"type": item['enclosure'].mime_type,
+ })
# add a "red book" icon
- handler.addQuickElement(u"link", '',
- {u"rel": u"http://opds-spec.org/thumbnail",
- u"href": self._book_img,
- u"length": self._book_img_size,
- u"type": u"image/png"})
+ handler.addQuickElement(
+ u"link", '',
+ {
+ u"rel": u"http://opds-spec.org/thumbnail",
+ u"href": self._book_img,
+ u"length": self._book_img_size,
+ u"type": u"image/png",
+ })
# Categories.
for cat in item['categories']:
def item_enclosure_length(self, book):
return book.epub_file.size if book.epub_file else None
+
@piwik_track
class RootFeed(Feed):
feed_type = OPDSFeed
def item_description(self, item):
return item['description']
+
@piwik_track
class ByCategoryFeed(Feed):
feed_type = OPDSFeed
def item_description(self):
return u''
+
@piwik_track
class ByTagFeed(AcquisitionFeed):
def link(self, tag):
return ''
return val
- criteria = dict([(cn, remove_dump_data(request.GET.get(cn, '')))
- for cn in self.MATCHES.keys()])
+ criteria = dict(
+ (cn, remove_dump_data(request.GET.get(cn, '')))
+ for cn in self.MATCHES.keys())
# query is set above.
log.debug("Inline query = [%s], criteria: %s" % (query, criteria))
if query:
q = srch.index.query(
- reduce(operator.or_,
- [srch.index.Q(**{self.PARAMS_TO_FIELDS.get(cn, cn): query})
- for cn in self.MATCHES.keys()],
- srch.index.Q()))
+ reduce(
+ operator.or_,
+ [srch.index.Q(**{self.PARAMS_TO_FIELDS.get(cn, cn): query}) for cn in self.MATCHES.keys()],
+ srch.index.Q()))
else:
q = srch.index.query(srch.index.Q())
prepopulated_fields = {'slug': ('title',)}
+
class AuthorAdmin(admin.ModelAdmin):
list_display = ('name', 'slug', 'death')
search_fields = ('name',)
ordering = ('sort_key', 'name')
- prepopulated_fields = {'slug': ('name',), 'sort_key': ('name',),}
+ prepopulated_fields = {'slug': ('name',), 'sort_key': ('name',)}
admin.site.register(BookStub, BookStubAdmin)
from datetime import datetime
from django.db.models.signals import post_save, post_delete
+
class Author(models.Model):
name = models.CharField(_('name'), max_length=50, db_index=True)
slug = models.SlugField(_('slug'), max_length=120, db_index=True, unique=True)
@permalink
def get_absolute_url(self):
- return ('catalogue.views.tagged_object_list', [self.url_chunk])
+ return 'catalogue.views.tagged_object_list', [self.url_chunk]
def has_description(self):
return len(self.description) > 0
@permalink
def get_absolute_url(self):
- return ('catalogue.views.book_detail', [self.slug])
+ return 'catalogue.views.book_detail', [self.slug]
def in_pd(self):
return self.pd is not None and self.pd <= datetime.now().year
def update_index(sender, instance, **kwargs):
from search.index import Index
idx = Index()
- idx.index_tags(instance, remove_only=not 'created' in kwargs)
+ idx.index_tags(instance, remove_only='created' not in kwargs)
post_delete.connect(update_index, Author)
post_delete.connect(update_index, BookStub)
register = template.Library()
+
@register.filter
def date_to_utc(date, day_end=False):
""" Converts a datetime.date to UTC datetime.
if book.pd and not book.in_pd():
pd_counter = datetime(book.pd, 1, 1)
- form = PublishingSuggestForm(
- initial={"books": u"%s — %s, \n" % (book.author, book.title)})
+ form = PublishingSuggestForm(initial={"books": u"%s — %s, \n" % (book.author, book.title)})
- return render_to_response('pdcounter/book_stub_detail.html', locals(),
- context_instance=RequestContext(request))
+ return render_to_response('pdcounter/book_stub_detail.html', locals(), context_instance=RequestContext(request))
@cache.never_cache
form = PublishingSuggestForm(initial={"books": author.name + ", \n"})
- return render_to_response('pdcounter/author_detail.html', locals(),
- context_instance=RequestContext(request))
+ return render_to_response('pdcounter/author_detail.html', locals(), context_instance=RequestContext(request))
from picture.models import Picture
from sorl.thumbnail.admin import AdminImageMixin
+
class PictureAdmin(AdminImageMixin, admin.ModelAdmin):
pass
from sorl.thumbnail.engines import pil_engine
from sorl.thumbnail import parsers
+
#
# Class developed by
# http://timmyomahony.com/blog/custom-cropping-engine-sorl-thumbnail/
m = parsers.bgpos_pat.match(crop)
if not m:
raise parsers.ThumbnailParseError('Unrecognized crop option: %s' % crop)
- value = int(m.group('value')) # we only take ints in the regexp
+ value = int(m.group('value')) # we only take ints in the regexp
unit = m.group('unit')
if unit == '%':
value = epsilon * value / 100.0
if not crop or crop == 'noop':
return image
x_image, y_image = self.get_image_size(image)
- x1,y1,x2,y2 = self._crop_parse(crop, (x_image, y_image), geometry)
+ x1, y1, x2, y2 = self._crop_parse(crop, (x_image, y_image), geometry)
return self._crop(image, x1, y1, x2, y2)
def _crop(self, image, x1, y1, x2, y2):
return super(PictureImportForm, self).clean()
def save(self, commit=True, **kwargs):
- return Picture.from_xml_file(self.cleaned_data['picture_xml_file'], image_file=self.cleaned_data['picture_image_file'],
- overwrite=True, **kwargs)
+ return Picture.from_xml_file(
+ self.cleaned_data['picture_xml_file'], image_file=self.cleaned_data['picture_image_file'],
+ overwrite=True, **kwargs)
class PictureArea(models.Model):
picture = models.ForeignKey('picture.Picture', related_name='areas')
area = jsonfield.JSONField(_('area'), default={}, editable=False)
- kind = models.CharField(_('kind'), max_length=10, blank=False,
- null=False, db_index=True,
- choices=(('thing', _('thing')),
- ('theme', _('theme'))))
-
- objects = models.Manager()
- tagged = managers.ModelTaggedItemManager(catalogue.models.Tag)
- tags = managers.TagDescriptor(catalogue.models.Tag)
+ kind = models.CharField(
+ _('kind'), max_length=10, blank=False, null=False, db_index=True,
+ choices=(('thing', _('thing')), ('theme', _('theme'))))
+
+ objects = models.Manager()
+ tagged = managers.ModelTaggedItemManager(catalogue.models.Tag)
+ tags = managers.TagDescriptor(catalogue.models.Tag)
tag_relations = GenericRelation(catalogue.models.Tag.intermediary_table_model)
short_html_url_name = 'picture_area_short'
Picture resource.
"""
- title = models.CharField(_('title'), max_length=32767)
- slug = models.SlugField(_('slug'), max_length=120, db_index=True, unique=True)
- sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False)
- sort_key_author = models.CharField(_('sort key by author'), max_length=120, db_index=True, editable=False, default=u'')
- created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
- changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
- xml_file = models.FileField('xml_file', upload_to="xml", storage=picture_storage)
- image_file = ImageField(_('image_file'), upload_to="images", storage=picture_storage)
- html_file = models.FileField('html_file', upload_to="html", storage=picture_storage)
- areas_json = jsonfield.JSONField(_('picture areas JSON'), default={}, editable=False)
- extra_info = jsonfield.JSONField(_('extra information'), default={})
- culturepl_link = models.CharField(blank=True, max_length=240)
- wiki_link = models.CharField(blank=True, max_length=240)
-
- width = models.IntegerField(null=True)
- height = models.IntegerField(null=True)
-
- objects = models.Manager()
- tagged = managers.ModelTaggedItemManager(catalogue.models.Tag)
- tags = managers.TagDescriptor(catalogue.models.Tag)
+ title = models.CharField(_('title'), max_length=32767)
+ slug = models.SlugField(_('slug'), max_length=120, db_index=True, unique=True)
+ sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False)
+ sort_key_author = models.CharField(
+ _('sort key by author'), max_length=120, db_index=True, editable=False, default=u'')
+ created_at = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
+ changed_at = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
+ xml_file = models.FileField('xml_file', upload_to="xml", storage=picture_storage)
+ image_file = ImageField(_('image_file'), upload_to="images", storage=picture_storage)
+ html_file = models.FileField('html_file', upload_to="html", storage=picture_storage)
+ areas_json = jsonfield.JSONField(_('picture areas JSON'), default={}, editable=False)
+ extra_info = jsonfield.JSONField(_('extra information'), default={})
+ culturepl_link = models.CharField(blank=True, max_length=240)
+ wiki_link = models.CharField(blank=True, max_length=240)
+
+ width = models.IntegerField(null=True)
+ height = models.IntegerField(null=True)
+
+ objects = models.Manager()
+ tagged = managers.ModelTaggedItemManager(catalogue.models.Tag)
+ tags = managers.TagDescriptor(catalogue.models.Tag)
tag_relations = GenericRelation(catalogue.models.Tag.intermediary_table_model)
short_html_url_name = 'picture_short'
@permalink
def get_absolute_url(self):
- return ('picture.views.picture_detail', [self.slug])
+ return 'picture.views.picture_detail', [self.slug]
def get_initial(self):
try:
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))
close_image_file = True
motif_tags = set()
thing_tags = set()
- area_data = {'themes':{}, 'things':{}}
+ area_data = {'themes': {}, 'things': {}}
# Treat all names in picture XML as in default language.
lang = settings.LANGUAGE_CODE
_tags = set()
for objname in part['object'].split(','):
objname = objname.strip().capitalize()
- tag, created = catalogue.models.Tag.objects.get_or_create(slug=slughifi(objname), category='thing')
+ tag, created = catalogue.models.Tag.objects.get_or_create(
+ slug=slughifi(objname), category='thing')
if created:
tag.name = objname
setattr(tag, 'name_%s' % lang, tag.name)
tag.sort_key = sortify(tag.name)
tag.save()
- #thing_tags.add(tag)
+ # thing_tags.add(tag)
area_data['things'][tag.slug] = {
'object': objname,
'coords': part['coords'],
_tags = set()
for motifs in part['themes']:
for motif in motifs.split(','):
- tag, created = catalogue.models.Tag.objects.get_or_create(slug=slughifi(motif), category='theme')
+ tag, created = catalogue.models.Tag.objects.get_or_create(
+ slug=slughifi(motif), category='theme')
if created:
tag.name = motif
tag.sort_key = sortify(tag.name)
tag.save()
- #motif_tags.add(tag)
+ # motif_tags.add(tag)
_tags.add(tag)
area_data['themes'][tag.slug] = {
'theme': motif,
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 = 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(
annotated.paste(annotation.resize((img.size[0], 40), Image.ANTIALIAS), (0, img.size[1]))
return annotated
+ # WTF/unused
@classmethod
def picture_list(cls, filter=None):
"""Generates a hierarchical listing of all pictures
Pictures are optionally filtered with a test function.
"""
- pics = cls.objects.all().order_by('sort_key')\
- .only('title', 'slug', 'image_file')
+ pics = cls.objects.all().order_by('sort_key').only('title', 'slug', 'image_file')
if filter:
pics = pics.filter(filter).distinct()
'themes': pic.areas_json['themes'],
}))
pic.html_file.save("%s.html" % pic.slug, ContentFile(html_text))
-
cropper = CustomCroppingEngine()
+
@register.inclusion_tag('picture/picture_wide.html', takes_context=True)
def picture_wide(context, picture):
context.update({
themes = set()
for area in picture.areas.all():
- themes.update([(tag.category, tag.name)
+ themes.update([
+ (tag.category, tag.name)
for tag in area.tags if tag.category in (u'theme', u'thing')])
- assert themes == set([(u'theme', u'nieporządek'), (u'thing', u'Kosmos')]), \
+ assert themes == {(u'theme', u'nieporządek'), (u'thing', u'Kosmos')}, \
'Bad themes on Picture areas: %s' % themes
pic_themes = set([tag.name for tag in picture.tags if tag.category in ('theme', 'thing')])
picture.delete()
-
def test_import_2(self):
picture = Picture.from_xml_file(path.join(path.dirname(__file__), "files/kandinsky-composition-viii.xml"),
path.join(path.dirname(__file__), "files/kandinsky-composition-viii.png"),
cats = set([t.category for t in picture.tags])
assert 'epoch' in cats
assert 'kind' in cats
-
# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
-from collections import OrderedDict
from django.contrib.auth.decorators import permission_required
from django.shortcuts import render_to_response, get_object_or_404, render
from django.template import RequestContext
from ssify import ssi_included
from sponsors.models import Sponsor
-# was picture/picture_list.html list (without thumbs)
-def picture_list(request, filter=None, get_filter=None, template_name='catalogue/picture_list.html', cache_key=None, context=None):
- """ generates a listing of all books, optionally filtered with a test function """
- if get_filter:
- filt = get_filter()
- pictures_by_author, orphans = Picture.picture_list(filt)
- books_nav = OrderedDict()
- for tag in pictures_by_author:
- if pictures_by_author[tag]:
- books_nav.setdefault(tag.sort_key[0], []).append(tag)
-
- return render_to_response(template_name, locals(),
- context_instance=RequestContext(request))
+# WTF/unused
+# # was picture/picture_list.html list (without thumbs)
+# def picture_list(request, filter=None, get_filter=None, template_name='catalogue/picture_list.html',
+# cache_key=None, context=None):
+# """ generates a listing of all books, optionally filtered with a test function """
+#
+# if get_filter:
+# filt = get_filter()
+# pictures_by_author, orphans = Picture.picture_list(filt)
+# books_nav = OrderedDict()
+# for tag in pictures_by_author:
+# if pictures_by_author[tag]:
+# books_nav.setdefault(tag.sort_key[0], []).append(tag)
+#
+# return render_to_response(template_name, locals(), context_instance=RequestContext(request))
-def picture_list_thumb(request, filter=None, get_filter=None, template_name='picture/picture_list_thumb.html', cache_key=None, context=None):
+def picture_list_thumb(request, filter=None, get_filter=None, template_name='picture/picture_list_thumb.html',
+ cache_key=None, context=None):
book_list = Picture.objects.all()
if filter:
book_list = book_list.filter(filter)
book_list = book_list.filter(get_filter())
book_list = book_list.order_by('sort_key_author')
book_list = list(book_list)
- return render_to_response(template_name, locals(),
- context_instance=RequestContext(request))
+ return render_to_response(template_name, locals(), context_instance=RequestContext(request))
+
def picture_detail(request, slug):
picture = get_object_or_404(Picture, slug=slug)
info = sys.exc_info()
exception = pprint.pformat(info[1])
tb = '\n'.join(traceback.format_tb(info[2]))
- return HttpResponse(_("An error occurred: %(exception)s\n\n%(tb)s") % {'exception':exception, 'tb':tb}, mimetype='text/plain')
+ return HttpResponse(_("An error occurred: %(exception)s\n\n%(tb)s") %
+ {'exception': exception, 'tb': tb}, mimetype='text/plain')
return HttpResponse(_("Picture imported successfully"))
else:
return HttpResponse(_("Error importing file: %r") % import_form.errors)
@ssi_included
def picture_mini(request, pk, with_link=True):
picture = get_object_or_404(Picture, pk=pk)
- author_str = ", ".join(tag.name
- for tag in picture.tags.filter(category='author'))
+ author_str = ", ".join(tag.name for tag in picture.tags.filter(category='author'))
return render(request, 'picture/picture_mini_box.html', {
'picture': picture,
'author_str': author_str,
class Meta:
verbose_name = _('vote item')
verbose_name_plural = _('vote items')
-\r
+
def __unicode__(self):
return self.content + ' @ ' + unicode(self.poll)
return (float(self.vote_count) / self.poll.vote_count) * 100 if self.poll.vote_count else 0
def vote(self, session):
- self.vote_count = self.vote_count + 1
+ self.vote_count += 1
self.save()
session.setdefault(USED_POLLS_KEY, []).append(self.poll.id)
session.save()
register = template.Library()
+
@register.inclusion_tag('polls/tags/poll.html', takes_context=True)
def poll(context, poll, show_results=True, redirect_to=''):
form = None
voted_already = poll.voted(context.get('request').session)
if not voted_already:
form = PollForm(poll=poll, initial=dict(redirect_to=redirect_to))
- return dict(poll=poll,
- form=form,
- voted_already=voted_already,
- vote_count=poll.vote_count,
- show_results=show_results,
- request=context.get('request'),
- )
+ return {'poll': poll, 'form': form, 'voted_already': voted_already, 'vote_count': poll.vote_count,
+ 'show_results': show_results, 'request': context.get('request')}
from django.conf.urls import patterns, url
-urlpatterns = patterns('polls.views',
+urlpatterns = patterns(
+ 'polls.views',
url(r'^(?P<slug>[^/]+)$', 'poll', name='poll'),
)
@cache.never_cache
@require_http_methods(['GET', 'POST'])
def poll(request, slug):
-
poll = get_object_or_404(Poll, slug=slug, open=True)
if request.method == 'POST':
- redirect_to = reverse('poll', args = [slug])
- form = PollForm(request.POST, poll = poll)
+ redirect_to = reverse('poll', args=[slug])
+ form = PollForm(request.POST, poll=poll)
if form.is_valid():
if not poll.voted(request.session):
try:
register = template.Library()
+
class StatsNode(template.Node):
def __init__(self, value, varname=None):
self.value = value
def count_books_all():
return Book.objects.all().count()
+
@register_counter
def count_books():
return Book.objects.filter(children=None).count()
+
@register_counter
def count_books_parent():
return Book.objects.exclude(children=None).count()
+
@register_counter
def count_books_root():
return Book.objects.filter(parent=None).count()
from django.conf.urls import patterns, url
-urlpatterns = patterns('reporting.views',
+urlpatterns = patterns(
+ 'reporting.views',
url(r'^$', 'stats_page', name='reporting_stats'),
url(r'^katalog.pdf$', 'catalogue_pdf', name='reporting_catalogue_pdf'),
url(r'^katalog.csv$', 'catalogue_csv', name='reporting_catalogue_csv'),
)
-
from django.conf import settings
import logging
from django.http import HttpResponse
+from wolnelektury.utils import makedirs
logger = logging.getLogger(__name__)
cwd = os.getcwd()
os.chdir(tempdir)
try:
- subprocess.check_call(['xelatex', '-interaction=batchmode', tex_path],
+ subprocess.check_call(
+ ['xelatex', '-interaction=batchmode', tex_path],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- try:
- os.makedirs(os.path.dirname(output_path))
- except:
- pass
+ makedirs(os.path.dirname(output_path))
shutil.move(os.path.join(tempdir, "doc.pdf"), output_path)
finally:
os.chdir(cwd)
from django.template.loader import render_to_string
- try:
- os.makedirs(os.path.dirname(output_path))
- except:
- pass
+ makedirs(os.path.dirname(output_path))
rendered = render_to_string(template, context)
with open(output_path, 'w') as csv_file:
@staff_member_required
def stats_page(request):
media = BookMedia.objects.count()
- media_types = BookMedia.objects.values('type').\
- annotate(count=Count('type')).\
- order_by('type')
+ media_types = BookMedia.objects.values('type').annotate(count=Count('type')).order_by('type')
for mt in media_types:
mt['size'] = sum(b.file.size for b in BookMedia.objects.filter(type=mt['type']).iterator())
if mt['type'] in ('mp3', 'ogg'):
- deprecated = BookMedia.objects.filter(
- type=mt['type'], source_sha1=None)
+ deprecated = BookMedia.objects.filter(type=mt['type'], source_sha1=None)
mt['deprecated'] = deprecated.count()
mt['deprecated_files'] = deprecated.order_by('book', 'name')
else:
mt['deprecated'] = '-'
- licenses = set(((b.extra_info.get('license'), b.extra_info.get('license_description'))
+ licenses = set((
+ (b.extra_info.get('license'), b.extra_info.get('license_description'))
for b in Book.objects.all().iterator() if b.extra_info.get('license')))
- return render_to_response('reporting/main.html',
- locals(), context_instance=RequestContext(request))
+ return render_to_response('reporting/main.html', locals(), context_instance=RequestContext(request))
@generated_file_view('reports/katalog.pdf', 'application/pdf',
- send_name=lambda: 'wolnelektury_%s.pdf' % date.today(),
- signals=[Book.published])
+ send_name=lambda: 'wolnelektury_%s.pdf' % date.today(), signals=[Book.published])
def catalogue_pdf(path):
books_by_author, orphans, books_by_parent = Book.book_list()
render_to_pdf(path, 'reporting/catalogue.texml', locals(), {
- "wl-logo.png": os.path.join(settings.STATIC_ROOT, "img/logo-big.png"),
- })
+ "wl-logo.png": os.path.join(settings.STATIC_ROOT, "img/logo-big.png"),
+ })
@generated_file_view('reports/katalog.csv', 'application/csv',
- send_name=lambda: 'wolnelektury_%s.csv' % date.today(),
- signals=[Book.published])
+ send_name=lambda: 'wolnelektury_%s.csv' % date.today(), signals=[Book.published])
def catalogue_csv(path):
books_by_author, orphans, books_by_parent = Book.book_list()
render_to_csv(path, 'reporting/catalogue.csv', locals())
def search_form(request):
- return { 'search_form': SearchForm(reverse('search.views.hint'), request.GET) }
+ return {'search_form': SearchForm(reverse('search.views.hint'), request.GET)}
qs = urllib.urlencode(params)
url = "%s?%s" % (self.analysis_url, qs)
if len(url) > self.max_length_get_url:
- warnings.warn("Long query URL encountered - POSTing instead of "
- "GETting. This query will not be cached at the HTTP layer")
+ warnings.warn("Long query URL encountered - POSTing instead of GETting. "
+ "This query will not be cached at the HTTP layer")
url = self.analysis_url
kwargs = dict(
method="POST",
class CustomSolrInterface(sunburnt.SolrInterface):
# just copied from parent and SolrConnection -> CustomSolrConnection
- def __init__(self, url, schemadoc=None, http_connection=None, mode='', retry_timeout=-1, max_length_get_url=sunburnt.MAX_LENGTH_GET_URL):
+ def __init__(self, url, schemadoc=None, http_connection=None, mode='', retry_timeout=-1,
+ max_length_get_url=sunburnt.MAX_LENGTH_GET_URL):
self.conn = CustomSolrConnection(url, http_connection, retry_timeout, max_length_get_url)
self.schemadoc = schemadoc
if 'w' not in mode:
args = {
'analysis_showmatch': True
}
- if 'field' in kwargs: args['analysis_fieldname'] = kwargs['field']
- if 'text' in kwargs: args['analysis_fieldvalue'] = kwargs['text']
- if 'q' in kwargs: args['q'] = kwargs['q']
- if 'query' in kwargs: args['q'] = kwargs['q']
+ if 'field' in kwargs:
+ args['analysis_fieldname'] = kwargs['field']
+ if 'text' in kwargs:
+ args['analysis_fieldvalue'] = kwargs['text']
+ if 'q' in kwargs:
+ args['q'] = kwargs['q']
+ if 'query' in kwargs:
+ args['q'] = kwargs['q']
params = map(lambda (k, v): (k.replace('_', '.'), v), sunburnt.params_from_dict(**args))
matches.add((start, end))
if matches:
- return self.substring(kwargs['text'], matches,
- margins=kwargs.get('margins', 30),
- mark=kwargs.get('mark', ("<b>", "</b>")))
+ return self.substring(
+ kwargs['text'], matches, margins=kwargs.get('margins', 30), mark=kwargs.get('mark', ("<b>", "</b>")))
else:
return None
break
end += 1
- return (start, end)
+ return start, end
def substring(self, text, matches, margins=30, mark=("<b>", "</b>")):
- start = None
- end = None
totlen = len(text)
- matches_margins = map(lambda (s, e):
- ((s, e),
- (max(0, s - margins), min(totlen, e + margins))),
- matches)
- matches_margins = map(lambda (m, (s, e)):
- (m, self.expand_margins(text, s, e)),
- matches_margins)
-
- # lets start with first match
+ matches_margins = [
+ ((s, e), self.expand_margins(text, max(0, s - margins), min(totlen, e + margins))) for s, e in matches]
+
+ # lets start with first match
(start, end) = matches_margins[0][1]
- matches = [matches_margins[0][0]]
+ new_matches = [matches_margins[0][0]]
for (m, (s, e)) in matches_margins[1:]:
if end < s or start > e:
continue
start = min(start, s)
end = max(end, e)
- matches.append(m)
+ new_matches.append(m)
snip = text[start:end]
- matches.sort(lambda a, b: cmp(b[0], a[0]))
+ new_matches.sort(lambda a, b: cmp(b[0], a[0]))
- for (s, e) in matches:
- off = - start
+ for (s, e) in new_matches:
+ off = -start
snip = snip[:e + off] + mark[1] + snip[e + off:]
snip = snip[:s + off] + mark[0] + snip[s + off:]
if value:
final_attrs['value'] = smart_unicode(value)
- if not self.attrs.has_key('id'):
+ if 'id' not in self.attrs:
final_attrs['id'] = 'id_%s' % name
html = u'''<input type="text" %(attrs)s/>
%(js)s//--></script>
''' % {
'attrs': flatatt(final_attrs),
- 'js' : self.render_js(final_attrs['id'], self.options),
+ 'js': self.render_js(final_attrs['id'], self.options),
}
return mark_safe(html)
def render_js(self, field_id, options):
return u""
-
+
class JQueryAutoCompleteField(forms.CharField):
- def __init__(self, source, options={}, *args, **kwargs):
+ def __init__(self, source, options=None, *args, **kwargs):
+ if options is None:
+ options = {}
if 'widget' not in kwargs:
options['source'] = source
kwargs['widget'] = JQueryAutoCompleteWidget(options)
class JQueryAutoCompleteSearchField(forms.CharField):
- def __init__(self, options={}, *args, **kwargs):
+ def __init__(self, options=None, *args, **kwargs):
+ if options is None:
+ options = {}
if 'widget' not in kwargs:
kwargs['widget'] = JQueryAutoCompleteSearchWidget(options)
class SearchForm(forms.Form):
- q = JQueryAutoCompleteSearchField(label=_('Search')) # {'minChars': 2, 'selectFirst': True, 'cacheLength': 50, 'matchContains': "word"})
+ q = JQueryAutoCompleteSearchField(label=_('Search'))
+ # {'minChars': 2, 'selectFirst': True, 'cacheLength': 50, 'matchContains': "word"})
def __init__(self, source, *args, **kwargs):
kwargs['auto_id'] = False
self.fields['q'].widget.attrs['id'] = 'search'
self.fields['q'].widget.attrs['autocomplete'] = 'off'
self.fields['q'].widget.attrs['data-source'] = source
- if not 'q' in self.data:
+ if 'q' not in self.data:
self.fields['q'].widget.attrs['placeholder'] = _('title, author, theme/topic, epoch, kind, genre, phrase')
import os
import re
-import errno
from librarian import dcparser
from librarian.parser import WLDocument
from lxml import etree
import catalogue.models
from pdcounter.models import Author as PDCounterAuthor, BookStub as PDCounterBook
from itertools import chain
-import traceback
-import logging
-log = logging.getLogger('search')
import sunburnt
import custom
import operator
+import logging
+from wolnelektury.utils import makedirs
log = logging.getLogger('search')
+
class SolrIndex(object):
def __init__(self, mode=None):
self.index = custom.CustomSolrInterface(settings.SOLR, mode=mode)
SNIPPET_DIR = "snippets"
def __init__(self, book_id, revision=None):
- try:
- os.makedirs(os.path.join(settings.SEARCH_INDEX, self.SNIPPET_DIR))
- except OSError as exc:
- if exc.errno == errno.EEXIST:
- pass
- else: raise
+ makedirs(os.path.join(settings.SEARCH_INDEX, self.SNIPPET_DIR))
self.book_id = book_id
self.revision = revision
self.file = None
+ self.position = None
@property
def path(self):
- if self.revision: fn = "%d.%d" % (self.book_id, self.revision)
- else: fn = "%d" % self.book_id
+ if self.revision:
+ fn = "%d.%d" % (self.book_id, self.revision)
+ else:
+ fn = "%d" % self.book_id
return os.path.join(settings.SEARCH_INDEX, self.SNIPPET_DIR, fn)
"""
Open the snippet file. Call .close() afterwards.
"""
- if not 'b' in mode:
+ if 'b' not in mode:
mode += 'b'
if 'w' in mode:
else:
return False
+ # WTF
def index_tags(self, *tags, **kw):
"""
Re-index global tag list.
if not remove_only:
# then add them [all or just one passed]
if not tags:
- tags = chain(catalogue.models.Tag.objects.exclude(category='set'), \
- PDCounterAuthor.objects.all(), \
+ tags = chain(
+ catalogue.models.Tag.objects.exclude(category='set'),
+ PDCounterAuthor.objects.all(),
PDCounterBook.objects.all())
for tag in tags:
"""
Create a lucene document referring book id.
"""
- doc = {
- 'book_id': int(book.id),
- }
+ doc = {'book_id': int(book.id)}
if book.parent is not None:
- doc["parent_id"] = int(book.parent.id)
+ doc['parent_id'] = int(book.parent.id)
return doc
def remove_book(self, book_or_id, remove_snippets=True):
footnote_tags = ['pa', 'pt', 'pr', 'pe']
- skip_header_tags = ['autor_utworu', 'nazwa_utworu', 'dzielo_nadrzedne', '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF']
+ skip_header_tags = ['autor_utworu', 'nazwa_utworu', 'dzielo_nadrzedne',
+ '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF']
published_date_re = re.compile("([0-9]+)[\]. ]*$")
book_info = dcparser.parse(open(book.xml_file.path))
fields['slug'] = book.slug
- fields['tags'] = [t.name for t in book.tags]
+ fields['tags'] = [t.name for t in book.tags]
fields['is_book'] = True
# validator, name
match = self.published_date_re.search(book_info.source_name)
if match is not None:
pd = str(match.groups()[0])
- if not pd: pd = ""
+ if not pd:
+ pd = ""
fields["published_date"] = pd
return fields
if master.tag in self.master_tags:
return master
- def index_content(self, book, book_fields={}):
+ def index_content(self, book, book_fields):
"""
Walks the book XML and extract content from it.
Adds parts for each header tag and for each fragment.
if master is None:
return []
- def walker(node, ignore_tags=[]):
-
+ def walker(node, ignore_tags=()):
if node.tag not in ignore_tags:
yield node, None, None
if node.text is not None:
return
def fix_format(text):
- # separator = [u" ", u"\t", u".", u";", u","]
+ # separator = [u" ", u"\t", u".", u";", u","]
if isinstance(text, list):
# need to join it first
text = filter(lambda s: s is not None, content)
# handle fragments and themes.
if start is not None and start.tag == 'begin':
fid = start.attrib['id'][1:]
- fragments[fid] = {'text': [], 'themes': [], 'start_section': position, 'start_header': header.tag}
+ fragments[fid] = {
+ 'text': [], 'themes': [], 'start_section': position, 'start_header': header.tag}
# themes for this fragment
elif start is not None and start.tag == 'motyw':
fid = start.attrib['id'][1:]
- handle_text.append(None)
+ handle_text.append(lambda text: None)
if start.text is not None:
fragments[fid]['themes'] += map(unicode.strip, map(unicode, (start.text.split(','))))
elif end is not None and end.tag == 'motyw':
if fid not in fragments:
continue # a broken <end> node, skip it
frag = fragments[fid]
- if frag['themes'] == []:
+ if not frag['themes']:
continue # empty themes list.
del fragments[fid]
if text is not None and handle_text is not []:
hdl = handle_text[-1]
- if hdl is not None:
- hdl(text)
+ hdl(text)
# in the end, add a section text.
doc = add_part(snippets, header_index=position,
self._processed_hits = None # processed hits
self.snippets = []
self.query_terms = query_terms
+ self._book = None
if 'score' in doc:
self._score = doc['score']
def __unicode__(self):
return u"<SR id=%d %d(%d) hits score=%f %d snippets>" % \
- (self.book_id, len(self._hits), self._processed_hits and len(self._processed_hits) or -1, self._score, len(self.snippets))
+ (self.book_id, len(self._hits),
+ len(self._processed_hits) if self._processed_hits else -1,
+ self._score, len(self.snippets))
def __str__(self):
return unicode(self).encode('utf-8')
return self
def get_book(self):
- if hasattr(self, '_book'):
+ if self._book is not None:
return self._book
self._book = catalogue.models.Book.objects.get(id=self.book_id)
return self._book
# sections not covered by fragments
sect = filter(lambda s: 0 == len(filter(
- lambda f: s[self.POSITION][self.POSITION_INDEX] >= f[self.POSITION][self.POSITION_INDEX]
- and s[self.POSITION][self.POSITION_INDEX] < f[self.POSITION][self.POSITION_INDEX] + f[self.POSITION][self.POSITION_SPAN],
- frags)), sect)
-
- hits = []
+ lambda f: f[self.POSITION][self.POSITION_INDEX] <= s[self.POSITION][self.POSITION_INDEX] <
+ f[self.POSITION][self.POSITION_INDEX] + f[self.POSITION][self.POSITION_SPAN], frags)), sect)
def remove_duplicates(lst, keyfn, compare):
els = {}
def snippet_revision(self, idx=0):
try:
return self.hits[idx]['snippets_revision']
- except:
+ except (IndexError, KeyError):
return None
def __init__(self, default_field="text"):
super(Search, self).__init__(mode='r')
-
def make_term_query(self, query, field='text', modal=operator.or_):
"""
Returns term queries joined by boolean query.
modal - applies to boolean query
fuzzy - should the query by fuzzy.
"""
- if query is None: query = ''
+ if query is None:
+ query = ''
q = self.index.Q()
- q = reduce(modal, map(lambda s: self.index.Q(**{field: s}),
- query.split(r" ")), q)
+ q = reduce(modal, map(lambda s: self.index.Q(**{field: s}), query.split(r" ")), q)
return q
def search_phrase(self, searched, field='text', book=False,
filters=None,
snippets=False):
- if filters is None: filters = []
- if book: filters.append(self.index.Q(is_book=True))
+ if filters is None:
+ filters = []
+ if book:
+ filters.append(self.index.Q(is_book=True))
q = self.index.query(**{field: searched})
q = self.apply_filters(q, filters).field_limit(score=True, all_fields=True)
def search_some(self, searched, fields, book=True,
filters=None, snippets=True, query_terms=None):
assert isinstance(fields, list)
- if filters is None: filters = []
- if book: filters.append(self.index.Q(is_book=True))
+ if filters is None:
+ filters = []
+ if book:
+ filters.append(self.index.Q(is_book=True))
query = self.index.Q()
res = query.execute()
return [SearchResult(found, how_found='search_some', query_terms=query_terms) for found in res]
-
def search_everywhere(self, searched, query_terms=None):
"""
Tries to use search terms to match different fields of book (or its parts).
"""
Search for Tag objects using query.
"""
- if not filters: filters = []
+ if not filters:
+ filters = []
if not pdcounter:
filters.append(~self.index.Q(is_pdcounter=True))
res = self.apply_filters(query, filters).execute()
is_pdcounter = doc.get('is_pdcounter', False)
category = doc.get('tag_category')
try:
- if is_pdcounter == True:
+ if is_pdcounter:
if category == 'pd_author':
tag = PDCounterAuthor.objects.get(id=doc.get('tag_id'))
elif category == 'pd_book':
tag = PDCounterBook.objects.get(id=doc.get('tag_id'))
tag.category = 'pd_book' # make it look more lik a tag.
else:
- print ("Warning. cannot get pdcounter tag_id=%d from db; cat=%s" % (int(doc.get('tag_id')), category)).encode('utf-8')
+ # WTF
+ print ("Warning. cannot get pdcounter tag_id=%d from db; cat=%s" % (
+ int(doc.get('tag_id')), category)).encode('utf-8')
pd_tags.append(tag)
else:
tag = catalogue.models.Tag.objects.get(id=doc.get("tag_id"))
tags.append(tag)
- except catalogue.models.Tag.DoesNotExist: pass
- except PDCounterAuthor.DoesNotExist: pass
- except PDCounterBook.DoesNotExist: pass
+ except catalogue.models.Tag.DoesNotExist:
+ pass
+ except PDCounterAuthor.DoesNotExist:
+ pass
+ except PDCounterBook.DoesNotExist:
+ pass
tags_slugs = set(map(lambda t: t.slug, tags))
- tags = tags + filter(lambda t: not t.slug in tags_slugs, pd_tags)
+ tags = tags + filter(lambda t: t.slug not in tags_slugs, pd_tags)
log.debug('search_tags: %s' % tags)
for r in res:
try:
bid = r['book_id']
- if not bid in bks_found:
+ if bid not in bks_found:
bks.append(catalogue.models.Book.objects.get(id=bid))
bks_found.add(bid)
- except catalogue.models.Book.DoesNotExist: pass
+ except catalogue.models.Book.DoesNotExist:
+ pass
return bks
-
@staticmethod
def apply_filters(query, filters):
"""
Apply filters to a query
"""
- if filters is None: filters = []
+ if filters is None:
+ filters = []
filters = filter(lambda x: x is not None, filters)
for f in filters:
query = query.query(f)
from optparse import make_option
+
def query_yes_no(question, default="yes"):
"""Ask a yes/no question via raw_input() and return their answer.
The "answer" return value is one of "yes" or "no".
"""
- valid = {"yes":True, "y":True, "ye":True,
- "no":False, "n":False}
- if default == None:
+ valid = {"yes": True, "y": True, "ye": True,
+ "no": False, "n": False}
+ if default is None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif choice in valid:
return valid[choice]
else:
- sys.stdout.write("Please respond with 'yes' or 'no' "\
- "(or 'y' or 'n').\n")
+ sys.stdout.write("Please respond with 'yes' or 'no' (or 'y' or 'n').\n")
+
class Command(BaseCommand):
help = 'Reindex everything.'
option_list = BaseCommand.option_list + (
make_option('-n', '--book-id', action='store_true', dest='book_id', default=False,
- help='book id instead of slugs'),
+ help='book id instead of slugs'),
make_option('-t', '--just-tags', action='store_true', dest='just_tags', default=False,
- help='just reindex tags'),
+ help='just reindex tags'),
)
+
def handle(self, *args, **opts):
from catalogue.models import Book
from search.index import Index
from sys import stdout
from django.conf import settings
+
class Command(BaseCommand):
help = 'Reindex everything.'
args = ''
option_list = BaseCommand.option_list + (
make_option('-C', '--check-just-read', action='store_true', dest='check', default=False,
- help='Check snippets utf-8'),
+ help='Check snippets utf-8'),
make_option('-c', '--check', action='store_true', dest='check2', default=False,
- help='Check snippets utf-8 by walking through index'),
+ help='Check snippets utf-8 by walking through index'),
)
-
def handle(self, *args, **opts):
- from catalogue.models import Book
- from search.index import Search
+ from search.index import Search, Snippets
if opts['check']:
sfn = glob(settings.SEARCH_INDEX+'snippets/*')
doc = reader.document(did)
if doc and doc.get('book_id'):
bkid = int(doc.get('book_id'))
- #import pdb; pdb.set_trace()
+ # import pdb; pdb.set_trace()
stdout.write("\r%d / %d" % (did, numdocs))
stdout.flush()
- ss = doc.get('snippet_position')
- sl = doc.get('snippet_length')
+ ss = doc.get('snippet_position')
+ sl = doc.get('snippet_length')
if ss and sl:
+ # WTF (nie było zaimportowane)
snips = Snippets(bkid)
try:
- txt = snips.get((ss,sl))
+ txt = snips.get((ss, sl))
assert len(txt) == sl
except UnicodeDecodeError, ude:
stdout.write("\nerror in snippets %d\n" % bkid)
raise ude
stdout.write("\ndone.\n")
-
results.append(res)
return results
- def search_phrase(self, searched, field='text', book=False,
- filters=None, snippets=False):
+ def search_phrase(self, searched, field='text', book=False, filters=None, snippets=False):
return self._find_some_books(snippets)
def search_some(self, searched, fields, book=True, filters=None, snippets=True, query_terms=None):
return self._find_some_books(snippets, query_terms)
+ # WTF
def search_books(self, query, filters=None, max_results=10):
return self._find_some_books(snippets, max_results=max_results)
'fragment' in h or
result.snippets[idx] is not None,
enumerate(result.hits))
- # print "[tmpl: from %d hits selected %d]" % (len(result.hits), len(hits))
+ # print "[tmpl: from %d hits selected %d]" % (len(result.hits), len(hits))
for (idx, hit) in hits:
# currently we generate one snipper per hit though.
snip = result.snippets[idx]
# fix some formattting
snip = re.subn(r"(^[ \t\n]+|[ \t\n]+$)", u"",
- re.subn(r"[ \t\n]*\n[ \t\n]*", u"\n", snip)[0])[0]
+ re.subn(r"[ \t\n]*\n[ \t\n]*", u"\n", snip)[0])[0]
snip = snip.replace("\n", "<br />").replace('---', '—')
hit['snippet'] = snip
from django.conf import settings
from django.test.utils import override_settings
from catalogue.test_utils import WLTestCase, get_fixture
-from os import path
import tempfile
-from catalogue.models import Book, Tag
-from search.index import Index, Search, SearchResult
+from catalogue.models import Book
+from search.index import Index, Search
import catalogue
import opds
-@override_settings(
- SEARCH_INDEX = tempfile.mkdtemp(prefix='djangotest_search_'),
-)
+@override_settings(SEARCH_INDEX=tempfile.mkdtemp(prefix='djangotest_search_'))
@skipIf(getattr(settings, 'NO_SEARCH_INDEX', False),
- u'Requires search server and NO_SEARCH_INDEX=False.')
+ u'Requires search server and NO_SEARCH_INDEX=False.')
class BookSearchTests(WLTestCase):
def setUp(self):
WLTestCase.setUp(self)
# a = SearchResult.aggregate(books)
# # just one fragment hit.
# assert len(a[0].hits) == 1
-
#
from django.conf.urls import patterns, url
-urlpatterns = patterns('search.views',
+urlpatterns = patterns(
+ 'search.views',
url(r'^$', 'main', name='search'),
url(r'^hint/$', 'hint', name='search_hint'),
)
-
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from django.conf import settings
-from django.shortcuts import render_to_response, get_object_or_404
+from django.shortcuts import render_to_response
from django.template import RequestContext
from django.views.decorators import cache
-from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponsePermanentRedirect, JsonResponse
+from django.http import HttpResponse, JsonResponse
from django.utils.translation import ugettext as _
from catalogue.utils import split_tags
-from catalogue.models import Book, Tag, Fragment
+from catalogue.models import Book
from pdcounter.models import Author as PDCounterAuthor, BookStub as PDCounterBook
from search.index import Search, SearchResult
from suggest.forms import PublishingSuggestForm
import re
-#import enchant
import json
@cache.never_cache
def main(request):
- results = {}
-
- results = None
- query = None
-
query = request.GET.get('q', '')
if len(query) < 2:
- return render_to_response('catalogue/search_too_short.html',
- {'prefix': query},
+ return render_to_response(
+ 'catalogue/search_too_short.html', {'prefix': query},
context_instance=RequestContext(request))
query = remove_query_syntax_chars(query)
theme_terms = search.index.analyze(text=query, field="themes_pl") \
+ search.index.analyze(text=query, field="themes")
- # change hints
+ # change hints
tags = search.hint_tags(query, pdcounter=True, prefix=False)
tags = split_tags(tags)
# return HttpResponseRedirect(results[0].book.get_absolute_url())
if len(results) == 0:
form = PublishingSuggestForm(initial={"books": query + ", "})
- return render_to_response('catalogue/search_no_hits.html',
- {'tags': tags,
- 'prefix': query,
- "form": form,
- 'did_you_mean': suggestion},
+ return render_to_response(
+ 'catalogue/search_no_hits.html',
+ {
+ 'tags': tags,
+ 'prefix': query,
+ 'form': form,
+ 'did_you_mean': suggestion
+ },
context_instance=RequestContext(request))
- return render_to_response('catalogue/search_multiple_hits.html',
- {'tags': tags,
- 'prefix': query,
- 'results': {'author': author_results,
- 'translator': translator_results,
- 'title': title_results,
- 'content': text_phrase,
- 'other': everywhere},
- 'did_you_mean': suggestion},
+ return render_to_response(
+ 'catalogue/search_multiple_hits.html',
+ {
+ 'tags': tags,
+ 'prefix': query,
+ 'results': {
+ 'author': author_results,
+ 'translator': translator_results,
+ 'title': title_results,
+ 'content': text_phrase,
+ 'other': everywhere
+ },
+ 'did_you_mean': suggestion
+ },
context_instance=RequestContext(request))
(None, {'fields': ('book', 'text', 'small', 'vip', 'link', 'sticky')}),
(
_('Background'),
- {'fields': ('image', 'image_shift', 'image_title', 'image_author',
- 'image_link', 'image_license', 'image_license_link')
- }
- )
+ {'fields': (
+ 'image', 'image_shift', 'image_title', 'image_author',
+ 'image_link', 'image_license', 'image_license_link')},
+ )
)
def nonempty_text(self, cite):
def __init__(self, book, user, *args, **kwargs):
super(UserSetsForm, self).__init__(*args, **kwargs)
self.fields['set_ids'] = forms.ChoiceField(
- choices=[(tag.id, tag.name) for tag in
- Tag.objects.filter(category='set', user=user).iterator()],
+ choices=[(tag.id, tag.name) for tag in Tag.objects.filter(category='set', user=user).iterator()],
)
self._user = user
data = kwargs.setdefault('data', {})
if 'tags' not in data and user.is_authenticated():
- data['tags'] = ', '.join(t.name
- for t in obj.tags.filter(category='set', user=user).iterator() if t.name)
+ data['tags'] = ', '.join(t.name for t in obj.tags.filter(category='set', user=user).iterator() if t.name)
super(ObjectSetsForm, self).__init__(*args, **kwargs)
def save(self, request):
- tags = [get_set(self._user, tag_name.strip())
- for tag_name in self.cleaned_data['tags'].split(',')]
+ tags = [get_set(self._user, tag_name.strip()) for tag_name in self.cleaned_data['tags'].split(',')]
set_sets(self._user, self._obj, tags)
return {"like": True}
def save(self, user, commit=True):
name = self.cleaned_data['name']
- new_set = Tag(name=name, slug=utils.get_random_hash(name), sort_key=name.lower(),
- category='set', user=user)
+ new_set = Tag(name=name, slug=utils.get_random_hash(name), sort_key=name.lower(), category='set', user=user)
new_set.save()
return new_set
class Cite(models.Model):
book = models.ForeignKey(Book, verbose_name=_('book'), null=True, blank=True)
text = models.TextField(_('text'))
- small = models.BooleanField(_('small'), default=False,
- help_text=_('Make this cite display smaller.'))
+ small = models.BooleanField(_('small'), default=False, help_text=_('Make this cite display smaller.'))
vip = models.CharField(_('VIP'), max_length=128, null=True, blank=True)
link = models.URLField(_('link'))
sticky = models.BooleanField(_('sticky'), default=False, db_index=True,
- help_text=_('Sticky cites will take precedense.'))
+ help_text=_('Sticky cites will take precedense.'))
- image = models.ImageField(_('image'), upload_to='social/cite',
- null=True, blank=True,
+ image = models.ImageField(
+ _('image'), upload_to='social/cite', null=True, blank=True,
help_text=_('Best image is exactly 975px wide and weights under 100kB.'))
- image_shift = models.IntegerField(_('shift'), null=True, blank=True,
- help_text=_(u'Vertical shift, in percents. 0 means top, 100 is bottom. Default is 50%.'))
- image_title = models.CharField(_('title'), max_length=255,
- null=True, blank=True)
- image_author = models.CharField(_('author'),
- max_length=255, blank=True, null=True)
+ image_shift = models.IntegerField(
+ _('shift'), null=True, blank=True,
+ help_text=_(u'Vertical shift, in percents. 0 means top, 100 is bottom. Default is 50%.'))
+ image_title = models.CharField(_('title'), max_length=255, null=True, blank=True)
+ image_author = models.CharField(_('author'), max_length=255, blank=True, null=True)
image_link = models.URLField(_('link'), blank=True, null=True)
- image_license = models.CharField(_('license name'),
- max_length=255, blank=True, null=True)
+ image_license = models.CharField(_('license name'), max_length=255, blank=True, null=True)
image_license_link = models.URLField(_('license link'), blank=True, null=True)
class Meta:
return None
book = Book.objects.get(pk=book_id)
lks = likes(request.user, book, request)
+
def get_value():
if not lks:
return ''
from django.views.decorators.cache import never_cache
from social.views import ObjectSetsFormView
-urlpatterns = patterns('social.views',
+urlpatterns = patterns(
+ 'social.views',
url(r'^lektura/(?P<slug>[a-z0-9-]+)/lubie/$', 'like_book', name='social_like_book'),
url(r'^lektura/(?P<slug>[a-z0-9-]+)/nie_lubie/$', 'unlike_book', name='social_unlike_book'),
url(r'^lektura/(?P<slug>[a-z0-9-]+)/polki/$', never_cache(ObjectSetsFormView()), name='social_book_sets'),
url(r'^cite_main/(?P<pk>\d+)\.(?P<lang>.+)\.html$', 'cite', {'main': True}, name='social_cite_main'),
url(r'^cite_info/(?P<pk>\d+).html$', 'cite_info', name='social_cite_info'),
- #~ url(r'^polki/(?P<shelf>[a-zA-Z0-9-]+)/formaty/$', 'shelf_book_formats', name='shelf_book_formats'),
- #~ url(r'^polki/(?P<shelf>[a-zA-Z0-9-]+)/(?P<slug>%s)/usun$' % SLUG, 'remove_from_shelf', name='remove_from_shelf'),
- #~ url(r'^polki/$', 'user_shelves', name='user_shelves'),
- #~ url(r'^polki/(?P<slug>[a-zA-Z0-9-]+)/usun/$', 'delete_shelf', name='delete_shelf'),
- #~ url(r'^polki/(?P<slug>[a-zA-Z0-9-]+)\.zip$', 'download_shelf', name='download_shelf'),
- #~ url(r'^polki/nowa/$', 'new_set', name='new_set'),
+ # url(r'^polki/(?P<shelf>[a-zA-Z0-9-]+)/formaty/$', 'shelf_book_formats', name='shelf_book_formats'),
+ # url(r'^polki/(?P<shelf>[a-zA-Z0-9-]+)/(?P<slug>%s)/usun$' % SLUG, 'remove_from_shelf', name='remove_from_shelf'),
+ # url(r'^polki/$', 'user_shelves', name='user_shelves'),
+ # url(r'^polki/(?P<slug>[a-zA-Z0-9-]+)/usun/$', 'delete_shelf', name='delete_shelf'),
+ # url(r'^polki/(?P<slug>[a-zA-Z0-9-]+)\.zip$', 'download_shelf', name='download_shelf'),
+ # url(r'^polki/nowa/$', 'new_set', name='new_set'),
)
if not hasattr(request, 'social_likes'):
# tuple: unchecked, checked, liked
- request.social_likes = defaultdict(lambda:(set(), set(), set()))
+ request.social_likes = defaultdict(lambda: (set(), set(), set()))
ct = ContentType.objects.get_for_model(type(work))
likes_t = request.social_likes[ct.pk]
return work.pk in likes_t[2]
else:
likes_t[0].add(work.pk)
+
def _likes():
if likes_t[0]:
ids = tuple(likes_t[0])
try:
tag = Tag.objects.get(category='set', user=user, name=name)
except Tag.DoesNotExist:
- tag = Tag.objects.create(category='set', user=user, name=name,
- slug=utils.get_random_hash(name), sort_key=name.lower())
+ tag = Tag.objects.create(
+ category='set', user=user, name=name, slug=utils.get_random_hash(name), sort_key=name.lower())
return tag
def replace_char(m):
char = m.group()
- if char_map.has_key(char):
+ if char in char_map:
order = char_order.get(char, 0)
return "%s~%d" % (char_map[char], order)
else:
value = unicode(value, 'utf-8')
# try to replace chars
- value = re.sub('[^a-zA-Z0-9\\s\\-]{1}', replace_char, value)
+ value = re.sub('[^a-zA-Z0-9\\s\\-]', replace_char, value)
value = value.lower()
value = re.sub(r'[^a-z0-9~]+', ' ', value)
if self.sprite:
self.sprite.delete(save=False)
- self.sprite.save('sponsorzy/sprite/%s-%d.png' % (self.name, time.time()), ContentFile(imgstr.getvalue()), save=False)
+ self.sprite.save('sponsorzy/sprite/%s-%d.png' % (
+ self.name, time.time()), ContentFile(imgstr.getvalue()), save=False)
def html(self):
return self._html
def __unicode__(self):
return self.name
-
#
from django.conf.urls import patterns, url
-urlpatterns = patterns('sponsors.views',
+urlpatterns = patterns(
+ 'sponsors.views',
url(r'^page/(?P<name>.+)\.html$', 'page', name='sponsor_page'),
)
output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
# TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically.
- output.append(u'$("#id_%s").sponsorsFooter({sponsors: [%s]}); });</script>\n' %
- (name, sponsors_js))
+ output.append(u'$("#id_%s").sponsorsFooter({sponsors: [%s]}); });</script>\n' % (name, sponsors_js))
return mark_safe(u''.join(output))
from suggest.models import Suggestion, PublishingSuggestion
+
class SuggestionAdmin(admin.ModelAdmin):
list_display = ('created_at', 'contact', 'user', 'description')
contact = self.cleaned_data['contact']
description = self.cleaned_data['description']
- suggestion = Suggestion(contact=contact,
- description=description, ip=request.META['REMOTE_ADDR'])
+ suggestion = Suggestion(contact=contact, description=description, ip=request.META['REMOTE_ADDR'])
if request.user.is_authenticated():
suggestion.user = request.user
suggestion.save()
except ValidationError:
pass
else:
- send_mail(u'[WolneLektury] ' +
- ugettext(u'Thank you for your suggestion.'),
- ugettext(u"""\
+ send_mail(u'[WolneLektury] ' + ugettext(u'Thank you for your suggestion.'),
+ ugettext(u"""\
Thank you for your comment on WolneLektury.pl.
The suggestion has been referred to the project coordinator.""") +
-u"""
-
---
-""" + ugettext(u'''Message sent automatically. Please do not reply.'''),
- 'no-reply@wolnelektury.pl', [contact], fail_silently=True)
+ u'\n\n-- \n' + ugettext(u'''Message sent automatically. Please do not reply.'''),
+ 'no-reply@wolnelektury.pl', [contact], fail_silently=True)
class PublishingSuggestForm(forms.Form):
books = forms.CharField(label=_('books'), widget=forms.Textarea, required=False)
audiobooks = forms.CharField(label=_('audiobooks'), widget=forms.Textarea, required=False)
- def clean(self, *args, **kwargs):
+ def clean(self):
if not self.cleaned_data['books'] and not self.cleaned_data['audiobooks']:
msg = ugettext(u"One of these fields is required.")
self._errors["books"] = self.error_class([msg])
self._errors["audiobooks"] = self.error_class([msg])
- return super(PublishingSuggestForm, self).clean(*args, **kwargs)
+ return super(PublishingSuggestForm, self).clean()
def save(self, request):
contact = self.cleaned_data['contact']
books = self.cleaned_data['books']
audiobooks = self.cleaned_data['audiobooks']
- suggestion = PublishingSuggestion(contact=contact, books=books,
+ suggestion = PublishingSuggestion(
+ contact=contact, books=books,
audiobooks=audiobooks, ip=request.META['REMOTE_ADDR'])
if request.user.is_authenticated():
suggestion.user = request.user
except ValidationError:
pass
else:
- send_mail(u'[WolneLektury] ' +
- ugettext(u'Thank you for your suggestion.'),
- ugettext(u"""\
+ send_mail(
+ u'[WolneLektury] ' + ugettext(u'Thank you for your suggestion.'),
+ ugettext(u"""\
Thank you for your comment on WolneLektury.pl.
The suggestion has been referred to the project coordinator.""") +
-u"""
-
---
-""" + ugettext(u'''Message sent automatically. Please do not reply.'''),
- 'no-reply@wolnelektury.pl', [contact], fail_silently=True)
+ u"\n\n-- \n" + ugettext(u'''Message sent automatically. Please do not reply.'''),
+ 'no-reply@wolnelektury.pl', [contact], fail_silently=True)
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
+
class Suggestion(models.Model):
contact = models.CharField(_('contact'), blank=True, max_length=120)
description = models.TextField(_('description'), blank=True)
from django.conf.urls import patterns, url
from suggest import views
-urlpatterns = patterns('',
+urlpatterns = patterns(
+ '',
url(r'^$', views.SuggestionFormView(), name='suggest'),
url(r'^plan/$', views.PublishingSuggestionFormView(), name='suggest_publishing'),
)
-
WAITER_MAX_QUEUE = settings.WAITER_MAX_QUEUE
except AttributeError:
WAITER_MAX_QUEUE = 20
-
#
from django.conf.urls import patterns, url
-urlpatterns = patterns('waiter.views',
+urlpatterns = patterns(
+ 'waiter.views',
url(r'^(?P<path>.*)$', 'wait', name='waiter'),
)
#
from django.apps import AppConfig
+
class WLCoreConfig(AppConfig):
name = 'wolnelektury'
+# -*- 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 __future__ import absolute_import
import os
import sys
+from celery import Celery
+from django.conf import settings
+
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path = [
os.path.join(ROOT, 'lib/librarian'),
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wolnelektury.settings')
-from celery import Celery
-from django.conf import settings
-
app = Celery('wolnelektury')
app.config_from_object('django.conf:settings')
#
from django.conf import settings
+
def extra_settings(request):
return {
'STATIC_URL': settings.STATIC_URL,
from django.core.management.base import BaseCommand
from django.core.management import call_command
from .translation2po import get_languages
+from wolnelektury.utils import makedirs
import os
import shutil
def generate(self, languages):
pass
+
def copy_f(frm, to):
- "I can create a necessary dest directiories, yey!"
- if not os.path.exists(os.path.dirname(to)):
- os.makedirs(os.path.dirname(to))
+ makedirs(os.path.dirname(to))
shutil.copyfile(frm, to)
+
class AppLocale(Locale):
def __init__(self, appmod):
self.app = appmod
lc = lc[0]
if os.path.exists(os.path.join(self.path, 'locale', lc)):
copy_f(os.path.join(self.path, 'locale', lc, 'LC_MESSAGES', 'django.po'),
- os.path.join(output_directory, lc, self.name + '.po'))
-
+ os.path.join(output_directory, lc, self.name + '.po'))
def load(self, input_directory, languages):
for lc in zip(*languages)[0]:
if os.path.exists(os.path.join(input_directory, lc, self.name + '.po')):
out = os.path.join(self.path, 'locale', lc, 'LC_MESSAGES', 'django.po')
- if not os.path.exists(os.path.dirname(out)):
- os.makedirs(os.path.dirname(out))
- copy_f(os.path.join(input_directory, lc, self.name + '.po'),
- out)
+ makedirs(os.path.dirname(out))
+ copy_f(os.path.join(input_directory, lc, self.name + '.po'), out)
wd = os.getcwd()
os.chdir(self.path)
finally:
os.chdir(wd)
-
def generate(self, languages):
wd = os.getcwd()
os.chdir(self.path)
for lc in zip(*languages)[0]:
if os.path.exists(self.po_file(lc)):
copy_f(self.po_file(lc),
- os.path.join(output_directory, lc, self.name + '.po'))
+ os.path.join(output_directory, lc, self.name + '.po'))
def load(self, input_directory, languages):
for lc in zip(*languages)[0]:
copy_f(os.path.join(input_directory, lc, self.name + '.po'),
- self.po_file(lc))
+ self.po_file(lc))
os.system('pybabel compile -D django -d %s' % os.path.dirname(self.out_file))
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
- make_option('-l', '--load', help='load locales back to source', action='store_true', dest='load', default=False),
+ make_option('-l', '--load', help='load locales back to source', action='store_true', dest='load',
+ default=False),
make_option('-L', '--lang', help='load just one language', dest='lang', default=None),
make_option('-d', '--directory', help='load from this directory', dest='directory', default=None),
make_option('-o', '--outfile', help='Resulting zip file', dest='outfile', default='./wl-locale.zip'),
- make_option('-m', '--merge', help='Use git to merge. Please use with clean working directory.', action='store_true', dest='merge', default=False),
+ make_option('-m', '--merge', help='Use git to merge. Please use with clean working directory.',
+ action='store_true', dest='merge', default=False),
make_option('-M', '--message', help='commit message', dest='message', default='New locale'),
-
- )
+ )
help = 'Make a locale pack'
args = ''
rf.write(rev)
rf.close()
-
cwd = os.getcwd()
try:
os.chdir(os.path.dirname(out_dir))
self.system('zip -r %s %s' % (os.path.join(cwd, packname_b+'.zip'), os.path.basename(out_dir)))
finally:
os.chdir(cwd)
- # shutil.make_archive(packname_b, fmt, root_dir=os.path.dirname(out_dir), base_dir=os.path.basename(out_dir))
+ # shutil.make_archive(packname_b, fmt, root_dir=os.path.dirname(out_dir),
+ # base_dir=os.path.basename(out_dir))
finally:
shutil.rmtree(tmp_dir, ignore_errors=True)
print "Directory not provided or does not exist, please use -d"
sys.exit(1)
- if options['merge']: self.merge_setup(options['directory'])
+ if options['merge']:
+ self.merge_setup(options['directory'])
self.load(options)
- if options['merge']: self.merge_finish(options['message'])
+ if options['merge']:
+ self.merge_finish(options['message'])
else:
self.save(options)
from optparse import make_option
from django.conf import settings
from django.core.management.base import BaseCommand
-from django.core.management.color import color_style
from django.db import models
import polib
-import modeltranslation.models
from modeltranslation.translator import translator, NotRegistered
+from wolnelektury.utils import makedirs
+
def metadata(language=''):
- "get metadata for PO, given language code"
+ """get metadata for PO, given language code"""
t = time.strftime('%Y-%m-%d %H:%M%z')
return {
def make_po(language=''):
- "Create new POFile object for language code"
+ """Create new POFile object for language code"""
po = polib.POFile()
po.metadata = metadata(language)
return po
def get_languages(langs):
- if not langs: return settings.LANGUAGES
+ if not langs:
+ return settings.LANGUAGES
langs = langs.split(',')
lm = dict(settings.LANGUAGES)
return map(lambda l: (l, lm.get(l, l)), langs)
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
- make_option('-d', '--directory', help='Specify which directory should hold generated PO files', dest='directory', default=''),
- make_option('-l', '--load', help='load locales back to source', action='store_true', dest='load', default=False),
+ make_option('-d', '--directory', help='Specify which directory should hold generated PO files',
+ dest='directory', default=''),
+ make_option('-l', '--load', help='load locales back to source', action='store_true', dest='load',
+ default=False),
make_option('-L', '--language', help='locales to load', dest='lang', default=None),
make_option('-n', '--poname', help='name of the po file [no extension]', dest='poname', default=None),
- make_option('-k', '--keep-running', help='keep running even when missing an input file', dest='keep_running', default=False, action='store_true'),
- )
+ make_option('-k', '--keep-running', help='keep running even when missing an input file', dest='keep_running',
+ default=False, action='store_true'),
+ )
help = 'Export models from app to po files'
args = 'app'
def get_models(self, app):
for mdname in dir(app.models):
- if mdname[0] == '_': continue
+ if mdname[0] == '_':
+ continue
md = getattr(app.models, mdname)
try:
assert issubclass(md, models.Model)
yield (md, opts)
def handle(self, appname, **options):
- if not options['poname']: options['poname'] = appname
+ if not options['poname']:
+ options['poname'] = appname
app = __import__(appname)
if options['load']:
objects = {}
modmod = {}
for md, opts in self.get_models(app):
- if not md.__name__ in objects:
+ if md.__name__ not in objects:
objects[md.__name__] = {}
modmod['model'] = md
cur_lang = locfld.language
try:
po = pofiles[cur_lang]
- except:
+ except KeyError:
po = make_po(cur_lang)
pofiles[cur_lang] = po
v = locfld.value_from_object(obj) or ''
directory = options['directory']
for lng, po in pofiles.items():
- os.makedirs(os.path.join(directory, lng))
+ makedirs(os.path.join(directory, lng))
po.save(os.path.join(directory, lng, '%s.po' % options['poname']))
-
+# -*- 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.
+#
import cProfile
import functools
import os
_object = None
+
def profile(meth):
def _wrapper(self, *args, **kwargs):
- object = self
- setattr(object, "__%s" % meth.__name__, meth)
- cProfile.runctx('object.__%s(object, *args, **kwargs)' % (meth.__name__, ), globals(), locals(),
- "profile.%d" % os.getpid())
+ setattr(self, "__%s" % meth.__name__, meth)
+ cProfile.runctx(
+ 'object.__%s(object, *args, **kwargs)' % (meth.__name__, ), globals(), locals(),
+ 'profile.%d' % os.getpid())
functools.update_wrapper(_wrapper, meth)
return _wrapper
-
-# Orignal version taken from http://www.djangosnippets.org/snippets/186/
+# -*- coding: utf-8 -*-
+# Original version taken from http://www.djangosnippets.org/snippets/186/
# Original author: udfalkso
# Modified by: Shwagroo Team
import sys
import os
import re
-import hotshot, hotshot.stats
+import hotshot
+import hotshot.stats
import tempfile
import StringIO
import pprint
from django.db import connection
-words_re = re.compile( r'\s+' )
+words_re = re.compile(r'\s+')
group_prefix_re = [
- re.compile( "^.*/django/[^/]+" ),
- re.compile( "^(.*)/[^/]+$" ), # extract module path
- re.compile( ".*" ), # catch strange entries
+ re.compile("^.*/django/[^/]+"),
+ re.compile("^(.*)/[^/]+$"), # extract module path
+ re.compile(".*"), # catch strange entries
]
WARNING: It uses hotshot profiler which is not thread safe.
"""
def process_request(self, request):
- if (settings.DEBUG or request.user.is_superuser) and request.GET.has_key('prof'):
+ if (settings.DEBUG or request.user.is_superuser) and 'prof' in request.GET:
connection.queries = []
self.tmpfile = tempfile.mktemp()
self.prof = hotshot.Profile(self.tmpfile)
def process_view(self, request, callback, callback_args, callback_kwargs):
- if (settings.DEBUG or request.user.is_superuser) and request.GET.has_key('prof'):
+ if (settings.DEBUG or request.user.is_superuser) and 'prof' in request.GET:
return self.prof.runcall(callback, request, *callback_args, **callback_kwargs)
def get_group(self, file):
for g in group_prefix_re:
- name = g.findall( file )
+ name = g.findall(file)
if name:
return name[0]
def get_summary(self, results_dict, sum):
- list = [ (item[1], item[0]) for item in results_dict.items() ]
- list.sort( reverse = True )
+ list = [(item[1], item[0]) for item in results_dict.items()]
+ list.sort(reverse=True)
list = list[:40]
res = " tottime\n"
foo = 0
else:
foo = 100*item[0]/sum
- res += "%4.1f%% %7.3f %s\n" % (foo, item[0], item[1] )
+ res += "%4.1f%% %7.3f %s\n" % (foo, item[0], item[1])
return res
sum = 0
for s in stats_str:
- fields = words_re.split(s);
+ fields = words_re.split(s)
if len(fields) == 7:
time = float(fields[2])
sum += time
file = fields[6].split(":")[0]
- if not file in mystats:
+ if file not in mystats:
mystats[file] = 0
mystats[file] += time
group = self.get_group(file)
- if not group in mygroups:
- mygroups[ group ] = 0
- mygroups[ group ] += time
+ if group not in mygroups:
+ mygroups[group] = 0
+ mygroups[group] += time
return "<pre>" + \
- " ---- By file ----\n\n" + self.get_summary(mystats,sum) + "\n" + \
- " ---- By group ---\n\n" + self.get_summary(mygroups,sum) + \
+ " ---- By file ----\n\n" + self.get_summary(mystats, sum) + "\n" + \
+ " ---- By group ---\n\n" + self.get_summary(mygroups, sum) + \
"</pre>"
def process_response(self, request, response):
- if (settings.DEBUG or request.user.is_superuser) and request.GET.has_key('prof'):
+ if (settings.DEBUG or request.user.is_superuser) and 'prof' in request.GET:
self.prof.close()
out = StringIO.StringIO()
response.content += pprint.pformat(connection.queries)
return response
-
+# -*- 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.
+#
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
SOCIALACCOUNT_QUERY_EMAIL = True
-SOCIALACCOUNT_PROVIDERS = \
- { 'openid':
- { 'SERVERS':
- [dict(id='google',
- name='Google',
- openid_url='https://www.google.com/accounts/o8/id')]}}
+SOCIALACCOUNT_PROVIDERS = {
+ 'openid': {
+ 'SERVERS': [{
+ 'id': 'google',
+ 'name': 'Google',
+ 'openid_url': 'https://www.google.com/accounts/o8/id'}],
+ },
+}
+# -*- 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 os import path
from .paths import PROJECT_DIR
DATABASES = {
'default': {
- 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
- 'NAME': path.join(PROJECT_DIR, 'dev.db'), # Or path to database file if using sqlite3.
+ 'ENGINE': 'django.db.backends.sqlite3', # 'postgresql_psycopg2'
+ 'NAME': path.join(PROJECT_DIR, 'dev.db'),
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
TEMPLATE_LOADERS = [
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
-# 'django.template.loaders.eggs.Loader',
+ # 'django.template.loaders.eggs.Loader',
]
+# -*- 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.
+#
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
+# -*- 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.
+#
BROKER_URL = 'django://'
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
+# -*- 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.
+#
HONEYPOT_FIELD_NAME = 'miut'
PAGINATION_INVALID_PAGE_RAISES_404 = True
THUMBNAIL_QUALITY = 95
MODELTRANSLATION_PREPOPULATE_LANGUAGE = 'pl'
MIGRATION_MODULES = {
- 'getpaid' : 'wolnelektury.migrations.getpaid',
- 'piston' : 'wolnelektury.migrations.piston',
+ 'getpaid': 'wolnelektury.migrations.getpaid',
+ 'piston': 'wolnelektury.migrations.piston',
}
GETPAID_ORDER_DESCRIPTION = "{% load funding_tags %}{{ order|sanitize_payment_title }}"
+# -*- 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.
+#
import os
from .paths import VAR_DIR
# -*- 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 os import path
from .paths import PROJECT_DIR
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'pl'
-gettext = lambda s: s
+
+def gettext(s):
+ return s
LANGUAGES = tuple(sorted([
('pl', u'polski'),
('ru', u'русский'),
('es', u'español'),
('uk', u'українська'),
-# ('jp', u'日本語'),
+ # ('jp', u'日本語'),
('it', u'italiano'),
], key=lambda x: x[0]))
+# -*- 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 os import path
PROJECT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
ROOT_DIR = path.dirname(path.dirname(PROJECT_DIR))
VAR_DIR = path.join(ROOT_DIR, 'var')
-
+# -*- 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 os import path
from .paths import VAR_DIR
PIPELINE = {
'PIPELINE_ENABLED': False,
'STYLESHEETS': {
- 'main': {
- # styles both for mobile and for big screen
- 'source_filenames': [
- 'css/jquery.countdown.css',
- 'jplayer/jplayer.blue.monday.css',
-
- 'sponsors/css/sponsors.css',
-
- 'uni_form/uni-form.css',
- 'uni_form/default.uni-form.css',
-
- 'css/ui-lightness/jquery-ui-1.8.16.custom.css',
-
- 'scss/main.scss',
- ],
- 'output_filename': 'css/compressed/main.css',
- },
- 'book': {
- 'source_filenames': [
- 'css/master.book.css',
- ],
- 'output_filename': 'css/compressed/book.css',
- },
- 'book_text': {
- 'source_filenames': [
- 'scss/book_text.scss',
- 'css/new.book.css',
-
- 'css/master.picture.css',
- ],
- 'output_filename': 'css/compressed/book_text.css',
- },
- 'picture': {
- 'source_filenames': [
- 'css/master.book.css',
- 'css/master.picture.css',
- ],
- 'output_filename': 'css/compressed/picture.css',
- },
- 'player': {
- 'source_filenames': [
- 'jplayer/jplayer.blue.monday.css',
- 'player/player.css',
- ],
- 'output_filename': 'css/compressed/player.css',
- },
- 'simple': {
- 'source_filenames': ('css/simple.css',),
- 'output_filename': 'css/compressed/simple.css',
- },
- 'widget': {
- 'source_filenames': ('scss/widget.scss',),
- 'output_filename': 'css/compressed/widget.css',
- },
+ 'main': {
+ # styles both for mobile and for big screen
+ 'source_filenames': [
+ 'css/jquery.countdown.css',
+ 'jplayer/jplayer.blue.monday.css',
+
+ 'sponsors/css/sponsors.css',
+
+ 'uni_form/uni-form.css',
+ 'uni_form/default.uni-form.css',
+
+ 'css/ui-lightness/jquery-ui-1.8.16.custom.css',
+
+ 'scss/main.scss',
+ ],
+ 'output_filename': 'css/compressed/main.css',
+ },
+ 'book': {
+ 'source_filenames': [
+ 'css/master.book.css',
+ ],
+ 'output_filename': 'css/compressed/book.css',
+ },
+ 'book_text': {
+ 'source_filenames': [
+ 'scss/book_text.scss',
+ 'css/new.book.css',
+
+ 'css/master.picture.css',
+ ],
+ 'output_filename': 'css/compressed/book_text.css',
+ },
+ 'picture': {
+ 'source_filenames': [
+ 'css/master.book.css',
+ 'css/master.picture.css',
+ ],
+ 'output_filename': 'css/compressed/picture.css',
+ },
+ 'player': {
+ 'source_filenames': [
+ 'jplayer/jplayer.blue.monday.css',
+ 'player/player.css',
+ ],
+ 'output_filename': 'css/compressed/player.css',
+ },
+ 'simple': {
+ 'source_filenames': ('css/simple.css',),
+ 'output_filename': 'css/compressed/simple.css',
+ },
+ 'widget': {
+ 'source_filenames': ('scss/widget.scss',),
+ 'output_filename': 'css/compressed/widget.css',
+ },
},
'JAVASCRIPT': {
- 'base': {
- 'source_filenames': (
- 'js/contrib/jquery.cycle.min.js',
- 'js/contrib/jquery.jqmodal.js',
- 'js/contrib/jquery.form.js',
- 'js/contrib/jquery.paging.min.js',
- 'js/contrib/jquery.countdown.js', 'js/contrib/jquery.countdown-pl.js',
- 'js/contrib/jquery.countdown-de.js', 'js/contrib/jquery.countdown-uk.js',
- 'js/contrib/jquery.countdown-es.js', 'js/contrib/jquery.countdown-lt.js',
- 'js/contrib/jquery.countdown-ru.js', 'js/contrib/jquery.countdown-fr.js',
-
- 'js/contrib/jquery-ui-1.8.16.custom.min.js',
-
- 'jplayer/jquery.jplayer.min.js',
- 'jplayer/jplayer.playlist.min.js',
- 'player/player.js',
-
- 'js/locale.js',
- 'js/dialogs.js',
- 'js/base.js',
- 'pdcounter/pdcounter.js',
- 'sponsors/js/sponsors.js',
- 'player/openplayer.js',
- 'js/search.js',
- 'funding/funding.js',
-
- 'uni_form/uni-form.js',
+ 'base': {
+ 'source_filenames': (
+ 'js/contrib/jquery.cycle.min.js',
+ 'js/contrib/jquery.jqmodal.js',
+ 'js/contrib/jquery.form.js',
+ 'js/contrib/jquery.paging.min.js',
+ 'js/contrib/jquery.countdown.js', 'js/contrib/jquery.countdown-pl.js',
+ 'js/contrib/jquery.countdown-de.js', 'js/contrib/jquery.countdown-uk.js',
+ 'js/contrib/jquery.countdown-es.js', 'js/contrib/jquery.countdown-lt.js',
+ 'js/contrib/jquery.countdown-ru.js', 'js/contrib/jquery.countdown-fr.js',
+
+ 'js/contrib/jquery-ui-1.8.16.custom.min.js',
+
+ 'jplayer/jquery.jplayer.min.js',
+ 'jplayer/jplayer.playlist.min.js',
+ 'player/player.js',
+
+ 'js/locale.js',
+ 'js/dialogs.js',
+ 'js/base.js',
+ 'pdcounter/pdcounter.js',
+ 'sponsors/js/sponsors.js',
+ 'player/openplayer.js',
+ 'js/search.js',
+ 'funding/funding.js',
+
+ 'uni_form/uni-form.js',
+ ),
+ 'output_filename': 'js/base.min.js',
+ },
+ 'player': {
+ 'source_filenames': [
+ 'jplayer/jquery.jplayer.min.js',
+ 'jplayer/jplayer.playlist.min.js',
+ 'player/player.js',
+ ],
+ 'output_filename': 'js/player.min.js',
+ },
+ 'book': {
+ 'source_filenames': [
+ 'js/contrib/jquery.eventdelegation.js',
+ 'js/contrib/jquery.scrollto.js',
+ 'js/contrib/jquery.highlightfade.js',
+ 'js/book_text/other.js',
+ 'js/book.js',
+
+ 'js/contrib/raphael-min.js',
+ 'js/contrib/progressSpin.min.js',
+ 'js/picture.js',
+ ],
+ 'output_filename': 'js/book.min.js',
+ },
+ 'book_text': {
+ 'source_filenames': [
+ 'js/contrib/jquery.form.js',
+ 'js/contrib/jquery.jqmodal.js',
+ 'js/book_text/*.js',
+ 'js/locale.js',
+ 'js/dialogs.js',
+
+ 'js/contrib/jquery.highlightfade.js',
+ 'js/contrib/raphael-min.js',
+ 'player/openplayer.js',
+ 'js/contrib/progressSpin.min.js',
+ 'js/picture.js',
+ ],
+ 'output_filename': 'js/book_text.js',
+ },
+ 'book_ie': {
+ 'source_filenames': ('js/contrib/ierange-m2.js',),
+ 'output_filename': 'js/book_ie.min.js',
+ },
+ 'widget': {
+ 'source_filenames': (
+ 'js/contrib/jquery.js',
+ 'js/contrib/jquery-ui-1.8.16.custom.min.js',
+ 'js/search.js',
+ 'js/widget_run.js',
),
- 'output_filename': 'js/base.min.js',
- },
- 'player': {
- 'source_filenames': [
- 'jplayer/jquery.jplayer.min.js',
- 'jplayer/jplayer.playlist.min.js',
- 'player/player.js',
- ],
- 'output_filename': 'js/player.min.js',
- },
- 'book': {
- 'source_filenames': [
- 'js/contrib/jquery.eventdelegation.js',
- 'js/contrib/jquery.scrollto.js',
- 'js/contrib/jquery.highlightfade.js',
- 'js/book_text/other.js',
- 'js/book.js',
-
- 'js/contrib/raphael-min.js',
- 'js/contrib/progressSpin.min.js',
- 'js/picture.js',
- ],
- 'output_filename': 'js/book.min.js',
- },
- 'book_text': {
- 'source_filenames': [
- 'js/contrib/jquery.form.js',
- 'js/contrib/jquery.jqmodal.js',
- 'js/book_text/*.js',
- 'js/locale.js',
- 'js/dialogs.js',
-
- 'js/contrib/jquery.highlightfade.js',
- 'js/contrib/raphael-min.js',
- 'player/openplayer.js',
- 'js/contrib/progressSpin.min.js',
- 'js/picture.js',
- ],
- 'output_filename': 'js/book_text.js',
- },
- 'book_ie': {
- 'source_filenames': ('js/contrib/ierange-m2.js',),
- 'output_filename': 'js/book_ie.min.js',
- },
- 'widget': {
- 'source_filenames': (
- 'js/contrib/jquery.js',
- 'js/contrib/jquery-ui-1.8.16.custom.min.js',
- 'js/search.js',
- 'js/widget_run.js',
- ),
- 'output_filename': 'js/widget.min.js',
- },
+ 'output_filename': 'js/widget.min.js',
+ },
},
'CSS_COMPRESSOR': None,
'JS_COMPRESSOR': 'pipeline.compressors.jsmin.JSMinCompressor',
# but they have some serious problems, like:
# https://github.com/Kronuz/pyScss/issues/166 (empty list syntax)
# https://github.com/Kronuz/pyScss/issues/258 (bad @media order)
- #'pyscss_compiler.PySCSSCompiler',
+ # 'pyscss_compiler.PySCSSCompiler',
)
}
-#~ STATICFILES_STORAGE = 'fnpdjango.utils.pipeline_storage.GzipPipelineCachedStorage'
+# STATICFILES_STORAGE = 'fnpdjango.utils.pipeline_storage.GzipPipelineCachedStorage'
-#PIPELINE_PYSCSS_BINARY = '/usr/bin/env pyscss'
-#PIPELINE_PYSCSS_ARGUMENTS = ''
+# PIPELINE_PYSCSS_BINARY = '/usr/bin/env pyscss'
+# PIPELINE_PYSCSS_ARGUMENTS = ''
STATICFILES_FINDERS = [
+# -*- coding: utf-8 -*-
# Source: http://djangosnippets.org/snippets/967/
# Author: adurdin
# Posted: August 13, 2008
# of Parser.parse() relating to the "parse_until" argument.
def __init__(self, *names):
self.names = set(names)
+
def __contains__(self, token_contents):
name = token_contents.split()[0]
return name in self.names
return SwitchNode(variable, cases)
+
class SwitchNode(Node):
def __init__(self, variable, cases):
self.variable = variable
except VariableDoesNotExist:
no_value = True
value_missing = None
+ value = None
for tests, nodelist in self.cases:
if tests is None:
import wolnelektury.views
-urlpatterns = patterns('wolnelektury.views',
+urlpatterns = patterns(
+ 'wolnelektury.views',
url(r'^$', 'main_page', name='main_page'),
url(r'^planowane/$', 'publish_plan', name='publish_plan'),
url(r'^widget\.html$', 'widget', name='widget'),
name='latest_blog_posts'),
)
-urlpatterns += patterns('',
+urlpatterns += patterns(
+ '',
url(r'^katalog/', include('catalogue.urls')),
url(r'^opds/', include('opds.urls')),
url(r'^sugestia/', include('suggest.urls')),
url(r'^i18n/', include('django.conf.urls.i18n')),
)
-urlpatterns += patterns('',
+urlpatterns += patterns(
+ '',
# old static pages - redirected
url(r'^1procent/$', RedirectView.as_view(
url='http://nowoczesnapolska.org.pl/wesprzyj_nas/', permanent=True)),
# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
+import os
import pytz
from django.utils import timezone
from django.conf import settings
tz = pytz.timezone(settings.TIME_ZONE)
+
def localtime_to_utc(localtime):
return timezone.utc.normalize(
tz.localize(localtime)
)
+
def utc_for_js(dt):
return dt.strftime('%Y/%m/%d %H:%M:%S UTC')
+
+
+def makedirs(path):
+ if not os.path.isdir(path):
+ os.makedirs(path)
continue
ctx['theme_fragment'] = tf[0]
for f in tf:
- if not f.book in ctx['theme_books']:
+ if f.book not in ctx['theme_books']:
ctx['theme_books'].append(f.book)
if len(ctx['theme_books']) == 3:
break
def __call__(self, request):
if request.user.is_authenticated():
- return self.redirect_or_refresh(request, '/',
- message=_('Already logged in as user %(user)s', ) %
- {'user': request.user.username})
+ return self.redirect_or_refresh(
+ request, '/',
+ message=_('Already logged in as user %(user)s', ) % {'user': request.user.username})
return super(LoginFormView, self).__call__(request)
def success(self, form, request):
def __call__(self, request):
if request.user.is_authenticated():
- return self.redirect_or_refresh(request, '/',
- message=_('Already logged in as user %(user)s', ) %
- {'user': request.user.username})
+ return self.redirect_or_refresh(
+ request, '/',
+ message=_('Already logged in as user %(user)s', ) % {'user': request.user.username})
return super(RegisterFormView, self).__call__(request)
def success(self, form, request):