def detailed_author(cls, contributor): """Turn a Contributor into a detailed <author> tag.""" children = [] children.append(AtomFeed.name(contributor.display_name or "")) sort_name = AtomFeed.makeelement("{%s}sort_name" % AtomFeed.SIMPLIFIED_NS) sort_name.text = contributor.sort_name children.append(sort_name) if contributor.family_name: family_name = AtomFeed.makeelement(AtomFeed.schema_("family_name")) family_name.text = contributor.family_name children.append(family_name) if contributor.wikipedia_name: wikipedia_name = AtomFeed.makeelement( "{%s}wikipedia_name" % AtomFeed.SIMPLIFIED_NS) wikipedia_name.text = contributor.wikipedia_name children.append(wikipedia_name) if contributor.viaf: viaf_tag = AtomFeed.makeelement(AtomFeed.schema_("sameas")) viaf_tag.text = "http://viaf.org/viaf/%s" % contributor.viaf children.append(viaf_tag) if contributor.lc: lc_tag = AtomFeed.makeelement(AtomFeed.schema_("sameas")) lc_tag.text = "http://id.loc.gov/authorities/names/%s" % contributor.lc children.append(lc_tag) return AtomFeed.author(*children)
def minimal_opds_entry(cls, identifier, cover, description, quality): elements = [] representations = [] most_recent_update = None if cover: cover_representation = cover.representation representations.append(cover.representation) cover_link = AtomFeed.makeelement( "link", href=cover_representation.mirror_url, type=cover_representation.media_type, rel=Hyperlink.IMAGE) elements.append(cover_link) if cover_representation.thumbnails: thumbnail = cover_representation.thumbnails[0] representations.append(thumbnail) thumbnail_link = AtomFeed.makeelement( "link", href=thumbnail.mirror_url, type=thumbnail.media_type, rel=Hyperlink.THUMBNAIL_IMAGE ) elements.append(thumbnail_link) if description: content = description.representation.content if isinstance(content, str): content = content.decode("utf8") description_e = AtomFeed.summary(content, type='html') elements.append(description_e) representations.append(description.representation) if quality: elements.append( Annotator.rating_tag(Measurement.QUALITY, quality)) # The update date is the most recent date any of these # resources were mirrored/fetched. potential_update_dates = [ r.mirrored_at or r.fetched_at for r in representations if r.mirrored_at or r.fetched_at ] if potential_update_dates: update_date = max(potential_update_dates) elements.append(AtomFeed.updated(AtomFeed._strftime(update_date))) entry = AtomFeed.entry( AtomFeed.id(identifier.urn), AtomFeed.title(OPDSFeed.NO_TITLE), *elements ) return entry
def add_breadcrumbs(self, lane, annotator, include_lane=False): """Add list of ancestor links in a breadcrumbs element.""" # Ensure that lane isn't top-level before proceeding if annotator.lane_url(lane) != annotator.default_lane_url(): breadcrumbs = AtomFeed.makeelement("{%s}breadcrumbs" % AtomFeed.SIMPLIFIED_NS) # Add root link root_url = annotator.default_lane_url() breadcrumbs.append( AtomFeed.link(title=annotator.top_level_title(), href=root_url) ) # Add links for all visible ancestors that aren't root for ancestor in reversed(lane.visible_ancestors()): lane_url = annotator.lane_url(ancestor) if lane_url != root_url: breadcrumbs.append( AtomFeed.link(title=ancestor.display_name, href=lane_url) ) # Include link to lane # For search, breadcrumbs include the searched lane if include_lane: breadcrumbs.append( AtomFeed.link(title=lane.display_name, href=annotator.lane_url(lane)) ) self.feed.append(breadcrumbs)
def rating_tag(cls, type_uri, value): """Generate a schema:Rating tag for the given type and value.""" rating_tag = AtomFeed.makeelement(AtomFeed.schema_("Rating")) value_key = AtomFeed.schema_('ratingValue') rating_tag.set(value_key, "%.4f" % value) if type_uri: type_key = AtomFeed.schema_('additionalType') rating_tag.set(type_key, type_uri) return rating_tag
def series(cls, series_name, series_position): """Generate a schema:Series tag for the given name and position.""" if not series_name: return None series_details = dict() series_details['name'] = series_name if series_position: series_details[AtomFeed.schema_('position')] = unicode(series_position) series_tag = AtomFeed.makeelement(AtomFeed.schema_("Series"), **series_details) return series_tag
def indirect_acquisition(cls, indirect_types): top_level_parent = None parent = None for t in indirect_types: indirect_link = AtomFeed.makeelement( "{%s}indirectAcquisition" % AtomFeed.OPDS_NS, type=t) if parent is not None: parent.extend([indirect_link]) parent = indirect_link if top_level_parent is None: top_level_parent = indirect_link return top_level_parent
def link(cls, rel, href, type): return AtomFeed.makeelement("link", type=type, rel=rel, href=href)
def _make_entry_xml(self, work, license_pool, edition, identifier, lane_link): # Find the .epub link epub_href = None p = None links = [] cover_quality = 0 qualities = [] if work: qualities.append(("Work quality", work.quality)) full_url = None thumbnail_urls, full_urls = self.annotator.cover_links(work) for rel, urls in ( (Hyperlink.IMAGE, full_urls), (Hyperlink.THUMBNAIL_IMAGE, thumbnail_urls)): for url in urls: image_type = "image/png" if url.endswith(".jpeg") or url.endswith(".jpg"): image_type = "image/jpeg" elif url.endswith(".gif"): image_type = "image/gif" links.append(AtomFeed.link(rel=rel, href=url, type=image_type)) permalink = self.annotator.permalink_for(work, license_pool, identifier) content = self.annotator.content(work) if isinstance(content, str): content = content.decode("utf8") content_type = 'html' kw = {} if edition.medium: additional_type = Edition.medium_to_additional_type.get( edition.medium) if not additional_type: logging.warn("No additionalType for medium %s", edition.medium) additional_type_field = AtomFeed.schema_("additionalType") kw[additional_type_field] = additional_type entry = AtomFeed.entry( AtomFeed.id(permalink), AtomFeed.title(edition.title or OPDSFeed.NO_TITLE), **kw ) if edition.subtitle: subtitle_tag = AtomFeed.makeelement(AtomFeed.schema_("alternativeHeadline")) subtitle_tag.text = edition.subtitle entry.append(subtitle_tag) if license_pool: provider_name_attr = "{%s}ProviderName" % AtomFeed.BIBFRAME_NS kwargs = {provider_name_attr : license_pool.data_source.name} data_source_tag = AtomFeed.makeelement( "{%s}distribution" % AtomFeed.BIBFRAME_NS, **kwargs ) entry.extend([data_source_tag]) author_tags = self.annotator.authors(work, license_pool, edition, identifier) entry.extend(author_tags) if edition.series: entry.extend([self.annotator.series(edition.series, edition.series_position)]) if content: entry.extend([AtomFeed.summary(content, type=content_type)]) entry.extend([ AtomFeed.updated(AtomFeed._strftime(datetime.datetime.utcnow())), ]) permanent_work_id_tag = AtomFeed.makeelement("{%s}pwid" % AtomFeed.SIMPLIFIED_NS) permanent_work_id_tag.text = edition.permanent_work_id entry.append(permanent_work_id_tag) entry.extend(links) categories_by_scheme = self.annotator.categories(work) category_tags = [] for scheme, categories in categories_by_scheme.items(): for category in categories: if isinstance(category, basestring): category = dict(term=category) category = dict(map(unicode, (k, v)) for k, v in category.items()) category_tag = AtomFeed.category(scheme=scheme, **category) category_tags.append(category_tag) entry.extend(category_tags) # print " ID %s TITLE %s AUTHORS %s" % (tag, work.title, work.authors) language = edition.language_code if language: language_tag = AtomFeed.makeelement("{%s}language" % AtomFeed.DCTERMS_NS) language_tag.text = language entry.append(language_tag) if edition.publisher: publisher_tag = AtomFeed.makeelement("{%s}publisher" % AtomFeed.DCTERMS_NS) publisher_tag.text = edition.publisher entry.extend([publisher_tag]) # We use Atom 'published' for the date the book first became # available to people using this application. now = datetime.datetime.utcnow() today = datetime.date.today() if license_pool and license_pool.availability_time: avail = license_pool.availability_time if isinstance(avail, datetime.datetime): avail = avail.date() if avail <= today: availability_tag = AtomFeed.makeelement("published") # TODO: convert to local timezone. availability_tag.text = AtomFeed._strftime(license_pool.availability_time) entry.extend([availability_tag]) # Entry.issued is the date the ebook came out, as distinct # from Entry.published (which may refer to the print edition # or some original edition way back when). # # For Dublin Core 'created' we use Entry.issued if we have it # and Entry.published if not. In general this means we use # issued date for Gutenberg and published date for other # sources. # # We use dc:created instead of dc:issued because dc:issued is # commonly conflated with atom:published. # # For the date the book was added to our collection we use # atom:published. issued = edition.issued or edition.published if (isinstance(issued, datetime.datetime) or isinstance(issued, datetime.date)): issued_already = False if isinstance(issued, datetime.datetime): issued_already = (issued <= now) elif isinstance(issued, datetime.date): issued_already = (issued <= today) if issued_already: issued_tag = AtomFeed.makeelement("{%s}created" % AtomFeed.DCTERMS_NS) # TODO: convert to local timezone, not that it matters much. issued_tag.text = issued.strftime("%Y-%m-%d") entry.extend([issued_tag]) return entry
def license_tags(cls, license_pool, loan, hold): # Generate a list of licensing tags. These should be inserted # into a <link> tag. tags = [] availability_tag_name = None suppress_since = False status = None since = None until = None if not license_pool: return if license_pool.open_access: default_loan_period = default_reservation_period = None else: ds = license_pool.data_source default_loan_period = ds.default_loan_period default_reservation_period = ds.default_reservation_period if loan: status = 'available' since = loan.start until = loan.until(default_loan_period) elif hold: until = hold.until(default_loan_period, default_reservation_period) if hold.position == 0: status = 'ready' since = None else: status = 'reserved' since = hold.start elif (license_pool.open_access or ( license_pool.licenses_available > 0 and license_pool.licenses_owned > 0) ): status = 'available' else: status='unavailable' kw = dict(status=status) if since: kw['since'] = AtomFeed._strftime(since) if until: kw['until'] = AtomFeed._strftime(until) tag_name = "{%s}availability" % AtomFeed.OPDS_NS availability_tag = AtomFeed.makeelement(tag_name, **kw) tags.append(availability_tag) # Open-access pools do not need to display <opds:holds> or <opds:copies>. if license_pool.open_access: return tags holds_kw = dict(total=str(license_pool.patrons_in_hold_queue or 0)) if hold and hold.position: holds_kw['position'] = str(hold.position) holds = AtomFeed.makeelement("{%s}holds" % AtomFeed.OPDS_NS, **holds_kw) tags.append(holds) copies_kw = dict( total=str(license_pool.licenses_owned or 0), available=str(license_pool.licenses_available or 0), ) copies = AtomFeed.makeelement("{%s}copies" % AtomFeed.OPDS_NS, **copies_kw) tags.append(copies) return tags