def __init__(self, _db, api, datasource, batch_size=10, metadata_replacement_policy=None, circulationdata_replacement_policy=None, cutoff_time=None): self._db = _db self.api = api output_source = DataSource.lookup(_db, datasource) input_identifier_types = [output_source.primary_identifier_type] service_name = "%s Bibliographic Coverage Provider" % datasource metadata_replacement_policy = ( metadata_replacement_policy or ReplacementPolicy.from_metadata_source()) circulationdata_replacement_policy = ( circulationdata_replacement_policy or ReplacementPolicy.from_license_source()) self.metadata_replacement_policy = metadata_replacement_policy self.circulationdata_replacement_policy = circulationdata_replacement_policy super(BibliographicCoverageProvider, self).__init__(service_name, input_identifier_types, output_source, batch_size=batch_size, cutoff_time=cutoff_time)
def populate_all_catalog(self): """ Call get_all_catalog to get all of library's book info from OneClick. Create Work, Edition, LicensePool objects in our database. """ catalog_list = self.get_all_catalog() items_transmitted = len(catalog_list) items_created = 0 coverage_provider = OneClickBibliographicCoverageProvider(_db=self._db) # the default policy doesn't update delivery mechanisms, which we do want to do metadata_replacement_policy = ReplacementPolicy.from_metadata_source() metadata_replacement_policy.formats = True for catalog_item in catalog_list: result = coverage_provider.update_metadata( catalog_item=catalog_item, metadata_replacement_policy=metadata_replacement_policy) if not isinstance(result, CoverageFailure): items_created += 1 if isinstance(result, Identifier): # calls work.set_presentation_ready() for us coverage_provider.handle_success(result) # We're populating the catalog, so we can assume the list OneClick # sent us is of books we own licenses to. # NOTE: TODO later: For the 4 out of 2000 libraries that chose to display # books they don't own, we'd need to call the search endpoint to get # the interest field, and then deal with licenses_owned. if result.licensed_through: result.licensed_through.licenses_owned = 1 # stay data, stay! self._db.commit() return items_transmitted, items_created
def _set_metadata(self, identifier, metadata, metadata_replacement_policy=None): """Finds or creates the Edition for an Identifier, updates it with the given metadata. :return: The Identifier (if successful) or an appropriate CoverageFailure (if not). """ metadata_replacement_policy = metadata_replacement_policy or ( ReplacementPolicy.from_metadata_source()) edition = self.edition(identifier) if isinstance(edition, CoverageFailure): return edition if not metadata: e = "Did not receive metadata from input source" return CoverageFailure(identifier, e, data_source=self.output_source, transient=True) try: metadata.apply( edition, replace=metadata_replacement_policy, ) except Exception as e: self.log.warn("Error applying metadata to edition %d: %s", edition.id, e, exc_info=e) return CoverageFailure(identifier, repr(e), data_source=self.output_source, transient=True) return identifier
class TitleFromExternalList(object): """This class helps you convert data from external lists into Simplified Edition and CustomListEntry objects. """ def __init__(self, metadata, first_appearance, most_recent_appearance, annotation): self.log = logging.getLogger("Title from external list") self.metadata = metadata self.first_appearance = first_appearance or most_recent_appearance self.most_recent_appearance = (most_recent_appearance or datetime.datetime.now()) self.annotation = annotation def to_custom_list_entry(self, custom_list, metadata_client, overwrite_old_data=False): """Turn this object into a CustomListEntry with associated Edition.""" _db = Session.object_session(custom_list) edition = self.to_edition(_db, metadata_client, overwrite_old_data) list_entry, is_new = get_one_or_create(_db, CustomListEntry, edition=edition, customlist=custom_list) if (not list_entry.first_appearance or list_entry.first_appearance > self.first_appearance): if list_entry.first_appearance: self.log.info( "I thought %s first showed up at %s, but then I saw it earlier, at %s!", self.metadata.title, list_entry.first_appearance, self.first_appearance) list_entry.first_appearance = self.first_appearance if (not list_entry.most_recent_appearance or list_entry.most_recent_appearance < self.most_recent_appearance): if list_entry.most_recent_appearance: self.log.info( "I thought %s most recently showed up at %s, but then I saw it later, at %s!", self.metadata.title, list_entry.most_recent_appearance, self.most_recent_appearance) list_entry.most_recent_appearance = self.most_recent_appearance list_entry.annotation = self.annotation list_entry.set_work(self.metadata, metadata_client) return list_entry, is_new def to_edition(self, _db, metadata_client, overwrite_old_data=False): """Create or update an Edition object for this list item. We have two goals here: 1. Make sure there is an Edition representing the list's view of the data. 2. If at all possible, connect the Edition's primary identifier to other identifiers in the system, identifiers which may have associated LicensePools. This can happen in two ways: 2a. The Edition's primary identifier, or other identifiers associated with the Edition, may be directly associated with LicensePools. This can happen if a book's list entry includes (e.g.) an Overdrive ID. 2b. The Edition's permanent work ID may identify it as the same work as other Editions in the system. In that case this Edition's primary identifier may be associated with the other Editions' primary identifiers. (p=0.85) """ self.log.info("Converting %s to an Edition object.", self.metadata.title) # Make sure the Metadata object's view of the book is present # as an Edition. This will also associate all its identifiers # with its primary identifier, and calculate the permanent work # ID if possible. try: edition, is_new = self.metadata.edition(_db) except ValueError, e: self.log.info("Ignoring %s, no corresponding edition.", self.metadata.title) return None if overwrite_old_data: policy = ReplacementPolicy.from_metadata_source( even_if_not_apparently_updated=True) else: policy = ReplacementPolicy.append_only( even_if_not_apparently_updated=True) self.metadata.apply( edition=edition, metadata_client=metadata_client, replace=policy, ) self.metadata.associate_with_identifiers_based_on_permanent_work_id( _db) return edition
def _default_replacement_policy(self, _db): """Unless told otherwise, assume that we are getting this data from a reliable metadata source. """ return ReplacementPolicy.from_metadata_source()
def __init__(self, **metadata_replacement_args): self.metadata_replacement_policy = ReplacementPolicy.from_metadata_source( **metadata_replacement_args )