From a225580faef801cae0476c6abf1eac652ad3878c Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 11 Feb 2017 15:58:54 -0500 Subject: [PATCH 1/5] add a json version of opds just for fun --- api/opds_json.py | 289 +++++++++++++++++++++++++++++++++++++++++++++++ api/urls.py | 2 + api/views.py | 37 ++++-- 3 files changed, 317 insertions(+), 11 deletions(-) create mode 100644 api/opds_json.py diff --git a/api/opds_json.py b/api/opds_json.py new file mode 100644 index 00000000..0f791a71 --- /dev/null +++ b/api/opds_json.py @@ -0,0 +1,289 @@ +from itertools import islice + +import datetime +import urlparse +from django.core.urlresolvers import reverse +from django.utils.http import urlquote +import json +import pytz + +import logging +logger = logging.getLogger(__name__) + +from regluit.core import models, facets +import regluit.core.cc as cc +from .opds import ( + feeds, + get_facet_class, + add_query_component, + Facet, + get_facet_facet, + opds_feed_for_work, +) + +licenses = cc.LICENSE_LIST + +FORMAT_TO_MIMETYPE = {'pdf':"application/pdf", + 'epub':"application/epub+zip", + 'mobi':"application/x-mobipocket-ebook", + 'html':"text/html", + 'text':"text/html"} + +UNGLUEIT_URL= 'https://unglue.it' +ACQUISITION = "application/atom+xml;profile=opds-catalog;kind=acquisition" +FACET_RELATION = "http://opds-spec.org/facet" + +def feeds(): + for facet_path in facets.get_all_facets('Format'): + yield get_facet_facet(facet_path) + for facet_path in facets.get_all_facets('Keyword'): + yield get_facet_facet(facet_path) + +def get_facet_class(name): + return get_facet_facet(name) + +def text_node(tag, text): + return {tag:text} + +def html_node(tag, html): + return {tag:html} + +def isbn_node(isbn): + return 'urn:ISBN:'+ isbn + +def work_node(work, facet=None): + + content={} + # title + content["title"] = work.title + + # id + content.update(text_node('id', "{base}{url}".format(base=UNGLUEIT_URL,url=reverse('work_identifier',kwargs={'work_id':work.id})))) + + updated = None + + # links for all ebooks + ebooks = facet.filter_model("Ebook",work.ebooks()) if facet else work.ebooks() + versions = set() + content['links'] = links = [] + for ebook in ebooks: + if updated is None: + # most recent ebook, first ebook in loop + updated = ebook.created.isoformat() + content.update(text_node('updated', updated)) + if not ebook.version_label in versions: + versions.add(ebook.version_label) + link_node_attrib = {} + link_node = {"link":link_node_attrib} + + # ebook.download_url is an absolute URL with the protocol, domain, and path baked in + link_rel = "http://opds-spec.org/acquisition/open-access" + link_node_attrib.update({"href":add_query_component(ebook.download_url, "feed=opds"), + "rel":link_rel, + "rights": str(ebook.rights)}) + if ebook.is_direct(): + link_node_attrib["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "") + else: + """ indirect acquisition, i.e. google books """ + link_node_attrib["type"] = "text/html" + indirect_attrib = {} + indirect = {"indirectAcquisition":indirect_attrib} + indirect_attrib["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "") + link_node.update(indirect) + if ebook.version_label: + link_node_attrib.update({"version": ebook.version_label}) + links.append(link_node) + + # get the cover -- assume jpg? + if work.cover_image_small(): + cover_node_attrib = {} + cover_node = {"link": cover_node_attrib} + cover_node_attrib.update({"href":work.cover_image_small(), + "type":"image/"+work.cover_filetype(), + "rel":"http://opds-spec.org/image/thumbnail"}) + links.append(cover_node) + if work.cover_image_thumbnail(): + cover_node2_attrib = {} + cover_node2 = {"link": cover_node2_attrib} + cover_node2_attrib.update({"href":work.cover_image_thumbnail(), + "type":"image/"+work.cover_filetype(), + "rel":"http://opds-spec.org/image"}) + links.append(cover_node2) + + + # 2012 + content.update({"issued": work.publication_date}) + + # author + # TO DO: include all authors? + content["author"] = {"name": work.author()} + + # publisher + #Open Book Publishers + if len(work.publishers()): + content["publishers"] = [{"publisher": publisher.name.name} + for publisher in work.publishers()] + + # language + content["language"] = work.language + + # description + content["content"] = work.description + + # identifiers + if work.identifiers.filter(type='isbn'): + content['identifers'] = [isbn_node(isbn.value) + for isbn in work.identifiers.filter(type='isbn')[0:9]] #10 should be more than enough + + + # subject tags + subjects = [subject.name for subject in work.subjects.all()] + if subjects: + content["category"] = subjects + + # age level + # + if work.age_level: + age_level_node_attrib = {} + age_level_node = {"category": age_level_node_attrib} + age_level_node_attrib["scheme"] = 'http://schema.org/typicalAgeRange' + age_level_node_attrib["term"] = work.age_level + age_level_node_attrib["label"] = work.get_age_level_display() + content.update(age_level_node) + + + # rating + content["Rating"] = {"ratingValue":"{:}".format(work.priority())} + return content + +class Facet: + title = '' + works = None + feed_path = '' + description = '' + + def feed(self, page=None, order_by='newest'): + self.works = self.works.order_by(*facets.get_order_by(order_by)) + return opds_feed_for_works(self, page=page, order_by=order_by) + + def updated(self): + # return the creation date for most recently added item + if not self.works: + return pytz.utc.localize(datetime.datetime.utcnow()).isoformat() + else: + return pytz.utc.localize(self.works[0].created).isoformat() + +def get_facet_facet(facet_path): + class Facet_Facet(Facet): + + def __init__(self, facet_path=facet_path): + self.feed_path = facet_path + self.facet_object = facets.get_facet_object(facet_path) + self.title = "Unglue.it" + for facet in self.facet_object.facets(): + self.title = self.title + " " + facet.title + self.works = self.facet_object.get_query_set().distinct() + self.description = self.facet_object.description + return Facet_Facet + +def opds_feed_for_work(work_id): + class single_work_facet: + def __init__(self, work_id): + try: + works=models.Work.objects.filter(id=work_id) + except models.Work.DoesNotExist: + works=models.Work.objects.none() + except ValueError: + # not a valid work_id + works=models.Work.objects.none() + self.works=works + self.title='Unglue.it work #%s' % work_id + self.feed_path='' + self.facet_object= facets.BaseFacet(None) + return opds_feed_for_works( single_work_facet(work_id) ) + +def opds_feed_for_works(the_facet, page=None, order_by='newest'): + works = the_facet.works + feed_path = the_facet.feed_path + title = the_facet.title + + feed = {} + + # add title + # TO DO: will need to calculate the number items and where in the feed we are + + feed.update(text_node('title', title + ' - sorted by ' + order_by)) + + # id + + feed.update(text_node('id', "{url}/api/opds/{feed_path}/?order_by={order_by}".format(url=UNGLUEIT_URL, + feed_path=urlquote(feed_path), order_by=order_by))) + + # updated + # TO DO: fix time zone? + # also use our wrapped datetime code + + feed.update(text_node('updated', + pytz.utc.localize(datetime.datetime.utcnow()).isoformat())) + + # author + + author_node = {"author":{'name': 'unglue.it','uri': UNGLUEIT_URL}} + feed.update(author_node) + + # links: start, self, next/prev (depending what's necessary -- to start with put all CC books) + feed['navlinks'] = [] + # start link + append_navlink(feed, 'start', feed_path, None , order_by, title="First 10") + + # next link + + if not page: + page =0 + else: + try: + page=int(page) + except TypeError: + page=0 + + try: + works[10 * page + 10] + append_navlink(feed, 'next', feed_path, page+1 , order_by, title="Next 10") + except IndexError: + pass + + # sort facets + append_navlink(feed, FACET_RELATION, feed_path, None, 'popular', group="Order", active = order_by=='popular', title="Sorted by popularity") + append_navlink(feed, FACET_RELATION, feed_path, None, 'newest', group="Order", active = order_by=='newest', title="Sorted by newest") + + #other facets + for other_group in the_facet.facet_object.get_other_groups(): + for facet_object in other_group.get_facets(): + append_navlink(feed, FACET_RELATION, feed_path + '/' + facet_object.facet_name, None, order_by, group=other_group.title, title=facet_object.title) + + works = islice(works, 10 * page, 10 * page + 10) + if page > 0: + append_navlink(feed, 'previous', feed_path, page-1, order_by, title="Previous 10") + feedlist = [] + feed['publications'] = feedlist + for work in works: + node = work_node(work, facet=the_facet.facet_object) + feedlist.append(node) + return json.dumps(feed,indent=2, separators=(',', ': '), sort_keys=False) + +def append_navlink(feed, rel, path, page, order_by, group=None, active=None , title=""): + if page==None: + return + link_attrib = {} + link = {"link": link_attrib} + link_attrib.update({"rel":rel, + "href": UNGLUEIT_URL + "/api/opds/" + urlquote(path) + '/?order_by=' + order_by + ('&page=' + unicode(page) ), + "type": ACQUISITION, + "title": title, + }) + if rel == FACET_RELATION: + if group: + link_attrib['facetGroup'] = group + if active: + link_attrib['activeFacet'] = 'true' + feed['navlinks'].append(link) \ No newline at end of file diff --git a/api/urls.py b/api/urls.py index be4aaff3..bd534ca6 100644 --- a/api/urls.py +++ b/api/urls.py @@ -32,7 +32,9 @@ urlpatterns = [ url(r'^featured_cover$', featured_cover, name="featured_cover"), url(r'^featured_url$', featured_url, name="featured_url"), url(r"^opds/$", OPDSNavigationView.as_view(template_name="opds.xml"), name="opds"), + url(r"^opdsjson/$", OPDSNavigationView.as_view(template_name="opds.json", json=True), name="opdsjson"), url(r"^opds/(?P.*)/$", OPDSAcquisitionView.as_view(), name="opds_acqusition"), + url(r"^opdsjson/(?P.*)/$", OPDSAcquisitionView.as_view(json=True), name="opdsjson_acqusition"), url(r"^onix/(?P.*)/$", OnixView.as_view(), name="onix"), url(r"^onix/$", OnixView.as_view(), name="onix_all"), url(r'^id/work/(?P\w+)/$', negotiate_content, name="work_identifier"), diff --git a/api/views.py b/api/views.py index 9e35a400..9ec1c59a 100755 --- a/api/views.py +++ b/api/views.py @@ -23,7 +23,7 @@ from django.http import ( import regluit.core.isbn from regluit.core.bookloader import load_from_yaml -from regluit.api import opds, onix +from regluit.api import opds, onix, opds_json from regluit.api.models import repo_allowed from regluit.core import models @@ -166,26 +166,37 @@ class ApiHelpView(TemplateView): return context class OPDSNavigationView(TemplateView): - + json=False # http://stackoverflow.com/a/6867976: secret to how to change content-type def render_to_response(self, context, **response_kwargs): - response_kwargs['content_type'] = "application/atom+xml;profile=opds-catalog;kind=navigation" + if json: + response_kwargs['content_type'] = "application/json;profile=opds-catalog;kind=navigation" + else: + response_kwargs['content_type'] = "application/atom+xml;profile=opds-catalog;kind=navigation" return super(TemplateView, self).render_to_response(context, **response_kwargs) def get_context_data(self, **kwargs): context = super(OPDSNavigationView, self).get_context_data(**kwargs) - context["feeds"] = opds.feeds() - context["feed"] = opds.get_facet_facet('all') + if json: + context["feeds"] = opds_json.feeds() + context["feed"] = opds_json.get_facet_facet('all') + else: + context["feeds"] = opds.feeds() + context["feed"] = opds.get_facet_facet('all') return context class OPDSAcquisitionView(View): - + json=False def get(self, request, *args, **kwargs): work = request.GET.get('work', None) if work: - return HttpResponse(opds.opds_feed_for_work(work), - content_type="application/atom+xml;profile=opds-catalog;kind=acquisition") + if json: + return HttpResponse(opds_json.opds_feed_for_work(work), + content_type="application/json;profile=opds-catalog;kind=acquisition") + else: + return HttpResponse(opds.opds_feed_for_work(work), + content_type="application/atom+xml;profile=opds-catalog;kind=acquisition") facet = kwargs.get('facet') page = request.GET.get('page', None) order_by = request.GET.get('order_by', 'newest') @@ -193,9 +204,13 @@ class OPDSAcquisitionView(View): page = int(page) except: page = None - facet_class = opds.get_facet_class(facet)() - return HttpResponse(facet_class.feed(page,order_by), - content_type="application/atom+xml;profile=opds-catalog;kind=acquisition") + facet_class = opds_json.get_facet_class(facet)() + if json: + return HttpResponse(facet_class.feed(page,order_by), + content_type="application/json;profile=opds-catalog;kind=acquisition") + else: + return HttpResponse(facet_class.feed(page,order_by), + content_type="application/atom+xml;profile=opds-catalog;kind=acquisition") class OnixView(View): From c2981ed74508d4972fa3316effd49aab95ff7fe4 Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 11 Feb 2017 16:03:29 -0500 Subject: [PATCH 2/5] fix urls --- api/opds_json.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/opds_json.py b/api/opds_json.py index 0f791a71..039be768 100644 --- a/api/opds_json.py +++ b/api/opds_json.py @@ -216,7 +216,7 @@ def opds_feed_for_works(the_facet, page=None, order_by='newest'): # id - feed.update(text_node('id', "{url}/api/opds/{feed_path}/?order_by={order_by}".format(url=UNGLUEIT_URL, + feed.update(text_node('id', "{url}/api/opdsjson/{feed_path}/?order_by={order_by}".format(url=UNGLUEIT_URL, feed_path=urlquote(feed_path), order_by=order_by))) # updated @@ -277,7 +277,7 @@ def append_navlink(feed, rel, path, page, order_by, group=None, active=None , ti link_attrib = {} link = {"link": link_attrib} link_attrib.update({"rel":rel, - "href": UNGLUEIT_URL + "/api/opds/" + urlquote(path) + '/?order_by=' + order_by + ('&page=' + unicode(page) ), + "href": UNGLUEIT_URL + "/api/opdsjson/" + urlquote(path) + '/?order_by=' + order_by + ('&page=' + unicode(page) ), "type": ACQUISITION, "title": title, }) From 61c15c03e444e8d66b7c48443d02580d032885f4 Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 11 Feb 2017 18:04:19 -0500 Subject: [PATCH 3/5] align with HADRIEN https://gist.github.com/HadrienGardeur/4354170#file-navigation-json-L1 --- api/opds_json.py | 45 +++++++++++++++++++---------------------- api/templates/opds.json | 22 ++++++++++++++++++++ api/templates/opds.xml | 6 +++--- api/views.py | 13 ++++++------ 4 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 api/templates/opds.json diff --git a/api/opds_json.py b/api/opds_json.py index 039be768..255b32d2 100644 --- a/api/opds_json.py +++ b/api/opds_json.py @@ -30,8 +30,8 @@ FORMAT_TO_MIMETYPE = {'pdf':"application/pdf", 'text':"text/html"} UNGLUEIT_URL= 'https://unglue.it' -ACQUISITION = "application/atom+xml;profile=opds-catalog;kind=acquisition" -FACET_RELATION = "http://opds-spec.org/facet" +ACQUISITION = "application/vnd.opds.acquisition+json" +FACET_RELATION = "opds:facet" def feeds(): for facet_path in facets.get_all_facets('Format'): @@ -65,7 +65,8 @@ def work_node(work, facet=None): # links for all ebooks ebooks = facet.filter_model("Ebook",work.ebooks()) if facet else work.ebooks() versions = set() - content['links'] = links = [] + content['_links'] = links = {} + for ebook in ebooks: if updated is None: # most recent ebook, first ebook in loop @@ -74,12 +75,10 @@ def work_node(work, facet=None): if not ebook.version_label in versions: versions.add(ebook.version_label) link_node_attrib = {} - link_node = {"link":link_node_attrib} - + ebookfiles = links.get("opds:acquisition:open-access",[]) + ebookfiles.append(link_node_attrib) # ebook.download_url is an absolute URL with the protocol, domain, and path baked in - link_rel = "http://opds-spec.org/acquisition/open-access" link_node_attrib.update({"href":add_query_component(ebook.download_url, "feed=opds"), - "rel":link_rel, "rights": str(ebook.rights)}) if ebook.is_direct(): link_node_attrib["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "") @@ -89,26 +88,26 @@ def work_node(work, facet=None): indirect_attrib = {} indirect = {"indirectAcquisition":indirect_attrib} indirect_attrib["type"] = FORMAT_TO_MIMETYPE.get(ebook.format, "") - link_node.update(indirect) + link_node_attrib.update(indirect) if ebook.version_label: link_node_attrib.update({"version": ebook.version_label}) - links.append(link_node) + links["opds:acquisition:open-access"] = ebookfiles # get the cover -- assume jpg? if work.cover_image_small(): cover_node_attrib = {} - cover_node = {"link": cover_node_attrib} + cover_node = {"opds:image:thumbnail": cover_node_attrib} cover_node_attrib.update({"href":work.cover_image_small(), "type":"image/"+work.cover_filetype(), - "rel":"http://opds-spec.org/image/thumbnail"}) - links.append(cover_node) + }) + links.update(cover_node) if work.cover_image_thumbnail(): cover_node2_attrib = {} - cover_node2 = {"link": cover_node2_attrib} + cover_node2 = {"opds:image": cover_node2_attrib} cover_node2_attrib.update({"href":work.cover_image_thumbnail(), "type":"image/"+work.cover_filetype(), - "rel":"http://opds-spec.org/image"}) - links.append(cover_node2) + }) + links.update(cover_node2) # 2012 @@ -207,7 +206,7 @@ def opds_feed_for_works(the_facet, page=None, order_by='newest'): feed_path = the_facet.feed_path title = the_facet.title - feed = {} + feed = {'_type': ACQUISITION} # add title # TO DO: will need to calculate the number items and where in the feed we are @@ -232,7 +231,7 @@ def opds_feed_for_works(the_facet, page=None, order_by='newest'): feed.update(author_node) # links: start, self, next/prev (depending what's necessary -- to start with put all CC books) - feed['navlinks'] = [] + feed['_links'] = {} # start link append_navlink(feed, 'start', feed_path, None , order_by, title="First 10") @@ -274,16 +273,14 @@ def opds_feed_for_works(the_facet, page=None, order_by='newest'): def append_navlink(feed, rel, path, page, order_by, group=None, active=None , title=""): if page==None: return - link_attrib = {} - link = {"link": link_attrib} - link_attrib.update({"rel":rel, + link = { "href": UNGLUEIT_URL + "/api/opdsjson/" + urlquote(path) + '/?order_by=' + order_by + ('&page=' + unicode(page) ), "type": ACQUISITION, "title": title, - }) + } if rel == FACET_RELATION: if group: - link_attrib['facetGroup'] = group + link['facetGroup'] = group if active: - link_attrib['activeFacet'] = 'true' - feed['navlinks'].append(link) \ No newline at end of file + link['activeFacet'] = 'true' + feed['_links'][rel] = link \ No newline at end of file diff --git a/api/templates/opds.json b/api/templates/opds.json new file mode 100644 index 00000000..85b33edb --- /dev/null +++ b/api/templates/opds.json @@ -0,0 +1,22 @@ +{ + "_type": "application/vnd.opds.navigation+json", + "title": "Unglue.it Catalog", + + "_links": { + "start": { "href": "https://unglue.it{% url 'opds' %}", "type": "application/vnd.opds.navigation+json" }, + "opds:featured": {"href": "{{ feed.feed_path }}/?order_by=popular", "type": "application/vnd.opds.acquisition+json" } + }, + + "navigation": [{ + "title": {{ feed.title }} - Popular, + "_links": {"opds:featured": { "href": "{{ feed.feed_path }}/?order_by=popular", "profile": "http://opds-spec.org/navigation", "type": "application/vnd.opds.acquisition+json"}} + },{ + "title": "{{ feed.title }} - New", + "_links": {"opds:new": { "href": "{{ feed.feed_path }}/?order_by=newest", "profile": "http://opds-spec.org/navigation", "type": "application/vnd.opds.acquisition+json" }} + } + {% for feed in feeds %},{ + "title": "{{ feed.title }}", + "_links": {"opds:new": { "href": "{{ feed.feed_path }}", "profile": "http://opds-spec.org/navigation", "type": "application/vnd.opds.acquisition+json" }} + } + {% endfor %}] +} diff --git a/api/templates/opds.xml b/api/templates/opds.xml index 57fde6a6..30cb0930 100644 --- a/api/templates/opds.xml +++ b/api/templates/opds.xml @@ -18,7 +18,7 @@ type="application/atom+xml;profile=opds-catalog;kind=acquisition" /> {{ feed.title }} - Popular - https://unglue.it{% url 'opds_acqusition' feed.feed_path %}?order_by=popular + https://unglue.it{% url 'opds_acqusition' feed.feed_path %}/?order_by=popular {{ feed.updated }} {{ feed.title }} - New - https://unglue.it{% url 'opds_acqusition' feed.feed_path %}?order_by=newest + https://unglue.it{% url 'opds_acqusition' feed.feed_path %}/?order_by=newest {{ feed.updated }} - {{ feed.description }} - ordered by newest diff --git a/api/views.py b/api/views.py index 9ec1c59a..fc24d5e4 100755 --- a/api/views.py +++ b/api/views.py @@ -171,7 +171,7 @@ class OPDSNavigationView(TemplateView): def render_to_response(self, context, **response_kwargs): if json: - response_kwargs['content_type'] = "application/json;profile=opds-catalog;kind=navigation" + response_kwargs['content_type'] = "application/vnd.opds.navigation+json" else: response_kwargs['content_type'] = "application/atom+xml;profile=opds-catalog;kind=navigation" return super(TemplateView, self).render_to_response(context, **response_kwargs) @@ -187,11 +187,11 @@ class OPDSNavigationView(TemplateView): return context class OPDSAcquisitionView(View): - json=False + json = False def get(self, request, *args, **kwargs): work = request.GET.get('work', None) if work: - if json: + if self.json: return HttpResponse(opds_json.opds_feed_for_work(work), content_type="application/json;profile=opds-catalog;kind=acquisition") else: @@ -204,11 +204,12 @@ class OPDSAcquisitionView(View): page = int(page) except: page = None - facet_class = opds_json.get_facet_class(facet)() - if json: + if self.json: + facet_class = opds_json.get_facet_class(facet)() return HttpResponse(facet_class.feed(page,order_by), - content_type="application/json;profile=opds-catalog;kind=acquisition") + content_type="application/vnd.opds.acquisition+json") else: + facet_class = opds.get_facet_class(facet)() return HttpResponse(facet_class.feed(page,order_by), content_type="application/atom+xml;profile=opds-catalog;kind=acquisition") From 7f5ffee7b76c5e623cd80443c2ad4e4a8c59d543 Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 11 Feb 2017 21:33:32 -0500 Subject: [PATCH 4/5] more alignment --- api/opds_json.py | 6 +++--- core/facets.py | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/api/opds_json.py b/api/opds_json.py index 255b32d2..51505521 100644 --- a/api/opds_json.py +++ b/api/opds_json.py @@ -115,7 +115,7 @@ def work_node(work, facet=None): # author # TO DO: include all authors? - content["author"] = {"name": work.author()} + content["contributor"] = {"name": work.author()} # publisher #Open Book Publishers @@ -127,7 +127,7 @@ def work_node(work, facet=None): content["language"] = work.language # description - content["content"] = work.description + content["summary"] = work.description # identifiers if work.identifiers.filter(type='isbn'): @@ -260,7 +260,7 @@ def opds_feed_for_works(the_facet, page=None, order_by='newest'): for facet_object in other_group.get_facets(): append_navlink(feed, FACET_RELATION, feed_path + '/' + facet_object.facet_name, None, order_by, group=other_group.title, title=facet_object.title) - works = islice(works, 10 * page, 10 * page + 10) + #works = islice(works, 10 * page, 10 * page + 10) if page > 0: append_navlink(feed, 'previous', feed_path, page-1, order_by, title="Previous 10") feedlist = [] diff --git a/core/facets.py b/core/facets.py index bb959112..7096c447 100644 --- a/core/facets.py +++ b/core/facets.py @@ -1,6 +1,6 @@ from django.apps import apps +from django.db.models import Q from regluit.core import cc - class BaseFacet(object): facet_name = 'all' @@ -236,7 +236,47 @@ class KeywordFacetGroup(FacetGroup): def description(self): return "%s eBooks" % self.keyword return KeywordFacet + +class SearchFacetGroup(FacetGroup): + def __init__(self): + super(FacetGroup,self).__init__() + self.title = 'Search Term' + # make facets in TOPKW available for display + self.facets = [] + self.label = '{} is ...'.format(self.title) + + def has_facet(self, facet_name): + + # recognize any facet_name that starts with "s." as a valid facet name + return facet_name.startswith('s.') + + def get_facet_class(self, facet_name): + class KeywordFacet(NamedFacet): + def set_name(self): + self.facet_name=facet_name + # facet_names of the form 's.SUBJECT' and SUBJECT is therefore the 3rd character on + self.term=self.facet_name[2:] + def get_query_set(self): + return self._get_query_set().filter( + Q(title__icontains=self.term) | + Q(editions__authors__name__icontains=self.term) | + Q(subjects__name__iexact=self.term) + ) + + def template(self): + return 'facets/search.html' + @property + def title(self): + return self.term + @property + def label(self): + return self.term + @property + def description(self): + return "eBooks for {}".format(self.term) + return KeywordFacet + class PublisherFacetGroup(FacetGroup): def __init__(self): @@ -285,7 +325,7 @@ class PublisherFacetGroup(FacetGroup): return PublisherFacet # order of groups in facet_groups determines order of display on /free/ -facet_groups = [KeywordFacetGroup(), FormatFacetGroup(), LicenseFacetGroup(), PublisherFacetGroup(), IdFacetGroup()] +facet_groups = [KeywordFacetGroup(), FormatFacetGroup(), LicenseFacetGroup(), PublisherFacetGroup(), IdFacetGroup(), SearchFacetGroup()] def get_facet(facet_name): for facet_group in facet_groups: From da21d172af82124cca0b060464aff4e9645795b7 Mon Sep 17 00:00:00 2001 From: eric Date: Sun, 12 Feb 2017 10:39:45 -0500 Subject: [PATCH 5/5] adjust paging --- api/opds_json.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/api/opds_json.py b/api/opds_json.py index 51505521..3bd5843e 100644 --- a/api/opds_json.py +++ b/api/opds_json.py @@ -202,6 +202,7 @@ def opds_feed_for_work(work_id): return opds_feed_for_works( single_work_facet(work_id) ) def opds_feed_for_works(the_facet, page=None, order_by='newest'): + books_per_page = 50 works = the_facet.works feed_path = the_facet.feed_path title = the_facet.title @@ -233,7 +234,7 @@ def opds_feed_for_works(the_facet, page=None, order_by='newest'): # links: start, self, next/prev (depending what's necessary -- to start with put all CC books) feed['_links'] = {} # start link - append_navlink(feed, 'start', feed_path, None , order_by, title="First 10") + append_navlink(feed, 'start', feed_path, None , order_by, title="First {}".format(books_per_page)) # next link @@ -246,8 +247,8 @@ def opds_feed_for_works(the_facet, page=None, order_by='newest'): page=0 try: - works[10 * page + 10] - append_navlink(feed, 'next', feed_path, page+1 , order_by, title="Next 10") + works[books_per_page * page + books_per_page] + append_navlink(feed, 'next', feed_path, page+1 , order_by, title="Next {}".format(books_per_page)) except IndexError: pass @@ -260,9 +261,9 @@ def opds_feed_for_works(the_facet, page=None, order_by='newest'): for facet_object in other_group.get_facets(): append_navlink(feed, FACET_RELATION, feed_path + '/' + facet_object.facet_name, None, order_by, group=other_group.title, title=facet_object.title) - #works = islice(works, 10 * page, 10 * page + 10) + works = islice(works, books_per_page * page, books_per_page * page + books_per_page) if page > 0: - append_navlink(feed, 'previous', feed_path, page-1, order_by, title="Previous 10") + append_navlink(feed, 'previous', feed_path, page-1, order_by, title="Previous {}".format(books_per_page)) feedlist = [] feed['publications'] = feedlist for work in works: