Example #1
0
    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)
Example #2
0
    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)
Example #3
0
 def test_add_link_to_entry(self):
     kwargs = dict(title=1, href="url", extra="extra info")
     entry = AtomFeed.E.entry()
     link_child = AtomFeed.E.link_child()
     AtomFeed.add_link_to_entry(entry, [link_child], **kwargs)
     assert '<link extra="extra info" href="url" title="1"><link_child/></link>' in etree.tostring(
         entry)
Example #4
0
 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
Example #5
0
 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
Example #6
0
    def categories(cls, work):
        """Send out _all_ categories for the work.

        (So long as the category type has a URI associated with it in
        Subject.uri_lookup.)
        """
        _db = Session.object_session(work)
        by_scheme_and_term = dict()
        identifier_ids = work.all_identifier_ids()
        classifications = Identifier.classifications_for_identifier_ids(
            _db, identifier_ids)
        for c in classifications:
            subject = c.subject
            if subject.type in Subject.uri_lookup:
                scheme = Subject.uri_lookup[subject.type]
                term = subject.identifier
                weight_field = AtomFeed.schema_("ratingValue")
                key = (scheme, term)
                if not key in by_scheme_and_term:
                    value = dict(term=subject.identifier)
                    if subject.name:
                        value['label'] = subject.name
                    value[weight_field] = 0
                    by_scheme_and_term[key] = value
                by_scheme_and_term[key][weight_field] += c.weight

        # Collapse by_scheme_and_term to by_scheme
        by_scheme = defaultdict(list)
        for (scheme, term), value in by_scheme_and_term.items():
            by_scheme[scheme].append(value)
        by_scheme.update(super(VerboseAnnotator, cls).categories(work))
        return by_scheme
Example #7
0
 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
Example #8
0
    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
Example #9
0
            try:
                for result in instance._run_self_tests(_db):
                    results.append(result)

            except Exception, e:
                # This should only happen when there's a bug in the
                # self-test method itself.
                failure = instance.test_failure(
                    "Uncaught exception in the self-test method itself.", e)
                results.append(failure)

        end = datetime.datetime.utcnow()

        # Format the results in a useful way.

        value = dict(start=AtomFeed._strftime(start),
                     end=AtomFeed._strftime(end),
                     duration=(end - start).total_seconds(),
                     results=[x.to_dict for x in results])
        # Store the formatted results in the database, if we can find
        # a place to store them.

        if instance and isinstance(instance, ExternalSearchIndex):
            integration = instance.search_integration(_db)
            for idx, result in enumerate(value.get("results")):
                if isinstance(results[idx].result, (list, )):
                    result["result"] = results[idx].result

        elif instance:
            integration = instance.external_integration(_db)
Example #10
0
 def link(cls, rel, href, type):
     return AtomFeed.makeelement("link", type=type, rel=rel, href=href)
Example #11
0
            try:
                for result in instance._run_self_tests(_db):
                    results.append(result)
            except Exception, e:
                # This should only happen when there's a bug in the
                # self-test method itself.
                failure = instance.test_failure(
                    "Uncaught exception in the self-test method itself.", e
                )
                results.append(failure)
            integration = instance.external_integration(_db)
        end = datetime.datetime.utcnow()

        # Format the results in a useful way.
        value = dict(
            start=AtomFeed._strftime(start),
            end=AtomFeed._strftime(end),
            duration = (end-start).total_seconds(),
            results = [x.to_dict for x in results]
        )

        # Store the formatted results in the database, if we can find
        # a place to store them.
        if integration:
            integration.setting(
                cls.SELF_TEST_RESULTS_SETTING
            ).value = json.dumps(value)
        return value, results

    @classmethod
    def prior_test_results(cls, _db, constructor_method=None, *args, **kwargs):
Example #12
0
            except Exception, e:
                # This should only happen when there's a bug in the
                # self-test method itself.
                failure = instance.test_failure(
                    "Uncaught exception in the self-test method itself.", e
                )
                results.append(failure)


        end = datetime.datetime.utcnow()

        # Format the results in a useful way.

        value = dict(
            start=AtomFeed._strftime(start),
            end=AtomFeed._strftime(end),
            duration = (end-start).total_seconds(),
            results = [x.to_dict for x in results]
        )
        # Store the formatted results in the database, if we can find
        # a place to store them.

        if instance and isinstance(instance, ExternalSearchIndex):
            integration = instance.search_integration(_db)
            for idx, result in enumerate(value.get("results")):
                if isinstance(results[idx].result, (list,)):
                    result["result"] = results[idx].result

        elif instance:
            integration = instance.external_integration(_db)
Example #13
0
    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
Example #14
0
 def test_contributor(self):
     kwargs = {'{%s}role' % AtomFeed.OPF_NS: 'ctb'}
     tag = etree.tostring(AtomFeed.author(**kwargs))
     assert tag.startswith('<author')
     assert 'xmlns:opf="http://www.idpf.org/2007/opf"' in tag
     assert tag.endswith('opf:role="ctb"/>')
Example #15
0
 def authors(cls, work, license_pool, edition, identifier):
     """Create one or more <author> tags for the given work."""
     return [AtomFeed.author(AtomFeed.name(edition.author or ""))]
Example #16
0
    def categories(cls, work):
        """Return all relevant classifications of this work.

        :return: A dictionary mapping 'scheme' URLs to dictionaries of
        attribute-value pairs.

        Notable attributes: 'term', 'label', 'http://schema.org/ratingValue'
        """
        if not work:
            return {}

        categories = {}

        fiction_term = None
        if work.fiction == True:
            fiction_term = 'Fiction'
        elif work.fiction == False:
            fiction_term = 'Nonfiction'
        if fiction_term:
            fiction_scheme = Subject.SIMPLIFIED_FICTION_STATUS
            categories[fiction_scheme] = [
                dict(term=fiction_scheme + fiction_term,
                     label=fiction_term)
            ]

        simplified_genres = []
        for wg in work.work_genres:
            simplified_genres.append(wg.genre.name)

        if simplified_genres:
            categories[Subject.SIMPLIFIED_GENRE] = [
                dict(term=Subject.SIMPLIFIED_GENRE + urllib.quote(x),
                     label=x)
                for x in simplified_genres
            ]

        # Add the appeals as a category of schema
        # http://librarysimplified.org/terms/appeal
        schema_url = AtomFeed.SIMPLIFIED_NS + "appeals/"
        appeals = []
        categories[schema_url] = appeals
        for name, value in (
                (Work.CHARACTER_APPEAL, work.appeal_character),
                (Work.LANGUAGE_APPEAL, work.appeal_language),
                (Work.SETTING_APPEAL, work.appeal_setting),
                (Work.STORY_APPEAL, work.appeal_story),
        ):
            if value:
                appeal = dict(term=schema_url + name, label=name)
                weight_field = AtomFeed.schema_("ratingValue")
                appeal[weight_field] = value
                appeals.append(appeal)

        # Add the audience as a category of schema
        # http://schema.org/audience
        if work.audience:
            audience_uri = AtomFeed.SCHEMA_NS + "audience"
            categories[audience_uri] = [
                dict(term=work.audience, label=work.audience)
            ]

        if work.target_age:
            uri = Subject.uri_lookup[Subject.AGE_RANGE]
            target_age = work.target_age_string
            if target_age:
                categories[uri] = [dict(term=target_age, label=target_age)]

        return categories
Example #17
0
    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