django-modeltranslation>=0.10,<0.11
django-allauth>=0.32,<0.33
django-extensions
+djangorestframework<3.7
+djangorestframework-xml
# contact
pyyaml
--- /dev/null
+from rest_framework import serializers
+from django.core.urlresolvers import reverse
+
+
+class AbsoluteURLField(serializers.ReadOnlyField):
+ def __init__(self, view_name=None, view_args=None, source='get_absolute_url', *args, **kwargs):
+ if view_name is not None:
+ source = '*'
+ super(AbsoluteURLField, self).__init__(*args, source=source, **kwargs)
+ self.view_name = view_name
+ self.view_args = {}
+ if view_args:
+ for v in view_args:
+ fields = v.split(':', 1)
+ self.view_args[fields[0]] = fields[1] if len(fields)>1 else fields[0]
+
+ def to_representation(self, value):
+ if self.view_name is not None:
+ kwargs = {
+ arg: getattr(value, field)
+ for (arg, field) in self.view_args.items()
+ }
+ value = reverse(self.view_name, kwargs=kwargs)
+ return self.context['request'].build_absolute_uri(value)
+
+
+class LegacyMixin(object):
+ def to_representation(self, value):
+ value = super(LegacyMixin, self).to_representation(value)
+ non_null_fields = getattr(getattr(self, 'Meta', None), 'legacy_non_null_fields', [])
+ for field in non_null_fields:
+ if field in value and value[field] is None:
+ value[field] = ''
+ return value
from catalogue.forms import BookImportForm
from catalogue.models import Book, Tag, BookMedia, Fragment, Collection
from catalogue.models.tag import prefetch_relations
-from librarian.cover import WLCover
from paypal.rest import user_is_subscribed
from picture.models import Picture
from picture.forms import PictureImportForm
API_BASE = WL_BASE = MEDIA_BASE = lazy(
lambda: u'https://' + Site.objects.get_current().domain, unicode)()
-SORT_KEY_SEP = '$'
-
category_singular = {
'authors': 'author',
'kinds': 'kind',
@classmethod
def href(cls, book):
""" Returns an URI for a Book in the API. """
- return API_BASE + reverse("api_book", args=[book.slug])
+ return API_BASE + reverse("catalogue_api_book", args=[book.slug])
@classmethod
def url(cls, book):
def simple_cover(cls, book):
return MEDIA_BASE + book.simple_cover.url if book.simple_cover else ''
- @classmethod
- def cover_color(cls, book):
- return WLCover.epoch_colors.get(book.extra_info.get('epoch'), '#000000')
-
- @classmethod
- def full_sort_key(cls, book):
- return '%s%s%s%s%s' % (book.sort_key_author, SORT_KEY_SEP, book.sort_key, SORT_KEY_SEP, book.id)
-
@staticmethod
def books_after(books, after, new_api):
if not new_api:
return books.filter(slug__gt=after)
try:
- author, title, book_id = after.split(SORT_KEY_SEP)
+ author, title, book_id = after.split(Book.SORT_KEY_SEP)
except ValueError:
return Book.objects.none()
return books.filter(Q(sort_key_author__gt=author)
remaining_count = count - len(filtered_books)
new_books = [
BookProxy(book, '%s%s%s' % (
- label, key_sep, book.slug if not new_api else self.full_sort_key(book)))
+ label, key_sep, book.slug if not new_api else book.full_sort_key()))
for book in book_list[:remaining_count]]
filtered_books += new_books
if len(filtered_books) == count:
add_file_getters()
-class CollectionDetails(object):
- """Custom Collection fields."""
-
- @classmethod
- def href(cls, collection):
- """ Returns URI in the API for the collection. """
-
- return API_BASE + reverse("api_collection", args=[collection.slug])
-
- @classmethod
- def url(cls, collection):
- """ Returns URL on the site. """
-
- return WL_BASE + collection.get_absolute_url()
-
- @classmethod
- def books(cls, collection):
- return Book.objects.filter(collection.get_query())
-
-
-class CollectionDetailHandler(BaseHandler, CollectionDetails):
- allowed_methods = ('GET',)
- fields = ['url', 'title', 'description', 'books']
-
- @piwik_track
- def read(self, request, slug):
- """ Returns details of a collection, identified by slug. """
- try:
- return Collection.objects.get(slug=slug)
- except Collection.DoesNotExist:
- return rc.NOT_FOUND
-
-
-class CollectionsHandler(BaseHandler, CollectionDetails):
- allowed_methods = ('GET',)
- model = Collection
- fields = ['url', 'href', 'title']
-
- @piwik_track
- def read(self, request):
- """ Returns all collections. """
- return Collection.objects.all()
-
-
class TagDetails(object):
"""Custom Tag fields."""
--- /dev/null
+from rest_framework_xml.renderers import XMLRenderer
+
+
+class LegacyXMLRenderer(XMLRenderer):
+ """
+ Renderer which serializes to XML.
+ """
+
+ item_tag_name = 'resource'
+ root_tag_name = 'response'
+
<li><a href='{% url "api_tag_list" "themes" %}'>
{% url "api_tag_list" "themes" %}</a> – {% trans "List of all themes" %}</li>
- <li><a href='{% url "api_collections" %}'>
- {% url "api_collections" %}</a> – {% trans "Collections" %}</li>
+ <li><a href='{% url "catalogue_api_collections" %}'>
+ {% url "catalogue_api_collections" %}</a> – {% trans "Collections" %}</li>
</ul>
<p>
- {% url "api_book" "studnia-i-wahadlo" as e1 %}
+ {% url "catalogue_api_book" "studnia-i-wahadlo" as e1 %}
{% url "api_tag" "authors" "edgar-allan-poe" as e2 %}
{% blocktrans %}
Each element of those lists contains a link (in a "href") attibute
"kind": "",
"full_sort_key": "$grandchild$3",
"author": "",
- "url": "https://example.com/katalog/lektura/grandchild/",
+ "url": "http://testserver/katalog/lektura/grandchild/",
"cover_color": "#000000",
"title": "Grandchild",
"cover": "",
"liked": null,
"slug": "grandchild",
"epoch": "",
- "href": "https://example.com/api/books/grandchild/",
+ "href": "http://testserver/api/books/grandchild/",
"genre": "Sonet",
"simple_thumb": "",
"has_audio": false,
"cover_thumb": ""
}
],
- "xml": "",
+ "xml": null,
"genres": [
{
- "url": "https://example.com/katalog/gatunek/wiersz/",
- "href": "https://example.com/api/genres/wiersz/",
+ "url": "http://testserver/katalog/gatunek/wiersz/",
+ "href": "http://testserver/api/genres/wiersz/",
"name": "Wiersz",
"slug": "wiersz"
}
"kind": "Liryka",
"full_sort_key": "john doe$parent$1",
"author": "John Doe",
- "url": "https://example.com/katalog/lektura/parent/",
+ "url": "http://testserver/katalog/lektura/parent/",
"cover_color": "#a6820a",
"title": "Parent",
- "cover": "https://example.com/media/cover/parent.jpg",
+ "cover": "http://testserver/media/cover/parent.jpg",
"liked": null,
"slug": "parent",
"epoch": "Barok",
- "href": "https://example.com/api/books/parent/",
+ "href": "http://testserver/api/books/parent/",
"genre": "Sonet",
- "simple_thumb": "https://example.com/media/cover_api_thumb/parent.jpg",
+ "simple_thumb": "http://testserver/media/cover_api_thumb/parent.jpg",
"has_audio": true,
- "cover_thumb": "https://example.com/media/cover/parent.jpg-139x193"
+ "cover_thumb": "http://testserver/media/cover/parent.jpg-139x193"
},
"cover_color": "#000000",
"simple_cover": "",
"epub": "",
"cover_thumb": "",
"mobi": "",
- "url": "https://example.com/katalog/lektura/child/",
+ "url": "http://testserver/katalog/lektura/child/",
"cover": "",
"pdf": "",
"simple_thumb": ""
"html": "Fragment",
"title": "Parent, Child"
},
- "txt": "https://example.com/katalog/pobierz/grandchild.txt",
+ "txt": "http://testserver/katalog/pobierz/grandchild.txt",
"children": [],
- "xml": "https://example.com/katalog/pobierz/grandchild.xml",
+ "xml": "http://testserver/katalog/pobierz/grandchild.xml",
"genres": [
{
- "url": "https://example.com/katalog/gatunek/sonet/",
- "href": "https://example.com/api/genres/sonet/",
+ "url": "http://testserver/katalog/gatunek/sonet/",
+ "href": "http://testserver/api/genres/sonet/",
"name": "Sonet",
"slug": "sonet"
}
],
"title": "Grandchild",
"media": [],
- "html": "https://example.com/katalog/pobierz/grandchild.html",
+ "html": "http://testserver/katalog/pobierz/grandchild.html",
"preview": true,
- "fb2": "https://example.com/katalog/pobierz/grandchild.fb2",
+ "fb2": "http://testserver/katalog/pobierz/grandchild.fb2",
"kinds": [],
"parent": {
"kind": "",
"full_sort_key": "$child$2",
"author": "",
- "url": "https://example.com/katalog/lektura/child/",
+ "url": "http://testserver/katalog/lektura/child/",
"cover_color": "#000000",
"title": "Child",
"cover": "",
"liked": null,
"slug": "child",
"epoch": "",
- "href": "https://example.com/api/books/child/",
+ "href": "http://testserver/api/books/child/",
"genre": "Wiersz",
"simple_thumb": "",
"has_audio": false,
"simple_cover": "",
"authors": [],
"audio_length": "",
- "epub": "https://example.com/katalog/pobierz/grandchild.epub",
+ "epub": "http://testserver/katalog/pobierz/grandchild.epub",
"cover_thumb": "",
- "mobi": "https://example.com/katalog/pobierz/grandchild.mobi",
- "url": "https://example.com/katalog/lektura/grandchild/",
+ "mobi": "http://testserver/katalog/pobierz/grandchild.mobi",
+ "url": "http://testserver/katalog/lektura/grandchild/",
"cover": "",
- "pdf": "https://example.com/katalog/pobierz/grandchild.pdf",
+ "pdf": "http://testserver/katalog/pobierz/grandchild.pdf",
"simple_thumb": ""
}
{
"epochs": [
{
- "url": "https://example.com/katalog/epoka/barok/",
- "href": "https://example.com/api/epochs/barok/",
+ "url": "http://testserver/katalog/epoka/barok/",
+ "href": "http://testserver/api/epochs/barok/",
"name": "Barok",
"slug": "barok"
}
"kind": "",
"full_sort_key": "$child$2",
"author": "",
- "url": "https://example.com/katalog/lektura/child/",
+ "url": "http://testserver/katalog/lektura/child/",
"cover_color": "#000000",
"title": "Child",
"cover": "",
"liked": null,
"slug": "child",
"epoch": "",
- "href": "https://example.com/api/books/child/",
+ "href": "http://testserver/api/books/child/",
"genre": "Wiersz",
"simple_thumb": "",
"has_audio": false,
"cover_thumb": ""
}
],
- "xml": "https://example.com/media/xml/parent.xml",
+ "xml": "http://testserver/media/xml/parent.xml",
"genres": [
{
- "url": "https://example.com/katalog/gatunek/sonet/",
- "href": "https://example.com/api/genres/sonet/",
+ "url": "http://testserver/katalog/gatunek/sonet/",
+ "href": "http://testserver/api/genres/sonet/",
"name": "Sonet",
"slug": "sonet"
}
"title": "Parent",
"media": [
{
- "url": "https://example.com/media/daisy/parent.daisy",
+ "url": "http://testserver/media/daisy/parent.daisy",
"director": "",
"type": "daisy",
"name": "Parent DAISY",
"artist": ""
},
{
- "url": "https://example.com/media/mp3/parent.mp3",
+ "url": "http://testserver/media/mp3/parent.mp3",
"director": "Director",
"type": "mp3",
"name": "Parent Audiobook",
"fb2": "",
"kinds": [
{
- "url": "https://example.com/katalog/rodzaj/liryka/",
- "href": "https://example.com/api/kinds/liryka/",
+ "url": "http://testserver/katalog/rodzaj/liryka/",
+ "href": "http://testserver/api/kinds/liryka/",
"name": "Liryka",
"slug": "liryka"
}
],
"parent": null,
"cover_color": "#a6820a",
- "simple_cover": "https://example.com/media/simple_cover/parent.jpg",
+ "simple_cover": "http://testserver/media/simple_cover/parent.jpg",
"authors": [
{
- "url": "https://example.com/katalog/autor/john-doe/",
- "href": "https://example.com/api/authors/john-doe/",
+ "url": "http://testserver/katalog/autor/john-doe/",
+ "href": "http://testserver/api/authors/john-doe/",
"name": "John Doe",
"slug": "john-doe"
}
],
"audio_length": "1:00",
- "epub": "https://example.com/media/epub/parent.epub",
- "cover_thumb": "https://example.com/media/cover/parent.jpg-139x193",
- "mobi": "https://example.com/media/mobi/parent.mobi",
- "url": "https://example.com/katalog/lektura/parent/",
- "cover": "https://example.com/media/cover/parent.jpg",
- "pdf": "https://example.com/media/pdf/parent.pdf",
- "simple_thumb": "https://example.com/media/cover_api_thumb/parent.jpg"
+ "epub": "http://testserver/media/epub/parent.epub",
+ "cover_thumb": "http://testserver/media/cover/parent.jpg-139x193",
+ "mobi": "http://testserver/media/mobi/parent.mobi",
+ "url": "http://testserver/katalog/lektura/parent/",
+ "cover": "http://testserver/media/cover/parent.jpg",
+ "pdf": "http://testserver/media/pdf/parent.pdf",
+ "simple_thumb": "http://testserver/media/cover_api_thumb/parent.jpg"
}
{
- "url": "https://example.com/katalog/lektury/a-collection/",
+ "url": "http://testserver/katalog/lektury/a-collection/",
"books": [
{
"kind": "Liryka",
"full_sort_key": "john doe$parent$1",
"author": "John Doe",
- "url": "https://example.com/katalog/lektura/parent/",
+ "url": "http://testserver/katalog/lektura/parent/",
"cover_color": "#a6820a",
"title": "Parent",
- "cover": "https://example.com/media/cover/parent.jpg",
+ "cover": "http://testserver/media/cover/parent.jpg",
"liked": null,
"slug": "parent",
"epoch": "Barok",
- "href": "https://example.com/api/books/parent/",
+ "href": "http://testserver/api/books/parent/",
"genre": "Sonet",
- "simple_thumb": "https://example.com/media/cover_api_thumb/parent.jpg",
+ "simple_thumb": "http://testserver/media/cover_api_thumb/parent.jpg",
"has_audio": true,
- "cover_thumb": "https://example.com/media/cover/parent.jpg-139x193"
+ "cover_thumb": "http://testserver/media/cover/parent.jpg-139x193"
}
],
"description": "Description",
[
{
- "url": "https://example.com/katalog/lektury/a-collection/",
- "href": "https://example.com/api/collections/a-collection/",
+ "url": "http://testserver/katalog/lektury/a-collection/",
+ "href": "http://testserver/api/collections/a-collection/",
"title": "A Collection"
}
]
return json.loads(self.signed(url, method, params).content)
def test_books(self):
+ self.assertEqual(
+ [b['liked'] for b in self.signed_json('/api/books/')],
+ [False, False, False]
+ )
+ # This one fails in the legacy implementation
+ # data = self.signed_json('/api/books/child/')
+ # self.assertFalse(data['parent']['liked'])
+ # self.assertFalse(data['children'][0]['liked'])
+
self.assertEqual(
self.signed_json('/api/like/parent/'),
{"likes": False}
self.assertTrue(self.signed_json('/api/parent_books/')[0]['liked'])
self.assertTrue(self.signed_json(
'/api/filter-books/', params={"search": "parent"})[0]['liked'])
+
+ # This one fails in the legacy implementation.
+ #self.assertTrue(self.signed_json(
+ # '/api/books/child/')['parent']['liked'])
# Liked books go on shelf.
self.assertEqual(
[x['slug'] for x in self.signed_json('/api/shelf/likes/')],
# 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.urls import url
+from django.conf.urls import url, include
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import TemplateView
from piston.authentication import OAuthAuthentication, oauth_access_token, oauth_request_token
book_list_resource = auth_resource(handler=handlers.BooksHandler)
ebook_list_resource = Resource(handler=handlers.EBooksHandler)
# book_list_resource = Resource(handler=handlers.BooksHandler)
-book_resource = Resource(handler=handlers.BookDetailHandler)
filter_book_resource = auth_resource(handler=handlers.FilterBooksHandler)
epub_resource = auth_resource(handler=handlers.EpubHandler)
like_resource = auth_resource(handler=handlers.UserLikeHandler)
-collection_resource = Resource(handler=handlers.CollectionDetailHandler)
-collection_list_resource = Resource(handler=handlers.CollectionsHandler)
-
tag_list_resource = Resource(handler=handlers.TagsHandler)
tag_resource = Resource(handler=handlers.TagDetailHandler)
url(r'^$', TemplateView.as_view(template_name='api/main.html'), name='api'),
+ # These are the new ones.
+ url(r'^', include('catalogue.api.urls')),
+
# info boxes (used by mobile app)
url(r'book/(?P<book_id>\d*?)/info\.html$', catalogue.views.book_info),
url(r'tag/(?P<tag_id>\d*?)/info\.html$', catalogue.views.tag_info),
- # books by collections
- url(r'^collections/$', collection_list_resource, name="api_collections"),
- url(r'^collections/(?P<slug>[^/]+)/$', collection_resource, name="api_collection"),
-
# epub preview
url(r'^epub/(?P<slug>[a-z0-9-]+)/$', epub_resource, name='api_epub'),
url(r'^like/(?P<slug>[a-z0-9-]+)/$', like_resource, name='api_like'),
# objects details
- url(r'^books/(?P<book>[a-z0-9-]+)/$', book_resource, name="api_book"),
url(r'^(?P<category>[a-z0-9-]+)/(?P<slug>[a-z0-9-]+)/$',
tag_resource, name="api_tag"),
url(r'^books/(?P<book>[a-z0-9-]+)/fragments/(?P<anchor>[a-z0-9-]+)/$',
--- /dev/null
+from rest_framework import serializers
+from sorl.thumbnail import default
+from catalogue.models import Book
+
+
+class BookLiked(serializers.ReadOnlyField):
+ def __init__(self, source='pk', **kwargs):
+ super(BookLiked, self).__init__(source=source, **kwargs)
+
+ def to_representation(self, value):
+ request = self.context['request']
+ if not hasattr(request, 'liked_books'):
+ if request.user.is_authenticated():
+ request.liked_books = set(Book.tagged.with_any(request.user.tag_set.all()).values_list('id', flat=True))
+ else:
+ request.liked_books = None
+ if request.liked_books is not None:
+ return value in request.liked_books
+
+
+class ThumbnailField(serializers.FileField):
+ def __init__(self, geometry, *args, **kwargs):
+ self.geometry = geometry
+ super(ThumbnailField, self).__init__(*args, **kwargs)
+
+ def to_representation(self, value):
+ if value:
+ return super(ThumbnailField, self).to_representation(
+ default.backend.get_thumbnail(value, self.geometry)
+ )
--- /dev/null
+from rest_framework import serializers
+from api.fields import AbsoluteURLField, LegacyMixin
+from catalogue.models import Book, Collection, Tag, BookMedia
+from .fields import BookLiked, ThumbnailField
+
+
+class TagSerializer(serializers.ModelSerializer):
+ url = AbsoluteURLField()
+ href = AbsoluteURLField(
+ view_name='api_tag',
+ view_args=('category:category_plural', 'slug')
+ )
+
+ class Meta:
+ model = Tag
+ fields = ['url', 'href', 'name', 'slug']
+
+
+class BookSerializer(LegacyMixin, serializers.ModelSerializer):
+ author = serializers.CharField(source='author_unicode')
+ kind = serializers.CharField(source='kind_unicode')
+ epoch = serializers.CharField(source='epoch_unicode')
+ genre = serializers.CharField(source='genre_unicode')
+
+ simple_thumb = serializers.FileField(source='cover_api_thumb')
+ href = AbsoluteURLField(view_name='catalogue_api_book', view_args=['slug'])
+ url = AbsoluteURLField()
+ liked = BookLiked()
+ cover_thumb = ThumbnailField('139x193', source='cover')
+
+ class Meta:
+ model = Book
+ fields = [
+ 'kind', 'full_sort_key', 'title', 'url', 'cover_color', 'author',
+ 'cover', 'liked', 'epoch', 'href', 'has_audio', 'genre',
+ 'simple_thumb', 'slug', 'cover_thumb']
+ legacy_non_null_fields = [
+ 'kind', 'author', 'epoch', 'genre',
+ 'cover', 'simple_thumb', 'cover_thumb']
+
+
+class MediaSerializer(LegacyMixin, serializers.ModelSerializer):
+ url = serializers.FileField(source='file')
+
+ class Meta:
+ model = BookMedia
+ fields = ['url', 'director', 'type', 'name', 'artist']
+ legacy_non_null_fields = ['director', 'artist']
+
+
+class BookDetailSerializer(LegacyMixin, serializers.ModelSerializer):
+ url = AbsoluteURLField()
+
+ authors = TagSerializer(many=True)
+ epochs = TagSerializer(many=True)
+ genres = TagSerializer(many=True)
+ kinds = TagSerializer(many=True)
+
+ fragment_data = serializers.DictField()
+ parent = BookSerializer()
+ children = BookSerializer(many=True)
+
+ xml = AbsoluteURLField(source='xml_url')
+ html = AbsoluteURLField(source='html_url')
+ txt = AbsoluteURLField(source='txt_url')
+ fb2 = AbsoluteURLField(source='fb2_url')
+ epub = AbsoluteURLField(source='epub_url')
+ mobi = AbsoluteURLField(source='mobi_url')
+ pdf = AbsoluteURLField(source='pdf_url')
+ media = MediaSerializer(many=True)
+ cover_thumb = ThumbnailField('139x193', source='cover')
+ simple_thumb = serializers.FileField(source='cover_api_thumb')
+
+ class Meta:
+ model = Book
+ fields = [
+ 'title', 'url',
+ 'epochs', 'genres', 'kinds', 'authors',
+ 'fragment_data', 'children', 'parent', 'preview',
+ 'epub', 'mobi', 'pdf', 'html', 'txt', 'fb2', 'xml', 'media', 'audio_length',
+ 'cover_color', 'simple_cover', 'cover_thumb', 'cover', 'simple_thumb'
+ ]
+ legacy_non_null_fields = ['html', 'txt', 'fb2', 'epub', 'mobi', 'pdf',
+ 'cover', 'simple_cover', 'cover_thumb', 'simple_thumb']
+
+
+class CollectionListSerializer(serializers.ModelSerializer):
+ url = AbsoluteURLField()
+ href = AbsoluteURLField(view_name='collection-detail', view_args=['slug'])
+
+ class Meta:
+ model = Collection
+ fields = ['url', 'href', 'title']
+
+
+class CollectionSerializer(serializers.ModelSerializer):
+ books = BookSerializer(many=True, source='get_books')
+ url = AbsoluteURLField()
+
+ class Meta:
+ model = Collection
+ fields = ['url', 'books', 'description', 'title']
--- /dev/null
+# -*- 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.urls import include, url
+from . import views
+
+
+urlpatterns = [
+ # books by collections
+ url(r'^collections/$', views.CollectionList.as_view(), name="api_collections"),
+ url(r'^collections/(?P<slug>[^/]+)/$',
+ views.CollectionDetail.as_view(), name="collection-detail"),
+
+ url(r'^books/(?P<slug>[^/]+)/$', views.BookDetail.as_view(), name='catalogue_api_book'),
+]
--- /dev/null
+from rest_framework.generics import ListAPIView, RetrieveAPIView
+from . import serializers
+from catalogue.models import Book, Collection
+
+
+class CollectionList(ListAPIView):
+ queryset = Collection.objects.all()
+ serializer_class = serializers.CollectionListSerializer
+
+
+class CollectionDetail(RetrieveAPIView):
+ queryset = Collection.objects.all()
+ lookup_field = 'slug'
+ serializer_class = serializers.CollectionSerializer
+
+
+class BookDetail(RetrieveAPIView):
+ queryset = Book.objects.all()
+ lookup_field = 'slug'
+ serializer_class = serializers.BookDetailSerializer
from fnpdjango.storage import BofhFileSystemStorage
from ssify import flush_ssi_includes
+from librarian.cover import WLCover
from librarian.html import transform_abstrakt
from newtagging import managers
from catalogue import constants
html_built = django.dispatch.Signal()
published = django.dispatch.Signal()
+ SORT_KEY_SEP = '$'
+
class AlreadyExists(Exception):
pass
def authors(self):
return self.tags.filter(category='author')
+ def epochs(self):
+ return self.tags.filter(category='epoch')
+
+ def genres(self):
+ return self.tags.filter(category='genre')
+
+ def kinds(self):
+ return self.tags.filter(category='kind')
+
def tag_unicode(self, category):
relations = prefetched_relations(self, category)
if relations:
def author_unicode(self):
return self.cached_author
+ def kind_unicode(self):
+ return self.tag_unicode('kind')
+
+ def epoch_unicode(self):
+ return self.tag_unicode('epoch')
+
+ def genre_unicode(self):
+ return self.tag_unicode('genre')
+
def translator(self):
translators = self.extra_info.get('translators')
if not translators:
if likes(user, self):
set_sets(user, self, [])
+ def full_sort_key(self):
+ return self.SORT_KEY_SEP.join((self.sort_key_author, self.sort_key, str(self.id)))
+
+ def cover_color(self):
+ return WLCover.epoch_colors.get(self.extra_info.get('epoch'), '#000000')
+
def add_file_fields():
for format_ in Book.formats:
return None
else:
return None
+
+ @property
+ def director(self):
+ return self.extra_info.get('director_name', None)
+
+ @property
+ def artist(self):
+ return self.extra_info.get('artist_name', None)
else:
return ''
+ @property
+ def category_plural(self):
+ return self.category + 's'
+
@permalink
def get_absolute_url(self):
return 'tagged_object_list', [self.url_chunk]
'django.contrib.admin',
'django.contrib.admindocs',
'django.contrib.staticfiles',
+ 'rest_framework',
'fnp_django_pagination',
'pipeline',
'piston',
EntryType('info', _('info'), commentable=False),
EntryType('event', _('events'), commentable=False),
)
+
+REST_FRAMEWORK = {
+ "DEFAULT_RENDERER_CLASSES": (
+ 'rest_framework.renderers.JSONRenderer',
+ 'rest_framework.renderers.BrowsableAPIRenderer',
+ 'api.renderers.LegacyXMLRenderer',
+ ),
+ 'DEFAULT_AUTHENTICATION_CLASSES': (
+ )
+}