core.model package¶
Submodules¶
core.model.admin module¶
- class core.model.admin.Admin(**kwargs)[source]¶
Bases:
Base
,HasFullTableCache
- classmethod authenticate(_db, email, password)[source]¶
Finds an authenticated Admin by email and password :return: Admin or None
- credential¶
- email¶
- id¶
- password¶
- password_hashed¶
- roles¶
- class core.model.admin.AdminRole(**kwargs)[source]¶
Bases:
Base
,HasFullTableCache
- LIBRARIAN = 'librarian'¶
- LIBRARY_MANAGER = 'manager'¶
- ROLES = ['system', 'manager-all', 'manager', 'librarian-all', 'librarian']¶
- SITEWIDE_LIBRARIAN = 'librarian-all'¶
- SITEWIDE_LIBRARY_MANAGER = 'manager-all'¶
- SYSTEM_ADMIN = 'system'¶
- admin¶
- admin_id¶
- id¶
- library¶
- library_id¶
- role¶
core.model.cachedfeed module¶
- class core.model.cachedfeed.CachedFeed(**kwargs)[source]¶
Bases:
Base
- CACHE_FOREVER = <object object>¶
- CONTRIBUTOR_TYPE = 'contributor'¶
- CRAWLABLE_TYPE = 'crawlable'¶
- class CachedFeedKeys(feed_type, library, work, lane_id, unique_key, facets_key, pagination_key)¶
Bases:
tuple
- facets_key¶
Alias for field number 5
- feed_type¶
Alias for field number 0
- lane_id¶
Alias for field number 3
- library¶
Alias for field number 1
- pagination_key¶
Alias for field number 6
- unique_key¶
Alias for field number 4
- work¶
Alias for field number 2
- GROUPS_TYPE = 'groups'¶
- IGNORE_CACHE = <object object>¶
- NAVIGATION_TYPE = 'navigation'¶
- PAGE_TYPE = 'page'¶
- RECOMMENDATIONS_TYPE = 'recommendations'¶
- RELATED_TYPE = 'related'¶
- SERIES_TYPE = 'series'¶
- content¶
- facets¶
- classmethod feed_type(worklist, facets)[source]¶
Determine the ‘type’ of the feed.
This may be defined either by worklist or by facets, with facets taking priority.
- Returns:
A string that can go into cachedfeeds.type.
- classmethod fetch(_db, worklist, facets, pagination, refresher_method, max_age=None, raw=False, **response_kwargs)[source]¶
Retrieve a cached feed from the database if possible.
Generate it from scratch and store it in the database if necessary.
Return it in the most useful form to the caller.
- Parameters:
_db – A database connection.
worklist – The WorkList associated with this feed.
facets – A Facets object that distinguishes this feed from others (for instance, by its sort order).
pagination – A Pagination object that explains which page of a larger feed is being cached.
refresher_method – A function to call if it turns out the contents of the feed need to be regenerated. This function must take no arguments and return an object that implements __unicode__. (A Unicode string or an OPDSFeed is fine.)
max_age – If a cached feed is older than this, it will be considered stale and regenerated. This may be either a number of seconds or a timedelta. If no value is specified, a default value will be calculated based on WorkList and Facets configuration. Setting this value to zero will force a refresh.
raw – If this is False (the default), a Response ready to be converted into a Flask Response object will be returned. If this is True, the CachedFeed object itself will be returned. In most non-test situations the default is better.
- Returns:
A Response or CachedFeed containing up-to-date content.
- id¶
- lane¶
- lane_id¶
- library¶
- library_id¶
- log = <Logger CachedFeed (WARNING)>¶
- classmethod max_cache_age(worklist, type, facets, override=None)[source]¶
Determine the number of seconds that a cached feed of a given type can remain fresh.
Order of precedence: override, facets, worklist.
- Parameters:
worklist – A WorkList which may have an opinion on this topic.
type – The type of feed being generated.
facets – A faceting object that may have an opinion on this topic.
override – A specific value passed in by the caller. This may either be a number of seconds or a timedelta.
- Returns:
A number of seconds, or CACHE_FOREVER or IGNORE_CACHE
- pagination¶
- timestamp¶
- type¶
- unique_key¶
- work¶
- work_id¶
core.model.circulationevent module¶
- class core.model.circulationevent.CirculationEvent(**kwargs)[source]¶
Bases:
Base
Changes to a license pool’s circulation status. We log these so we can measure things like the velocity of individual books.
- CLIENT_EVENTS = ['open_book']¶
- CM_CHECKIN = 'circulation_manager_check_in'¶
- CM_CHECKOUT = 'circulation_manager_check_out'¶
- CM_FULFILL = 'circulation_manager_fulfill'¶
- CM_HOLD_PLACE = 'circulation_manager_hold_place'¶
- CM_HOLD_RELEASE = 'circulation_manager_hold_release'¶
- DISTRIBUTOR_AVAILABILITY_NOTIFY = 'distributor_availability_notify'¶
- DISTRIBUTOR_CHECKIN = 'distributor_check_in'¶
- DISTRIBUTOR_CHECKOUT = 'distributor_check_out'¶
- DISTRIBUTOR_HOLD_PLACE = 'distributor_hold_place'¶
- DISTRIBUTOR_HOLD_RELEASE = 'distributor_hold_release'¶
- DISTRIBUTOR_LICENSE_ADD = 'distributor_license_add'¶
- DISTRIBUTOR_LICENSE_REMOVE = 'distributor_license_remove'¶
- DISTRIBUTOR_TITLE_ADD = 'distributor_title_add'¶
- DISTRIBUTOR_TITLE_REMOVE = 'distributor_title_remove'¶
- NEW_PATRON = 'circulation_manager_new_patron'¶
- NO_DATE = <object object>¶
- OPEN_BOOK = 'open_book'¶
- SOURCE = 'source'¶
- TIME_FORMAT = '%Y-%m-%dT%H:%M:%S+00:00'¶
- TYPE = 'event'¶
- delta¶
- end¶
- id¶
- library¶
- library_id¶
- license_pool¶
- license_pool_id¶
- location¶
- classmethod log(_db, license_pool, event_name, old_value, new_value, start=None, end=None, library=None, location=None)[source]¶
Log a CirculationEvent to the database, assuming it hasn’t already been recorded.
- new_value¶
- old_value¶
- start¶
- type¶
core.model.classification module¶
- class core.model.classification.Classification(**kwargs)[source]¶
Bases:
Base
The assignment of a Identifier to a Subject.
- TRUSTED_DISTRIBUTOR_WEIGHT = 100.0¶
- property comes_from_license_source¶
Does this Classification come from a data source that also provided a license for this book?
- data_source¶
- data_source_id¶
- property generic_juvenile_audience¶
Is this a classification that mentions (e.g.) a Children’s audience but is actually a generic ‘Juvenile’ classification?
- id¶
- identifier¶
- identifier_id¶
- property quality_as_indicator_of_target_age¶
- property scaled_weight¶
- subject¶
- subject_id¶
- weight¶
- property weight_as_indicator_of_target_age¶
- class core.model.classification.Genre(**kwargs)[source]¶
Bases:
Base
,HasFullTableCache
A subject-matter classification for a book. Much, much more general than Classification.
- property default_fiction¶
- property genredata¶
- id¶
- lane_genres¶
- name¶
- property self_and_subgenres¶
- property subgenres¶
- subjects¶
- work_genres¶
- works = ObjectAssociationProxyInstance(AssociationProxy('work_genres', 'work'))¶
- class core.model.classification.Subject(**kwargs)[source]¶
Bases:
Base
A subject under which books might be classified.
- AGE_RANGE = 'schema:typicalAgeRange'¶
- ATOS_SCORE = 'ATOS'¶
- AXIS_360_AUDIENCE = 'Axis 360 Audience'¶
- BIC = 'BIC'¶
- BISAC = 'BISAC'¶
- DDC = 'DDC'¶
- FAST = 'FAST'¶
- FREEFORM_AUDIENCE = 'schema:audience'¶
- GRADE_LEVEL = 'Grade level'¶
- GUTENBERG_BOOKSHELF = 'gutenberg:bookshelf'¶
- INTEREST_LEVEL = 'Interest Level'¶
- LCC = 'LCC'¶
- LCSH = 'LCSH'¶
- LEXILE_SCORE = 'Lexile'¶
- NYPL_APPEAL = 'NYPL Appeal'¶
- ORGANIZATION = 'schema:Organization'¶
- OVERDRIVE = 'Overdrive'¶
- PERSON = 'schema:Person'¶
- PLACE = 'schema:Place'¶
- RBDIGITAL = 'RBdigital'¶
- RBDIGITAL_AUDIENCE = 'RBdigital Audience'¶
- SIMPLIFIED_FICTION_STATUS = 'http://librarysimplified.org/terms/fiction/'¶
- SIMPLIFIED_GENRE = 'http://librarysimplified.org/terms/genres/Simplified/'¶
- TAG = 'tag'¶
- TOPIC = 'schema:Topic'¶
- TYPES_FOR_SEARCH = ['FAST', 'Overdrive', 'BISAC', 'tag']¶
- classmethod assign_to_genres(_db, type_restriction=None, force=False, batch_size=1000)[source]¶
Find subjects that have not been checked yet, assign each a genre/audience/fiction status if possible, and mark each as checked.
- Parameters:
type_restriction – Only consider subjects of the given type.
force – Assign a genre to all subjects not just the ones that have been checked.
batch_size – Perform a database commit every time this many subjects have been checked.
- audience¶
- by_uri = {'http://id.worldcat.org/fast/': 'FAST', 'http://librarysimplified.org/terms/fiction/': 'http://librarysimplified.org/terms/fiction/', 'http://librarysimplified.org/terms/genres/3M/': 'BISAC', 'http://librarysimplified.org/terms/genres/Overdrive/': 'Overdrive', 'http://librarysimplified.org/terms/genres/Simplified/': 'http://librarysimplified.org/terms/genres/Simplified/', 'http://purl.org/dc/terms/DDC': 'DDC', 'http://purl.org/dc/terms/LCC': 'LCC', 'http://purl.org/dc/terms/LCSH': 'LCSH', 'http://schema.org/audience': 'schema:audience', 'http://schema.org/typicalAgeRange': 'schema:typicalAgeRange', 'http://www.bisg.org/standards/bisac_subject/': 'BISAC', 'http://www.feedbooks.com/categories': 'BISAC'}¶
- checked¶
- classifications¶
- classmethod common_but_not_assigned_to_genre(_db, min_occurances=1000, type_restriction=None)[source]¶
- property describes_format¶
Does this Subject describe a format of book rather than subject matter, audience, etc? If so, there are limitations on when we believe this Subject actually applies to a given book–it may describe a very different adaptation of the same underlying work. TODO: See note in assign_genres about the hacky way this is used.
- fiction¶
- genre¶
- genre_id¶
- id¶
- identifier¶
- k = 'http://www.feedbooks.com/categories'¶
- locked¶
- classmethod lookup(_db, type, identifier, name, autocreate=True)[source]¶
Turn a subject type and identifier into a Subject.
- name¶
- target_age¶
- property target_age_string¶
- type¶
- uri_lookup = {'BISAC': 'http://www.feedbooks.com/categories', 'DDC': 'http://purl.org/dc/terms/DDC', 'FAST': 'http://id.worldcat.org/fast/', 'LCC': 'http://purl.org/dc/terms/LCC', 'LCSH': 'http://purl.org/dc/terms/LCSH', 'Overdrive': 'http://librarysimplified.org/terms/genres/Overdrive/', 'http://librarysimplified.org/terms/fiction/': 'http://librarysimplified.org/terms/fiction/', 'http://librarysimplified.org/terms/genres/Simplified/': 'http://librarysimplified.org/terms/genres/Simplified/', 'schema:audience': 'http://schema.org/audience', 'schema:typicalAgeRange': 'http://schema.org/typicalAgeRange'}¶
- v = 'BISAC'¶
core.model.collection module¶
- class core.model.collection.Collection(**kwargs)[source]¶
Bases:
Base
,HasFullTableCache
A Collection is a set of LicensePools obtained through some mechanism.
- AUDIOBOOK_LOAN_DURATION_KEY = 'audio_loan_duration'¶
- DATA_SOURCE_NAME_SETTING = 'data_source'¶
- DEFAULT_AUDIENCE_KEY = 'default_audience'¶
- DEFAULT_RESERVATION_PERIOD_KEY = 'default_reservation_period'¶
- EBOOK_LOAN_DURATION_KEY = 'ebook_loan_duration'¶
- EXTERNAL_ACCOUNT_ID_KEY = 'external_account_id'¶
- GLOBAL_COLLECTION_DATA_SOURCES = ['Enki']¶
- STANDARD_DEFAULT_LOAN_PERIOD = 21¶
- STANDARD_DEFAULT_RESERVATION_PERIOD = 3¶
- classmethod by_datasource(_db, data_source)[source]¶
Query collections that are associated with the given DataSource.
Collections marked for deletion are not included.
- classmethod by_name_and_protocol(_db, name, protocol)[source]¶
Find or create a Collection with the given name and the given protocol.
This method uses the full-table cache if possible.
- Returns:
A 2-tuple (collection, is_new)
- classmethod by_protocol(_db, protocol)[source]¶
Query collections that get their licenses through the given protocol.
Collections marked for deletion are not included.
- Parameters:
protocol – Protocol to use. If this is None, all Collections will be returned except those marked for deletion.
- catalog¶
- children¶
- coverage_records¶
- create_external_integration(protocol)[source]¶
Create an ExternalIntegration for this Collection.
To be used immediately after creating a new Collection, e.g. in by_name_and_protocol, from_metadata_identifier, and various test methods that create mock Collections.
If an external integration already exists, return it instead of creating another one.
- Parameters:
protocol – The protocol known to be in use when getting licenses for this collection.
- credentials¶
- customlists¶
- data_source¶
Find the data source associated with this Collection.
Bibliographic metadata obtained through the collection protocol is recorded as coming from this data source. A LicensePool inserted into this collection will be associated with this data source, unless its bibliographic metadata indicates some other data source.
For most Collections, the integration protocol sets the data source. For collections that use the OPDS import protocol, the data source is a Collection-specific setting.
- default_audience¶
Return the default audience set up for this collection.
- Returns:
Default audience
- Return type:
Optional[str]
- default_loan_period(library, medium='Book')[source]¶
Until we hear otherwise from the license provider, we assume that someone who borrows a non-open-access item from this collection has it for this number of days.
- default_loan_period_setting(library, medium='Book')[source]¶
Until we hear otherwise from the license provider, we assume that someone who borrows a non-open-access item from this collection has it for this number of days.
- default_reservation_period¶
Until we hear otherwise from the license provider, we assume that someone who puts an item on hold has this many days to check it out before it goes to the next person in line.
- delete(search_index=None)[source]¶
Delete a collection.
Collections can have hundreds of thousands of LicensePools. This deletes a collection gradually in a way that can be confined to the background and survive interruption.
- disassociate_library(library)[source]¶
Disassociate a Library from this Collection and delete any relevant ConfigurationSettings.
- explain(include_secrets=False)[source]¶
Create a series of human-readable strings to explain a collection’s settings.
- Parameters:
include_secrets – For security reasons, sensitive settings such as passwords are not displayed by default.
- Returns:
A list of explanatory strings.
- external_account_id¶
- property external_integration¶
Find the external integration for this Collection, assuming it already exists.
This is generally a safe assumption since by_name_and_protocol and from_metadata_identifier both create ExternalIntegrations for the Collections they create.
- external_integration_id¶
- classmethod from_metadata_identifier(_db, metadata_identifier, data_source=None)[source]¶
Finds or creates a Collection on the metadata wrangler, based on its unique metadata_identifier.
- id¶
- isbns_updated_since(_db, timestamp)[source]¶
Finds all ISBNs in a collection’s catalog that have been updated since the timestamp but don’t have a Work to show for it. Used in the metadata wrangler.
- Returns:
a Query
- libraries¶
- licensepools¶
- licensepools_with_works_updated_since(_db, timestamp)[source]¶
Finds all LicensePools in a collection’s catalog whose Works’ OPDS entries have been updated since the timestamp. Used by the metadata wrangler.
- Parameters:
_db – A database connection,
timestamp – A datetime.timestamp object
- Returns:
a Query that yields LicensePools. The Work and Identifier associated with each LicensePool have been pre-loaded, giving the caller all the information necessary to create full OPDS entries for the works.
- marked_for_deletion¶
- property metadata_identifier¶
Identifier based on collection details that uniquely represents this Collection on the metadata wrangler. This identifier is composed of the Collection protocol and account identifier.
A circulation manager provides a Collection’s metadata identifier as part of collection registration. The metadata wrangler creates a corresponding Collection on its side, named after the metadata identifier – regardless of the name of that collection on the circulation manager side.
- name¶
- parent¶
- parent_id¶
- property parents¶
- property pools_with_no_delivery_mechanisms¶
Find all LicensePools in this Collection that have no delivery mechanisms whatsoever.
- Returns:
A query object.
- primary_identifier_source¶
Identify if should try to use another identifier than <id>
- protocol¶
What protocol do we need to use to get licenses for this collection?
- classmethod restrict_to_ready_deliverable_works(query, collection_ids=None, show_suppressed=False, allow_holds=True)[source]¶
Restrict a query to show only presentation-ready works present in an appropriate collection which the default client can fulfill.
Note that this assumes the query has an active join against LicensePool and Edition.
- Parameters:
query – The query to restrict.
show_suppressed – Include titles that have nothing but suppressed LicensePools.
collection_ids – Only include titles in the given collections.
allow_holds – If false, pools with no available copies will be hidden.
- timestamps¶
- property unique_account_id¶
Identifier that uniquely represents this Collection of works
- class core.model.collection.CollectionConfigurationStorage(external_integration_association, collection)[source]¶
Bases:
BaseConfigurationStorage
Serializes and deserializes values as library’s configuration settings
- class core.model.collection.CollectionIdentifier[source]¶
Bases:
object
- collection_id¶
- identifier_id¶
- exception core.model.collection.CollectionMissing[source]¶
Bases:
Exception
An operation was attempted that can only happen within the context of a Collection, but there was no Collection available.
- class core.model.collection.HasExternalIntegrationPerCollection[source]¶
Bases:
object
Interface allowing to get access to an external integration
core.model.complaint module¶
- class core.model.complaint.Complaint(**kwargs)[source]¶
Bases:
Base
A complaint about a LicensePool (or, potentially, something else).
- LICENSE_POOL_TYPES = ['cannot-fulfill-loan', 'cannot-issue-loan', 'cannot-render', 'cannot-return']¶
- VALID_TYPES = {'http://librarysimplified.org/terms/problem/bad-cover-image', 'http://librarysimplified.org/terms/problem/bad-description', 'http://librarysimplified.org/terms/problem/cannot-fulfill-loan', 'http://librarysimplified.org/terms/problem/cannot-issue-loan', 'http://librarysimplified.org/terms/problem/cannot-render', 'http://librarysimplified.org/terms/problem/cannot-return', 'http://librarysimplified.org/terms/problem/wrong-age-range', 'http://librarysimplified.org/terms/problem/wrong-audience', 'http://librarysimplified.org/terms/problem/wrong-author', 'http://librarysimplified.org/terms/problem/wrong-genre', 'http://librarysimplified.org/terms/problem/wrong-medium', 'http://librarysimplified.org/terms/problem/wrong-title'}¶
- detail¶
- property for_license_pool¶
- id¶
- license_pool¶
- license_pool_id¶
- classmethod register(license_pool, type, source, detail, resolved=None)[source]¶
Register a problem detail document as a Complaint against the given LicensePool.
- resolved¶
- source¶
- timestamp¶
- type¶
core.model.configuration module¶
- class core.model.configuration.BaseConfigurationStorage[source]¶
Bases:
object
Serializes and deserializes values as configuration settings
- class core.model.configuration.ConfigurationAttribute(value)[source]¶
Bases:
Enum
Enumeration of configuration setting attributes
- CATEGORY = 'category'¶
- DEFAULT = 'default'¶
- DESCRIPTION = 'description'¶
- FORMAT = 'format'¶
- KEY = 'key'¶
- LABEL = 'label'¶
- OPTIONS = 'options'¶
- REQUIRED = 'required'¶
- TYPE = 'type'¶
- class core.model.configuration.ConfigurationAttributeType(value)[source]¶
Bases:
Enum
Enumeration of configuration setting types
- LIST = 'list'¶
- MENU = 'menu'¶
- NUMBER = 'number'¶
- SELECT = 'select'¶
- TEXT = 'text'¶
- TEXTAREA = 'textarea'¶
- class core.model.configuration.ConfigurationFactory[source]¶
Bases:
object
Factory creating new instances of ConfigurationGrouping class descendants.
- create(configuration_storage, db, configuration_grouping_class)[source]¶
Create a new instance of ConfigurationGrouping.
- Parameters:
configuration_storage (ConfigurationStorage) – ConfigurationStorage object
db (sqlalchemy.orm.session.Session) – Database session
configuration_grouping_class (Type[ConfigurationGrouping]) – Configuration bucket’s class
- Returns:
ConfigurationGrouping instance
- Return type:
- class core.model.configuration.ConfigurationGrouping(configuration_storage, db)[source]¶
Bases:
HasConfigurationSettings
Base class for all classes containing configuration settings
NOTE: Be aware that it’s valid only while a database session is valid and must not be stored between requests
- get_setting_value(setting_name)[source]¶
Returns a settings’value
- Parameters:
setting_name (string) – Name of the setting
- Returns:
Setting’s value
- Return type:
Any
- set_setting_value(setting_name, setting_value)[source]¶
Sets setting’s value
- Parameters:
setting_name (string) – Name of the setting
setting_value (Any) – New value of the setting
- class core.model.configuration.ConfigurationMetadata(key, label, description, type, required=False, default=None, options=None, category=None, format=None, index=None)[source]¶
Bases:
object
Contains configuration metadata
- property category¶
Returns the setting’s category
- Returns:
Setting’s category
- Return type:
string
- property default¶
Returns the setting’s default value
- Returns:
Setting’s default value
- Return type:
string
- property description¶
Returns the setting’s description
- Returns:
Setting’s description
- Return type:
string
- property format¶
Returns the setting’s format
- Returns:
Setting’s format
- Return type:
string
- static get_configuration_metadata(cls)[source]¶
Returns a list of 2-tuples containing information ConfigurationMetadata properties in the specified class
- Parameters:
cls (type) – Class
- Returns:
List of 2-tuples containing information ConfigurationMetadata properties in the specified class
- Return type:
List[Tuple[string, ConfigurationMetadata]]
- property index¶
- property key¶
Returns the setting’s key
- Returns:
Setting’s key
- Return type:
string
- property label¶
Returns the setting’s label
- Returns:
Setting’s label
- Return type:
string
- property options¶
Returns the setting’s options (used in the case of select)
- Returns:
Setting’s options (used in the case of select)
- Return type:
string
- property required¶
Returns the boolean value indicating whether the setting is required or not
- Returns:
Boolean value indicating whether the setting is required or not
- Return type:
string
- static to_bool(metadata)[source]¶
- Return a boolean scalar indicating whether the configuration setting
contains a value that can be treated as True (see ConfigurationSetting.MEANS_YES).
- Parameters:
metadata (ConfigurationMetadata) – ConfigurationMetadata object
- Returns:
Boolean scalar indicating whether this configuration setting contains a value that can be treated as True
- Return type:
bool
- property type¶
Returns the setting’s type
- Returns:
Setting’s type
- Return type:
string
- class core.model.configuration.ConfigurationOption(key, label)[source]¶
Bases:
object
Key-value pair containing information about configuration attribute option
- static from_enum(cls)[source]¶
Convers Enum to a list of options in the SETTINGS format
- Parameters:
cls (type) – Enum type
- Returns:
List of options in the SETTINGS format
- Return type:
List[Dict]
- property key¶
Returns option’s key
- Returns:
Option’s key
- Return type:
string
- property label¶
Returns option’s label
- Returns:
Option’s label
- Return type:
string
- class core.model.configuration.ConfigurationSetting(**kwargs)[source]¶
Bases:
Base
,HasFullTableCache
An extra piece of site configuration. A ConfigurationSetting may be associated with an ExternalIntegration, a Library, both, or neither. * The secret used by the circulation manager to sign OAuth bearer tokens is not associated with an ExternalIntegration or with a Library. * The link to a library’s privacy policy is associated with the Library, but not with any particular ExternalIntegration. * The “website ID” for an Overdrive collection is associated with an ExternalIntegration (the Overdrive integration), but not with any particular Library (since multiple libraries might share an Overdrive collection). * The “identifier prefix” used to determine which library a patron is a patron of, is associated with both a Library and an ExternalIntegration.
- EXCLUDED_AUDIO_DATA_SOURCES_DEFAULT = []¶
- MEANS_YES = {'t', 'true', 'y', 'yes'}¶
- property bool_value¶
Turn the value into a boolean if possible. :return: A boolean, or None if there is no value.
- classmethod excluded_audio_data_sources(_db)[source]¶
List the data sources whose audiobooks should not be published in feeds, either because this server can’t fulfill them or the expected client can’t play them. Most methods like this go into Configuration, but this one needs to reference data model objects for its default value.
- classmethod explain(_db, include_secrets=False)[source]¶
Explain all site-wide ConfigurationSettings.
- external_integration¶
- external_integration_id¶
- property float_value¶
Turn the value into an float if possible. :return: A float, or None if there is no value. :raise ValueError: If the value cannot be converted to a float.
- classmethod for_externalintegration(key, externalintegration)[source]¶
Find or create a ConfigurationSetting for the given ExternalIntegration.
- classmethod for_library(key, library)[source]¶
Find or create a ConfigurationSetting for the given Library.
- classmethod for_library_and_externalintegration(_db, key, library, external_integration)[source]¶
Find or create a ConfigurationSetting associated with a Library and an ExternalIntegration.
- id¶
- property int_value¶
Turn the value into an int if possible. :return: An integer, or None if there is no value. :raise ValueError: If the value cannot be converted to an int.
- property is_secret¶
Should the value of this key be treated as secret?
- property json_value¶
Interpret the value as JSON if possible. :return: An object, or None if there is no value. :raise ValueError: If the value cannot be parsed as JSON.
- key¶
- library¶
- library_id¶
- classmethod sitewide_secret(_db, key)[source]¶
Find or create a sitewide shared secret. The value of this setting doesn’t matter, only that it’s unique across the site and that it’s always available.
- value¶
What’s the current value of this configuration setting? If not present, the value may be inherited from some other ConfigurationSetting.
- class core.model.configuration.ConfigurationStorage(integration_association)[source]¶
Bases:
BaseConfigurationStorage
Serializes and deserializes values as configuration settings
- class core.model.configuration.ExternalIntegration(**kwargs)[source]¶
Bases:
Base
,HasFullTableCache
An external integration contains configuration for connecting to a third-party API.
- ADMIN_AUTH_GOAL = 'admin_auth'¶
- ADMIN_AUTH_PROTOCOLS = ['Google OAuth']¶
- ADOBE_VENDOR_ID = 'Adobe Vendor ID'¶
- ANALYTICS_GOAL = 'analytics'¶
- AXIS_360 = 'Axis 360'¶
- BIBBLIO = 'Bibblio'¶
- BIBLIOTHECA = 'Bibliotheca'¶
- CATALOG_GOAL = 'ils_catalog'¶
- CDN = 'CDN'¶
- CDN_GOAL = 'CDN'¶
- CLOUDWATCH = 'AWS Cloudwatch Logs'¶
- CONTENT_CAFE = 'Content Cafe'¶
- CONTENT_SERVER = 'Content Server'¶
- CUSTOM_ACCEPT_HEADER = 'custom_accept_header'¶
- DATA_SOURCE_FOR_LICENSE_PROTOCOL = {'Axis 360': 'Axis 360', 'Bibliotheca': 'Bibliotheca', 'Enki': 'Enki', 'FeedBooks': 'FeedBooks', 'Odilo': 'Odilo', 'Overdrive': 'Overdrive', 'RBdigital': 'RBdigital'}¶
- DCTERMS_IDENTIFIER = 'first_dcterms_identifier'¶
- DIRECTORY_IMPORT = 'Directory Import'¶
- DISCOVERY_GOAL = 'discovery'¶
- DRM_GOAL = 'drm'¶
- ELASTICSEARCH = 'Elasticsearch'¶
- ENKI = 'Enki'¶
- FEEDBOOKS = 'FeedBooks'¶
- GOOGLE_ANALYTICS = 'Google Analytics'¶
- GOOGLE_OAUTH = 'Google OAuth'¶
- GUTENBERG = 'Gutenberg'¶
- INTERNAL_LOGGING = 'Internal logging'¶
- LCP = 'LCP'¶
- LICENSE_GOAL = 'licenses'¶
- LICENSE_PROTOCOLS = ['OPDS Import', 'Overdrive', 'Odilo', 'Bibliotheca', 'Axis 360', 'RBdigital', 'Gutenberg', 'Enki', 'Manual intervention']¶
- LOGGING_GOAL = 'logging'¶
- LOGGLY = 'Loggly'¶
- MANUAL = 'Manual intervention'¶
- MARC_EXPORT = 'MARC Export'¶
- METADATA_GOAL = 'metadata'¶
- METADATA_WRANGLER = 'Metadata Wrangler'¶
- MINIO = 'MinIO'¶
- NOVELIST = 'NoveList Select'¶
- NYPL_SHADOWCAT = 'Shadowcat'¶
- NYT = 'New York Times'¶
- ODILO = 'Odilo'¶
- ONE_CLICK = 'RBdigital'¶
- OPDS2_IMPORT = 'OPDS 2.0 Import'¶
- OPDS_FOR_DISTRIBUTORS = 'OPDS for Distributors'¶
- OPDS_IMPORT = 'OPDS Import'¶
- OPDS_REGISTRATION = 'OPDS Registration'¶
- OVERDRIVE = 'Overdrive'¶
- PASSWORD = 'password'¶
- PATRON_AUTH_GOAL = 'patron_auth'¶
- PRIMARY_IDENTIFIER_SOURCE = 'primary_identifier_source'¶
- PROQUEST = 'ProQuest'¶
- RB_DIGITAL = 'RBdigital'¶
- S3 = 'Amazon S3'¶
- SEARCH_GOAL = 'search'¶
- STORAGE_GOAL = 'storage'¶
- URL = 'url'¶
- USERNAME = 'username'¶
- collections¶
- custom_accept_header¶
- explain(library=None, include_secrets=False)[source]¶
Create a series of human-readable strings to explain an ExternalIntegration’s settings.
- Parameters:
library – Include additional settings imposed upon this ExternalIntegration by the given Library.
include_secrets – For security reasons, sensitive settings such as passwords are not displayed by default.
- Returns:
A list of explanatory strings.
- classmethod for_collection_and_purpose(_db, collection, purpose)[source]¶
Find the ExternalIntegration for the collection.
- Parameters:
collection – Use the mirror configuration for this Collection.
purpose – Use the purpose of the mirror configuration.
- classmethod for_library_and_goal(_db, library, goal)[source]¶
Find all ExternalIntegrations associated with the given Library and the given goal. :return: A Query.
- goal¶
- id¶
- libraries¶
- links¶
- name¶
- classmethod one_for_library_and_goal(_db, library, goal)[source]¶
Find the ExternalIntegration associated with the given Library and the given goal. :return: An ExternalIntegration, or None. :raise: CannotLoadConfiguration
- password¶
- primary_identifier_source¶
- protocol¶
- setting(key)[source]¶
Find or create a ConfigurationSetting on this ExternalIntegration. :param key: Name of the setting. :return: A ConfigurationSetting
- settings¶
- url¶
- username¶
- classmethod with_setting_value(_db, protocol, goal, key, value)[source]¶
Find ExternalIntegrations with the given protocol, goal, and with a particular ConfigurationSetting key/value pair. This is useful in a scenario where an ExternalIntegration is made unique by a ConfigurationSetting, such as ExternalIntegration.URL, rather than by anything in the ExternalIntecation itself.
- Parameters:
protocol – ExternalIntegrations must have this protocol.
goal – ExternalIntegrations must have this goal.
key – Look only at ExternalIntegrations with a ConfigurationSetting for this key.
value – Find ExternalIntegrations whose ConfigurationSetting has this value.
- Returns:
A Query object.
- class core.model.configuration.ExternalIntegrationLink(**kwargs)[source]¶
Bases:
Base
,HasFullTableCache
- COLLECTION_MIRROR_SETTINGS = [{'key': 'covers_mirror_integration_id', 'label': l'Covers Mirror', 'description': l'Any cover images encountered while importing content from this collection can be mirrored to a server you control.', 'type': 'select', 'options': [{'key': 'NO_MIRROR', 'label': l'None - Do not mirror cover images'}]}, {'key': 'books_mirror_integration_id', 'label': l'Open Access Books Mirror', 'description': l'Any free books encountered while importing content from this collection can be mirrored to a server you control.', 'type': 'select', 'options': [{'key': 'NO_MIRROR', 'label': l'None - Do not mirror free books'}]}, {'key': 'protected_access_books_mirror_integration_id', 'label': l'Protected Access Books Mirror', 'description': l'Any self-hosted, commercially licensed books encountered while importing content from this collection can be mirrored to a server you control.', 'type': 'select', 'options': [{'key': 'NO_MIRROR', 'label': l'None - Do not mirror self-hosted, commercially licensed books'}]}]¶
- COVERS = 'covers_mirror'¶
- COVERS_KEY = 'covers_mirror_integration_id'¶
- MARC = 'MARC_mirror'¶
- NO_MIRROR_INTEGRATION = 'NO_MIRROR'¶
- OPEN_ACCESS_BOOKS = 'books_mirror'¶
- OPEN_ACCESS_BOOKS_KEY = 'books_mirror_integration_id'¶
- PROTECTED_ACCESS_BOOKS = 'protected_access_books_mirror'¶
- PROTECTED_ACCESS_BOOKS_KEY = 'protected_access_books_mirror_integration_id'¶
- external_integration_id¶
- id¶
- library_id¶
- mirror_description_type = 'self-hosted, commercially licensed books'¶
- mirror_label = 'Protected Access Books Mirror'¶
- mirror_setting = {'description_type': 'self-hosted, commercially licensed books', 'key': 'protected_access_books_mirror_integration_id', 'label': 'Protected Access Books Mirror', 'type': 'protected_access_books_mirror'}¶
- mirror_settings = [{'key': 'covers_mirror_integration_id', 'type': 'covers_mirror', 'description_type': 'cover images', 'label': 'Covers Mirror'}, {'key': 'books_mirror_integration_id', 'type': 'books_mirror', 'description_type': 'free books', 'label': 'Open Access Books Mirror'}, {'key': 'protected_access_books_mirror_integration_id', 'type': 'protected_access_books_mirror', 'description_type': 'self-hosted, commercially licensed books', 'label': 'Protected Access Books Mirror'}]¶
- mirror_type = 'protected_access_books_mirror'¶
- other_integration¶
- other_integration_id¶
- purpose¶
- settings = [{'key': 'covers_mirror_integration_id', 'label': l'Covers Mirror', 'description': l'Any cover images encountered while importing content from this collection can be mirrored to a server you control.', 'type': 'select', 'options': [{'key': 'NO_MIRROR', 'label': l'None - Do not mirror cover images'}]}, {'key': 'books_mirror_integration_id', 'label': l'Open Access Books Mirror', 'description': l'Any free books encountered while importing content from this collection can be mirrored to a server you control.', 'type': 'select', 'options': [{'key': 'NO_MIRROR', 'label': l'None - Do not mirror free books'}]}, {'key': 'protected_access_books_mirror_integration_id', 'label': l'Protected Access Books Mirror', 'description': l'Any self-hosted, commercially licensed books encountered while importing content from this collection can be mirrored to a server you control.', 'type': 'select', 'options': [{'key': 'NO_MIRROR', 'label': l'None - Do not mirror self-hosted, commercially licensed books'}]}]¶
- class core.model.configuration.HasConfigurationSettings[source]¶
Bases:
object
Interface representing class containing ConfigurationMetadata properties
- class core.model.configuration.HasExternalIntegration[source]¶
Bases:
object
Interface allowing to get access to an external integration
core.model.constants module¶
- class core.model.constants.DataSourceConstants[source]¶
Bases:
object
- ADOBE = 'Adobe DRM'¶
- AMAZON = 'Amazon'¶
- AXIS_360 = 'Axis 360'¶
- BIBBLIO = 'Bibblio'¶
- BIBLIOTHECA = 'Bibliotheca'¶
- CONTENT_CAFE = 'Content Cafe'¶
- COVER_IMAGE_PRIORITY = ['Library Simplified metadata wrangler', 'Library staff', 'Manual intervention']¶
- DEPRECATED_NAMES = {'3M': 'Bibliotheca', 'OneClick': 'RBdigital'}¶
- ELIB = 'eLiburutegia'¶
- ENKI = 'Enki'¶
- FEEDBOOKS = 'FeedBooks'¶
- GUTENBERG = 'Gutenberg'¶
- GUTENBERG_COVER_GENERATOR = 'Gutenberg Illustrated'¶
- GUTENBERG_EPUB_GENERATOR = 'Project Gutenberg EPUB Generator'¶
- INTERNAL_PROCESSING = 'Library Simplified Internal Process'¶
- LCP = 'LCP'¶
- LIBRARY_STAFF = 'Library staff'¶
- MANUAL = 'Manual intervention'¶
- METADATA_WRANGLER = 'Library Simplified metadata wrangler'¶
- NOVELIST = 'NoveList Select'¶
- NYPL_SHADOWCAT = 'NYPL Shadowcat'¶
- NYT = 'New York Times'¶
- OA_CONTENT_SERVER = 'Library Simplified Open Access Content Server'¶
- OCLC = 'OCLC Classify'¶
- OCLC_LINKED_DATA = 'OCLC Linked Data'¶
- ODILO = 'Odilo'¶
- ONECLICK = 'RBdigital'¶
- OPEN_ACCESS_SOURCE_PRIORITY = ['unglue.it', 'Gutenberg', 'Project Gutenberg EPUB Generator', 'Project GITenberg', 'eLiburutegia', 'FeedBooks', 'Plympton', 'Standard Ebooks']¶
- OPEN_LIBRARY = 'Open Library'¶
- OVERDRIVE = 'Overdrive'¶
- PLYMPTON = 'Plympton'¶
- PRESENTATION_EDITION = 'Presentation edition generator'¶
- PRESENTATION_EDITION_PRIORITY = ['Library staff', 'Manual intervention']¶
- PROJECT_GITENBERG = 'Project GITenberg'¶
- PROQUEST = 'ProQuest'¶
- RB_DIGITAL = 'RBdigital'¶
- STANDARD_EBOOKS = 'Standard Ebooks'¶
- THREEM = 'Bibliotheca'¶
- UNGLUE_IT = 'unglue.it'¶
- VIAF = 'VIAF'¶
- WEB = 'Web'¶
- XID = 'WorldCat xID'¶
- class core.model.constants.EditionConstants[source]¶
Bases:
object
- ALL_MEDIUM = <object object>¶
- AUDIO_MEDIUM = 'Audio'¶
- BOOK_MEDIUM = 'Book'¶
- CODEX_FORMAT = 'Codex'¶
- COURSEWARE_MEDIUM = 'Courseware'¶
- ELECTRONIC_FORMAT = 'Electronic'¶
- FULFILLABLE_MEDIA = ['Book', 'Audio']¶
- IMAGE_MEDIUM = 'Image'¶
- KNOWN_MEDIA = ('Book', 'Periodical', 'Audio', 'Music', 'Video', 'Image', 'Courseware')¶
- MUSIC_MEDIUM = 'Music'¶
- PERIODICAL_MEDIUM = 'Periodical'¶
- VIDEO_MEDIUM = 'Video'¶
- additional_type_to_medium = {'http://bib.schema.org/Audiobook': 'Audio', 'http://schema.org/Book': 'Book', 'http://schema.org/Course': 'Courseware', 'http://schema.org/EBook': 'Book', 'http://schema.org/ImageObject': 'Image', 'http://schema.org/MusicRecording': 'Music', 'http://schema.org/PublicationIssue': 'Periodical', 'http://schema.org/VideoObject': 'Video'}¶
- k = 'Courseware'¶
- medium_for_permanent_work_id = {'Audio': 'book', 'Book': 'book', 'Courseware': 'courseware', 'Image': 'image', 'Music': 'music', 'Periodical': 'book', 'Video': 'movie'}¶
- medium_to_additional_type = {'Audio': 'http://bib.schema.org/Audiobook', 'Book': 'http://schema.org/EBook', 'Courseware': 'http://schema.org/Course', 'Image': 'http://schema.org/ImageObject', 'Music': 'http://schema.org/MusicRecording', 'Periodical': 'http://schema.org/PublicationIssue', 'Video': 'http://schema.org/VideoObject'}¶
- v = 'http://schema.org/Course'¶
- class core.model.constants.IdentifierConstants[source]¶
Bases:
object
- ASIN = 'ASIN'¶
- AXIS_360_ID = 'Axis 360 ID'¶
- BIBBLIO_CONTENT_ITEM_ID = 'Bibblio Content Item ID'¶
- BIBLIOCOMMONS_ID = 'Bibliocommons ID'¶
- BIBLIOTHECA_ID = 'Bibliotheca ID'¶
- DEPRECATED_NAMES = {'3M ID': 'Bibliotheca ID', 'OneClick ID': 'RBdigital ID'}¶
- DOI = 'DOI'¶
- ELIB_ID = 'eLiburutegia ID'¶
- ENKI_ID = 'Enki ID'¶
- GUTENBERG_ID = 'Gutenberg ID'¶
- GUTENBERG_URN_SCHEME_PREFIX = 'http://www.gutenberg.org/ebooks/'¶
- GUTENBERG_URN_SCHEME_RE = re.compile('http://www.gutenberg.org/ebooks/([0-9]+)')¶
- IDEAL_COVER_ASPECT_RATIO = 0.6666666666666666¶
- IDEAL_IMAGE_HEIGHT = 240¶
- IDEAL_IMAGE_WIDTH = 160¶
- ISBN = 'ISBN'¶
- ISBN_URN_SCHEME_PREFIX = 'urn:isbn:'¶
- LICENSE_PROVIDING_IDENTIFIER_TYPES = ['Bibliotheca ID', 'Overdrive ID', 'Odilo ID', 'Axis 360 ID', 'Gutenberg ID', 'eLiburutegia ID', 'SuDoc Call Number']¶
- NOVELIST_ID = 'NoveList ID'¶
- OCLC_NUMBER = 'OCLC Number'¶
- OCLC_WORK = 'OCLC Work ID'¶
- ODILO_ID = 'Odilo ID'¶
- ONECLICK_ID = 'RBdigital ID'¶
- OPEN_LIBRARY_ID = 'OLID'¶
- OTHER_URN_SCHEME_PREFIX = 'urn:'¶
- OVERDRIVE_ID = 'Overdrive ID'¶
- PROQUEST_ID = 'ProQuest Doc ID'¶
- RB_DIGITAL_ID = 'RBdigital ID'¶
- SUDOC_CALL_NUMBER = 'SuDoc Call Number'¶
- THREEM_ID = 'Bibliotheca ID'¶
- UPC = 'UPC'¶
- URI = 'URI'¶
- URN_SCHEME_PREFIX = 'urn:librarysimplified.org/terms/id/'¶
- class core.model.constants.LinkRelations[source]¶
Bases:
object
- ALTERNATE = 'alternate'¶
- AUTHOR = 'http://schema.org/author'¶
- BORROW = 'http://opds-spec.org/acquisition/borrow'¶
- CANONICAL = 'canonical'¶
- CIRCULATION_ALLOWED = ['http://opds-spec.org/acquisition/open-access', 'http://opds-spec.org/acquisition/', 'http://opds-spec.org/acquisition/borrow', 'http://opds-spec.org/acquisition']¶
- DESCRIPTION = 'http://schema.org/description'¶
- DRM_ENCRYPTED_DOWNLOAD = 'http://opds-spec.org/acquisition/'¶
- GENERIC_OPDS_ACQUISITION = 'http://opds-spec.org/acquisition'¶
- ILLUSTRATION = 'http://librarysimplified.org/terms/rel/illustration'¶
- IMAGE = 'http://opds-spec.org/image'¶
- METADATA_ALLOWED = ['canonical', 'http://opds-spec.org/image', 'http://opds-spec.org/image/thumbnail', 'http://librarysimplified.org/terms/rel/illustration', 'http://schema.org/Review', 'http://schema.org/description', 'http://librarysimplified.org/terms/rel/short-description', 'http://schema.org/author', 'alternate', 'http://opds-spec.org/acquisition/sample']¶
- MIRRORED = ['http://opds-spec.org/acquisition/open-access', 'http://opds-spec.org/acquisition', 'http://opds-spec.org/image', 'http://opds-spec.org/image/thumbnail']¶
- OPEN_ACCESS_DOWNLOAD = 'http://opds-spec.org/acquisition/open-access'¶
- REVIEW = 'http://schema.org/Review'¶
- SAMPLE = 'http://opds-spec.org/acquisition/sample'¶
- SELF_HOSTED_BOOKS = ['http://opds-spec.org/acquisition', 'http://opds-spec.org/acquisition/open-access']¶
- SHORT_DESCRIPTION = 'http://librarysimplified.org/terms/rel/short-description'¶
- THUMBNAIL_IMAGE = 'http://opds-spec.org/image/thumbnail'¶
- class core.model.constants.MediaTypes[source]¶
Bases:
object
- AMAZON_KF8_MEDIA_TYPE = 'application/x-mobi8-ebook'¶
- APPLICATION_XML_MEDIA_TYPE = 'application/xml'¶
- AUDIOBOOK_MANIFEST_MEDIA_TYPE = 'application/audiobook+json'¶
- AUDIOBOOK_MEDIA_TYPES = ['application/vnd.overdrive.circulation.api+json;profile=audiobook', 'application/audiobook+json', 'application/audiobook+zip']¶
- AUDIOBOOK_PACKAGE_MEDIA_TYPE = 'application/audiobook+zip'¶
- BOOK_MEDIA_TYPES = ['application/epub+zip', 'application/pdf', 'application/x-mobipocket-ebook', 'audio/mpeg', 'application/x-mobi8-ebook']¶
- COMMON_EBOOK_EXTENSIONS = ['.epub', '.pdf', '.audiobook']¶
- COMMON_IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif']¶
- EPUB_MEDIA_TYPE = 'application/epub+zip'¶
- FILE_EXTENSIONS = {'application/audiobook+json': 'audiobook-manifest', 'application/audiobook+zip': 'audiobook', 'application/epub+zip': 'epub', 'application/pdf': 'pdf', 'application/vnd.librarysimplified.scorm+zip': 'zip', 'application/x-mobipocket-ebook': 'mobi', 'application/xml': 'xml', 'application/zip': 'zip', 'audio/mpeg': 'mp3', 'image/gif': 'gif', 'image/jpeg': 'jpg', 'image/png': 'png', 'image/svg+xml': 'svg', 'text/html': 'html', 'text/plain': 'txt', 'video/mp4': 'mp4', 'video/x-ms-wmv': 'wmv'}¶
- GENERIC_MEDIA_TYPES = ['application/octet-stream']¶
- GIF_MEDIA_TYPE = 'image/gif'¶
- IMAGE_MEDIA_TYPES = ['image/png', 'image/jpeg', 'image/gif', 'image/svg+xml']¶
- JPEG_MEDIA_TYPE = 'image/jpeg'¶
- MARC_MEDIA_TYPE = 'application/marc'¶
- MEDIA_TYPE_FOR_EXTENSION = {'.audiobook': 'application/audiobook+zip', '.audiobook-manifest': 'application/audiobook+json', '.epub': 'application/epub+zip', '.gif': 'image/gif', '.htm': 'text/html', '.html': 'text/html', '.jpeg': 'image/jpeg', '.jpg': 'image/jpeg', '.mobi': 'application/x-mobipocket-ebook', '.mp3': 'audio/mpeg', '.mp4': 'video/mp4', '.pdf': 'application/pdf', '.png': 'image/png', '.svg': 'image/svg+xml', '.txt': 'text/plain', '.wmv': 'video/x-ms-wmv', '.xml': 'application/xml', '.zip': 'application/zip'}¶
- MOBI_MEDIA_TYPE = 'application/x-mobipocket-ebook'¶
- MP3_MEDIA_TYPE = 'audio/mpeg'¶
- MP4_MEDIA_TYPE = 'video/mp4'¶
- OCTET_STREAM_MEDIA_TYPE = 'application/octet-stream'¶
- OVERDRIVE_AUDIOBOOK_MANIFEST_MEDIA_TYPE = 'application/vnd.overdrive.circulation.api+json;profile=audiobook'¶
- OVERDRIVE_EBOOK_MANIFEST_MEDIA_TYPE = 'application/vnd.overdrive.circulation.api+json;profile=ebook'¶
- OVERDRIVE_MANIFEST_MEDIA_TYPE = 'application/vnd.overdrive.circulation.api+json'¶
- PDF_MEDIA_TYPE = 'application/pdf'¶
- PNG_MEDIA_TYPE = 'image/png'¶
- SCORM_MEDIA_TYPE = 'application/vnd.librarysimplified.scorm+zip'¶
- SUPPORTED_BOOK_MEDIA_TYPES = ['application/epub+zip', 'application/pdf', 'application/audiobook+json']¶
- SVG_MEDIA_TYPE = 'image/svg+xml'¶
- TEXT_HTML_MEDIA_TYPE = 'text/html'¶
- TEXT_PLAIN = 'text/plain'¶
- TEXT_XML_MEDIA_TYPE = 'text/xml'¶
- WMV_MEDIA_TYPE = 'video/x-ms-wmv'¶
- ZIP_MEDIA_TYPE = 'application/zip'¶
- extension = '.zip'¶
- media_type = 'application/vnd.librarysimplified.scorm+zip'¶
core.model.contributor module¶
- class core.model.contributor.Contribution(**kwargs)[source]¶
Bases:
Base
A contribution made by a Contributor to a Edition.
- contributor¶
- contributor_id¶
- edition¶
- edition_id¶
- id¶
- role¶
- class core.model.contributor.Contributor(**kwargs)[source]¶
Bases:
Base
Someone (usually human) who contributes to books.
- ACTOR_ROLE = 'Actor'¶
- ADAPTER_ROLE = 'Adapter'¶
- AFTERWORD_ROLE = 'Afterword Author'¶
- ALPHABETIC = re.compile('[a-zA-z]')¶
- ARTIST_ROLE = 'Artist'¶
- ASSOCIATED_ROLE = 'Associated name'¶
- AUTHOR_ROLE = 'Author'¶
- AUTHOR_ROLES = {'Author', 'Primary Author'}¶
- AUTHOR_SUBSTITUTE_ROLES = ['Editor', 'Compiler', 'Composer', 'Director', 'Contributor', 'Translator', 'Adapter', 'Photographer', 'Artist', 'Lyricist', 'Copyright holder']¶
- BIRTH_DATE = 'birthDate'¶
- COLLABORATOR_ROLE = 'Collaborator'¶
- COLOPHON_ROLE = 'Colophon Author'¶
- COLORIST_ROLE = 'Colorist'¶
- COMPILER_ROLE = 'Compiler'¶
- COMPOSER_ROLE = 'Composer'¶
- CONTRIBUTOR_ROLE = 'Contributor'¶
- COPYRIGHT_HOLDER_ROLE = 'Copyright holder'¶
- DATE_RES = [re.compile('\\(?[0-9?]+-\\)?'), re.compile('\\(?[0-9]+st cent\\)?'), re.compile('\\(?[0-9]+nd cent\\)?'), re.compile('\\(?[0-9]+th cent\\)?'), re.compile('\\(?\x08circa\\)?')]¶
- DEATH_DATE = 'deathDate'¶
- DESIGNER_ROLE = 'Designer'¶
- DIRECTOR_ROLE = 'Director'¶
- EDITOR_ROLE = 'Editor'¶
- ENGINEER_ROLE = 'Engineer'¶
- EXECUTIVE_PRODUCER_ROLE = 'Executive Producer'¶
- FOREWORD_ROLE = 'Foreword Author'¶
- ILLUSTRATOR_ROLE = 'Illustrator'¶
- INKER_ROLE = 'Inker'¶
- INTRODUCTION_ROLE = 'Introduction Author'¶
- LETTERER_ROLE = 'Letterer'¶
- LYRICIST_ROLE = 'Lyricist'¶
- MARC_ROLE_CODES = {'Actor': 'act', 'Adapter': 'adp', 'Afterword Author': 'aft', 'Artist': 'art', 'Associated name': 'asn', 'Author': 'aut', 'Collaborator': 'ctb', 'Colophon Author': 'aft', 'Colorist': 'clr', 'Compiler': 'com', 'Composer': 'cmp', 'Contributor': 'ctb', 'Copyright holder': 'cph', 'Designer': 'dsr', 'Director': 'drt', 'Editor': 'edt', 'Engineer': 'eng', 'Executive Producer': 'pro', 'Foreword Author': 'wpr', 'Illustrator': 'ill', 'Inker': 'ctb', 'Introduction Author': 'win', 'Letterer': 'ctb', 'Lyricist': 'lyr', 'Musician': 'mus', 'Narrator': 'nrt', 'Penciler': 'ctb', 'Performer': 'prf', 'Photographer': 'pht', 'Primary Author': 'aut', 'Producer': 'pro', 'Transcriber': 'trc', 'Translator': 'trl', 'Unknown': 'asn'}¶
- MUSICIAN_ROLE = 'Musician'¶
- NARRATOR_ROLE = 'Narrator'¶
- NUMBERS = re.compile('[0-9]')¶
- PARENTHETICAL = re.compile('\\([^)]*\\)')¶
- PENCILER_ROLE = 'Penciler'¶
- PERFORMER_ROLE = 'Performer'¶
- PERFORMER_ROLES = ['Actor', 'Performer', 'Narrator', 'Musician']¶
- PHOTOGRAPHER_ROLE = 'Photographer'¶
- PRIMARY_AUTHOR_ROLE = 'Primary Author'¶
- PRODUCER_ROLE = 'Producer'¶
- TRANSCRIBER_ROLE = 'Transcriber'¶
- TRANSLATOR_ROLE = 'Translator'¶
- UNKNOWN_ROLE = 'Unknown'¶
- aliases¶
- biography¶
- contributions¶
- default_names(default_display_name=None)[source]¶
Attempt to derive a family name (“Twain”) and a display name (“Mark Twain”) from a catalog name (“Twain, Mark”).
This is full of pitfalls, which is why we prefer to use data from VIAF. But when there is no data from VIAF, the output of this algorithm is better than the input in pretty much every case.
- display_name¶
- extra¶
- family_name¶
- id¶
- lc¶
- classmethod lookup(_db, sort_name=None, viaf=None, lc=None, aliases=None, extra=None, create_new=True, name=None)[source]¶
Find or create a record (or list of records) for the given Contributor. :return: A tuple of found Contributor (or None), and a boolean flag indicating if new Contributor database object has beed created.
- merge_into(destination)[source]¶
Two Contributor records should be the same.
Merge this one into the other one.
For now, this should only be used when the exact same record comes in through two sources. It should not be used when two Contributors turn out to represent different names for the same human being, e.g. married names or (especially) pen names. Just because we haven’t thought that situation through well enough.
- sort_name¶
- viaf¶
- wikipedia_name¶
core.model.coverage module¶
- class core.model.coverage.BaseCoverageRecord[source]¶
Bases:
object
Contains useful constants used by both CoverageRecord and WorkCoverageRecord.
- ALL_STATUSES = ['registered', 'success', 'transient failure', 'persistent failure']¶
- DEFAULT_COUNT_AS_COVERED = ['success', 'persistent failure']¶
- PERSISTENT_FAILURE = 'persistent failure'¶
- PREVIOUSLY_ATTEMPTED = ['success', 'transient failure', 'persistent failure']¶
- REGISTERED = 'registered'¶
- SUCCESS = 'success'¶
- TRANSIENT_FAILURE = 'transient failure'¶
- classmethod not_covered(count_as_covered=None, count_as_not_covered_if_covered_before=None)[source]¶
Filter a query to find only items without coverage records.
- Parameters:
count_as_covered – A list of constants that indicate types of coverage records that should count as ‘coverage’ for purposes of this query.
count_as_not_covered_if_covered_before – If a coverage record exists, but is older than the given date, do not count it as covered.
- Returns:
A clause that can be passed in to Query.filter().
- status_enum = Enum('success', 'transient failure', 'persistent failure', 'registered', name='coverage_status')¶
- class core.model.coverage.CoverageRecord(**kwargs)[source]¶
Bases:
Base
,BaseCoverageRecord
A record of a Identifier being used as input into some process.
- CHOOSE_COVER_OPERATION = 'choose-cover'¶
- IMPORT_OPERATION = 'import'¶
- METADATA_UPLOAD_OPERATION = 'metadata-upload'¶
- REAP_OPERATION = 'reap'¶
- REPAIR_SORT_NAME_OPERATION = 'repair-sort-name'¶
- RESOLVE_IDENTIFIER_OPERATION = 'resolve-identifier'¶
- SET_EDITION_METADATA_OPERATION = 'set-edition-metadata'¶
- classmethod add_for(edition, data_source, operation=None, timestamp=None, status='success', collection=None)[source]¶
- classmethod bulk_add(identifiers, data_source, operation=None, timestamp=None, status='success', exception=None, collection=None, force=False)[source]¶
Create and update CoverageRecords so that every Identifier in identifiers has an identical record.
- collection¶
- collection_id¶
- data_source¶
- data_source_id¶
- exception¶
- id¶
- identifier¶
- identifier_id¶
- operation¶
- status¶
- timestamp¶
- class core.model.coverage.Timestamp(**kwargs)[source]¶
Bases:
Base
Tracks the activities of Monitors, CoverageProviders, and general scripts.
- CLEAR_VALUE = <object object>¶
- COVERAGE_PROVIDER_TYPE = 'coverage_provider'¶
- MONITOR_TYPE = 'monitor'¶
- SCRIPT_TYPE = 'script'¶
- achievements¶
- collection¶
- collection_id¶
- counter¶
- exception¶
- finish¶
- id¶
- service¶
- service_type¶
- service_type_enum = Enum('monitor', 'coverage_provider', 'script', name='service_type')¶
- classmethod stamp(_db, service, service_type, collection=None, start=None, finish=None, achievements=None, counter=None, exception=None)[source]¶
Set a Timestamp, creating it if necessary.
This should be called once a service has stopped running, whether or not it was able to complete its task.
- Parameters:
_db – A database connection.
service – The name of the service associated with the Timestamp.
service_type – The type of the service associated with the Timestamp. This must be one of the values in Timestmap.service_type_enum.
collection – The Collection, if any, on which this service just ran.
start – The time at which this service started running. Defaults to now.
finish – The time at which this service stopped running. Defaults to now.
achievements – A human-readable description of what the service did during its run.
counter – An integer item of state that the service may use to track its progress between runs.
exception – A stack trace for the exception, if any, which stopped the service from running.
- start¶
- class core.model.coverage.WorkCoverageRecord(**kwargs)[source]¶
Bases:
Base
,BaseCoverageRecord
A record of some operation that was performed on a Work. This is similar to CoverageRecord, which operates on Identifiers, but since Work identifiers have no meaning outside of the database, we presume that all the operations involve internal work only, and as such there is no data_source_id.
- CHOOSE_EDITION_OPERATION = 'choose-edition'¶
- CLASSIFY_OPERATION = 'classify'¶
- GENERATE_MARC_OPERATION = 'generate-marc'¶
- GENERATE_OPDS_OPERATION = 'generate-opds'¶
- QUALITY_OPERATION = 'quality'¶
- SUMMARY_OPERATION = 'summary'¶
- UPDATE_SEARCH_INDEX_OPERATION = 'update-search-index'¶
- classmethod bulk_add(works, operation, timestamp=None, status='success', exception=None)[source]¶
Create and update WorkCoverageRecords so that every Work in works has an identical record.
- exception¶
- id¶
- operation¶
- status¶
- timestamp¶
- work¶
- work_id¶
core.model.credential module¶
- class core.model.credential.Credential(**kwargs)[source]¶
Bases:
Base
A place to store credentials for external services.
- IDENTIFIER_FROM_REMOTE_SERVICE = 'Identifier Received From Remote Service'¶
- IDENTIFIER_TO_REMOTE_SERVICE = 'Identifier Sent To Remote Service'¶
- collection¶
- collection_id¶
- credential¶
- data_source¶
- data_source_id¶
- drm_device_identifiers¶
- expires¶
- id¶
- classmethod lookup(_db, data_source, token_type, patron, refresher_method, allow_persistent_token=False, allow_empty_token=False, collection=None, force_refresh=False)[source]¶
- classmethod lookup_and_expire_temporary_token(_db, data_source, type, token)[source]¶
Look up a temporary token and expire it immediately.
- classmethod lookup_by_patron(_db, data_source_name, token_type, patron, allow_persistent_token=False, auto_create_datasource=True)[source]¶
Look up a unique token. Lookup will fail on expired tokens. Unless persistent tokens are specifically allowed, lookup will fail on persistent tokens.
- Parameters:
_db (sqlalchemy.orm.session.Session) – Database session
data_source_name (str) – Name of the data source
token_type (str) – Token type
patron (core.model.patron.Patron) – Patron object
allow_persistent_token (bool) – Boolean value indicating whether persistent tokens are allowed or not
auto_create_datasource (bool) – Boolean value indicating whether a data source should be created in the case it doesn’t
- classmethod lookup_by_token(_db, data_source, token_type, token, allow_persistent_token=False)[source]¶
Look up a unique token. Lookup will fail on expired tokens. Unless persistent tokens are specifically allowed, lookup will fail on persistent tokens.
- patron¶
- patron_id¶
- classmethod persistent_token_create(_db, data_source, type, patron, token_string=None)[source]¶
Create or retrieve a persistent token for the given data_source/type/patron.
- classmethod temporary_token_create(_db, data_source, token_type, patron, duration, value=None)[source]¶
Create a temporary token for the given data_source/type/patron. The token will be good for the specified duration.
- type¶
- class core.model.credential.DRMDeviceIdentifier(**kwargs)[source]¶
Bases:
Base
A device identifier for a particular DRM scheme. Associated with a Credential, most commonly a patron’s “Identifier for Adobe account ID purposes” Credential.
- credential¶
- credential_id¶
- device_identifier¶
- id¶
- class core.model.credential.DelegatedPatronIdentifier(**kwargs)[source]¶
Bases:
Base
This library is in charge of coming up with, and storing, identifiers associated with the patrons of some other library. e.g. NYPL provides Adobe IDs for patrons of all libraries that use the SimplyE app. Those identifiers are stored here.
- ADOBE_ACCOUNT_ID = 'Adobe Account ID'¶
- delegated_identifier¶
- classmethod get_one_or_create(_db, library_uri, patron_identifier, identifier_type, create_function)[source]¶
Look up the delegated identifier for the given patron. If there is none, create one.
- Parameters:
library_uri – A URI identifying the patron’s library.
patron_identifier – An identifier used by that library to distinguish between this patron and others. This should be an identifier created solely for the purpose of identifying the patron with _this_ library, and not (e.g.) the patron’s barcode.
identifier_type – The type of the delegated identifier to look up. (probably ADOBE_ACCOUNT_ID)
create_function – If this patron does not have a DelegatedPatronIdentifier, one will be created, and this function will be called to determine the value of DelegatedPatronIdentifier.delegated_identifier.
- Returns:
A 2-tuple (DelegatedPatronIdentifier, is_new)
- id¶
- library_uri¶
- patron_identifier¶
- type¶
core.model.customlist module¶
- class core.model.customlist.CustomList(**kwargs)[source]¶
Bases:
Base
A custom grouping of Editions.
- STAFF_PICKS_NAME = 'Staff Picks'¶
- add_entry(work_or_edition, annotation=None, first_appearance=None, featured=None, update_external_index=True)[source]¶
Add a Work or Edition to a CustomList.
- Parameters:
work_or_edition – A Work or an Edition. If this is a Work, that specific Work will be added to the CustomList. If this is an Edition, that Edition will be added to the CustomList, assuming there’s no equivalent Edition already in the list.
update_external_index – When a Work is added to a list, its external index needs to be updated. The only reason not to do this is when the current database session already contains a new WorkCoverageRecord for this purpose (e.g. because the Work was just created) and creating another one would violate the workcoveragerecords table’s unique constraint. TODO: This is probably no longer be necessary since we no longer update the external index in real time.
- classmethod all_from_data_sources(_db, data_sources)[source]¶
All custom lists from the given data sources.
- collections¶
- created¶
- data_source¶
- data_source_id¶
- description¶
- entries¶
- entries_for_work(work_or_edition)[source]¶
Find all of the entries in the list representing a particular Edition or Work.
- property featured_works¶
- classmethod find(_db, foreign_identifier_or_name, data_source=None, library=None)[source]¶
Finds a foreign list in the database by its foreign_identifier or its name.
- foreign_identifier¶
- id¶
- lane¶
- library¶
- library_id¶
- name¶
- primary_language¶
- remove_entry(work_or_edition)[source]¶
Remove the entry for a particular Work or Edition and/or any of its equivalent Editions.
- responsible_party¶
- size¶
- updated¶
- class core.model.customlist.CustomListEntry(**kwargs)[source]¶
Bases:
Base
- annotation¶
- customlist¶
- edition¶
- edition_id¶
- featured¶
- first_appearance¶
- id¶
- list_id¶
- most_recent_appearance¶
- set_work(metadata=None, metadata_client=None, policy=None)[source]¶
If possible, identify a locally known Work that is the same title as the title identified by this CustomListEntry.
- Parameters:
policy – A PresentationCalculationPolicy, used to determine how far to go when looking for equivalent Identifiers.
- update(_db, equivalent_entries=None)[source]¶
Combines any number of equivalent entries into a single entry and updates the edition being used to represent the Work.
- work¶
- work_id¶
core.model.datasource module¶
- class core.model.datasource.DataSource(**kwargs)[source]¶
Bases:
Base
,HasFullTableCache
,DataSourceConstants
A source for information about books, and possibly the books themselves.
- URI_PREFIX = 'http://librarysimplified.org/terms/sources/'¶
- classifications¶
- coverage_records¶
- credentials¶
- custom_lists¶
- delivery_mechanisms¶
- editions¶
- extra¶
- id¶
- id_equivalencies¶
- integration_client¶
- integration_client_id¶
- license_lanes¶
- license_pools¶
- classmethod license_source_for(_db, identifier)[source]¶
Find the one DataSource that provides licenses for books identified by the given identifier. If there is no such DataSource, or there is more than one, raises an exception.
- classmethod license_sources_for(_db, identifier)[source]¶
A query that locates all DataSources that provide licenses for books identified by the given identifier.
- links¶
- list_lanes¶
- classmethod lookup(_db, name, autocreate=False, offers_licenses=False, primary_identifier_type=None)[source]¶
- measurements¶
- classmethod metadata_sources_for(_db, identifier)[source]¶
Finds the DataSources that provide metadata for books identified by the given identifier.
- name¶
- classmethod name_from_uri(uri)[source]¶
Turn a data source URI into a name suitable for passing into lookup().
- offers_licenses¶
- primary_identifier_type¶
- resources¶
- property uri¶
core.model.edition module¶
- class core.model.edition.Edition(**kwargs)[source]¶
Bases:
Base
,EditionConstants
A lightly schematized collection of metadata for a work, or an edition of a work, or a book, or whatever. If someone thinks of it as a “book” with a “title” it can go in here.
- MAX_FALLBACK_THUMBNAIL_HEIGHT = 500¶
- MAX_THUMBNAIL_HEIGHT = 300¶
- MAX_THUMBNAIL_WIDTH = 200¶
- MEDIUM_ENUM = Enum('Book', 'Periodical', 'Audio', 'Music', 'Video', 'Image', 'Courseware', name='medium')¶
- UNKNOWN_AUTHOR = '[Unknown]'¶
- add_contributor(name, roles, aliases=None, lc=None, viaf=None, **kwargs)[source]¶
Assign a contributor to this Edition.
- apply_similarity_threshold(candidates, threshold=0.5)[source]¶
Yield the Editions from the given list that are similar enough to this one.
- author¶
- property author_contributors¶
All distinct ‘author’-type contributors, with the primary author first, other authors sorted by sort name. Basically, we’re trying to figure out what would go on the book cover. The primary author should go first, and be followed by non-primary authors in alphabetical order. People whose role does not rise to the level of “authorship” (e.g. author of afterword) do not show up. The list as a whole should contain no duplicates. This might happen because someone is erroneously listed twice in the same role, someone is listed as both primary author and regular author, someone is listed as both author and translator, etc. However it happens, your name only shows up once on the front of the book.
- property author_for_permanent_work_id¶
- calculate_author()[source]¶
Turn the list of Contributors into string values for .author and .sort_author.
- calculate_presentation(policy=None)[source]¶
Make sure the presentation of this Edition is up-to-date.
- contributions¶
- property contributors¶
- cover¶
- cover_full_url¶
- cover_id¶
- cover_thumbnail_url¶
- custom_list_entries¶
- data_source¶
- data_source_id¶
- equivalent_editions(policy=None)[source]¶
All Editions whose primary ID is equivalent to this Edition’s primary ID, according to the given PresentationCalculationPolicy.
- equivalent_identifiers(type=None, policy=None)[source]¶
All Identifiers equivalent to this Edition’s primary identifier, according to the given PresentationCalculationPolicy
- extra¶
- classmethod for_foreign_id(_db, data_source, foreign_id_type, foreign_id, create_if_not_exists=True)[source]¶
Find the Edition representing the given data source’s view of the work that it primarily identifies by foreign ID. e.g. for_foreign_id(_db, DataSource.OVERDRIVE, Identifier.OVERDRIVE_ID, uuid) finds the Edition for Overdrive’s view of a book identified by Overdrive UUID. This: for_foreign_id(_db, DataSource.OVERDRIVE, Identifier.ISBN, isbn) will probably return nothing, because although Overdrive knows that books have ISBNs, it doesn’t use ISBN as a primary identifier.
- id¶
- imprint¶
- is_presentation_for¶
- issued¶
- language¶
- property language_code¶
- property license_pools¶
The LicensePools that provide access to the book described by this Edition.
- medium¶
- classmethod medium_from_media_type(media_type)[source]¶
Derive a value for Edition.medium from a media type.
TODO: It’s not necessary right now, but we could theoretically derive this information from some other types such as our internal types for Overdrive manifests.
- Parameters:
media_type – A media type with optional parameters
- Returns:
A value for Edition.medium.
- classmethod missing_coverage_from(_db, edition_data_sources, coverage_data_source, operation=None)[source]¶
Find Editions from edition_data_source whose primary identifiers have no CoverageRecord from coverage_data_source. e.g.:: gutenberg = DataSource.lookup(_db, DataSource.GUTENBERG) oclc_classify = DataSource.lookup(_db, DataSource.OCLC) missing_coverage_from(_db, gutenberg, oclc_classify)
will find Editions that came from Project Gutenberg and have never been used as input to the OCLC Classify web service.
- permanent_work_id¶
- primary_identifier¶
- primary_identifier_id¶
- published¶
- publisher¶
- series¶
- series_position¶
- similarity_to(other_record)[source]¶
How likely is it that this record describes the same book as the given record? 1 indicates very strong similarity, 0 indicates no similarity at all. For now we just compare the sets of words used in the titles and the authors’ names. This should be good enough for most cases given that there is usually some preexisting reason to suppose that the two records are related (e.g. OCLC said they were). Most of the Editions are from OCLC Classify, and we expect to get some of them wrong (e.g. when a single OCLC work is a compilation of several novels by the same author). That’s okay because those Editions aren’t backed by LicensePools. They’re purely informative. We will have some bad information in our database, but the clear-cut cases should outnumber the fuzzy cases, so we we should still group the Editions that really matter–the ones backed by LicensePools–together correctly. TODO: apply much more lenient terms if the two Editions are identified by the same ISBN or other unique identifier.
- simple_opds_entry¶
- sort_author¶
- classmethod sort_by_priority(editions, license_source=None)[source]¶
Return all Editions that describe the Identifier associated with this LicensePool, in the order they should be used to create a presentation Edition for the LicensePool.
- sort_title¶
- subtitle¶
- title¶
- property title_for_permanent_work_id¶
- work¶
core.model.hasfulltablecache module¶
- class core.model.hasfulltablecache.HasFullTableCache[source]¶
Bases:
object
A mixin class for ORM classes that maintain an in-memory cache of (hopefully) every item in the database table for performance reasons.
- RESET = <object object>¶
core.model.identifier module¶
- class core.model.identifier.Equivalency(**kwargs)[source]¶
Bases:
Base
An assertion that two Identifiers identify the same work. This assertion comes with a ‘strength’ which represents how confident the data source is in the assertion.
- data_source¶
- data_source_id¶
- enabled¶
- classmethod for_identifiers(_db, identifiers, exclude_ids=None)[source]¶
Find all Equivalencies for the given Identifiers.
- id¶
- input¶
- input_id¶
- input_identifiers¶
- output¶
- output_id¶
- output_identifiers¶
- strength¶
- votes¶
- class core.model.identifier.Identifier(**kwargs)[source]¶
Bases:
Base
,IdentifierConstants
A way of uniquely referring to a particular edition.
- add_link(rel, href, data_source, media_type=None, content=None, content_path=None, rights_status_uri=None, rights_explanation=None, original_resource=None, transformation_settings=None)[source]¶
Create a link between this Identifier and a (potentially new) Resource. TODO: There’s some code in metadata_layer for automatically fetching, mirroring and scaling Representations as links are created. It might be good to move that code into here.
- add_measurement(data_source, quantity_measured, value, weight=1, taken_at=None)[source]¶
Associate a new Measurement with this Identifier.
- annotations¶
- classifications¶
- classify(data_source, subject_type, subject_identifier, subject_name=None, weight=1)[source]¶
Classify this Identifier under a Subject.
- Parameters:
type – Classification scheme; one of the constants from Subject.
subject_identifier – Internal ID of the subject according to that classification scheme.
value – Human-readable description of the subject, if different from the ID.
weight – How confident the data source is in classifying a book under this subject. The meaning of this number depends entirely on the source of the information.
- collections¶
- coverage_records¶
- delivery_mechanisms¶
- equivalencies¶
- equivalent_to(data_source, identifier, strength)[source]¶
Make one Identifier equivalent to another. data_source is the DataSource that believes the two identifiers are equivalent.
- classmethod evaluate_summary_quality(_db, identifier_ids, privileged_data_sources=None)[source]¶
Evaluate the summaries for the given group of Identifier IDs. This is an automatic evaluation based solely on the content of the summaries. It will be combined with human-entered ratings to form an overall quality score. We need to evaluate summaries from a set of Identifiers (typically those associated with a single work) because we need to see which noun phrases are most frequently used to describe the underlying work. :param privileged_data_sources: If present, a summary from one of these data source will be instantly chosen, short-circuiting the decision process. Data sources are in order of priority. :return: The single highest-rated summary Resource.
- classmethod for_foreign_id(_db, foreign_identifier_type, foreign_id, autocreate=True)[source]¶
Turn a foreign ID into an Identifier.
- classmethod from_asin(_db, asin, autocreate=True)[source]¶
Turn an ASIN-like string into an Identifier. If the string is an ISBN10 or ISBN13, the Identifier will be of type ISBN and the value will be the equivalent ISBN13. Otherwise the Identifier will be of type ASIN and the value will be the value of asin.
- id¶
- identifier¶
- inbound_equivalencies¶
- licensed_through¶
- licensed_through_collection(collection)[source]¶
Find the LicensePool, if any, for this Identifier in the given Collection. :return: At most one LicensePool.
- links¶
- measurements¶
- classmethod missing_coverage_from(_db, identifier_types, coverage_data_source, operation=None, count_as_covered=None, count_as_missing_before=None, identifiers=None, collection=None)[source]¶
Find identifiers of the given types which have no CoverageRecord from coverage_data_source. :param count_as_covered: Identifiers will be counted as covered if their CoverageRecords have a status in this list. :param identifiers: Restrict search to a specific set of identifier objects.
- opds_entry()[source]¶
Create an OPDS entry using only resources directly associated with this Identifier. This makes it possible to create an OPDS entry even when there is no Edition. Currently the only things in this OPDS entry will be description, cover image, and popularity. NOTE: The timestamp doesn’t take into consideration when the description was added. Rather than fixing this it’s probably better to get rid of this hack and create real Works where we would be using this method.
- classmethod parse(_db, identifier_string, parser, must_support_license_pools=False)[source]¶
Parse identifier string.
- Parameters:
_db (sqlalchemy.orm.session.Session) – Database session
identifier_string (str) – String containing an identifier
parser (IdentifierParser) – Identifier parser
must_support_license_pools (bool) – Boolean value indicating whether there should be a DataSource that provides licenses for books identified by the given identifier
- Returns:
2-tuple containing Identifier object and a boolean value indicating whether it’s new
- Return type:
Tuple[core.model.identifier.Identifier, bool]
- classmethod parse_urn(_db, identifier_string, must_support_license_pools=False)[source]¶
Parse identifier string.
- Parameters:
_db (sqlalchemy.orm.session.Session) – Database session
identifier_string (str) – String containing an identifier
must_support_license_pools (bool) – Boolean value indicating whether there should be a DataSource that provides licenses for books identified by the given identifier
- Returns:
2-tuple containing Identifier object and a boolean value indicating whether it’s new
- Return type:
Tuple[core.model.identifier.Identifier, bool]
- classmethod parse_urns(_db, identifier_strings, autocreate=True, allowed_types=None)[source]¶
Converts a batch of URNs into Identifier objects.
- Parameters:
_db – A database connection
identifier_strings – A list of strings, each a URN identifying some identifier.
autocreate – Create an Identifier for a URN if none presently exists.
allowed_types – If this is a list of Identifier types, only identifiers of those types may be looked up. All other identifier types will be treated as though they did not exist.
- Returns:
A 2-tuple (identifiers, failures). identifiers is a list of Identifiers. failures is a list of URNs that did not become Identifiers.
- primarily_identifies¶
- classmethod recursively_equivalent_identifier_ids(_db, identifier_ids, policy=None)[source]¶
All Identifier IDs equivalent to the given set of Identifier IDs at the given confidence threshold. This uses the function defined in files/recursive_equivalents.sql. Four levels is enough to go from a Gutenberg text to an ISBN. Gutenberg ID -> OCLC Work IS -> OCLC Number -> ISBN Returns a dictionary mapping each ID in the original to a list of equivalent IDs.
- Parameters:
policy – A PresentationCalculationPolicy that explains how you’ve chosen to make the tradeoff between performance, data quality, and sheer number of equivalent identifiers.
- classmethod recursively_equivalent_identifier_ids_query(identifier_id_column, policy=None)[source]¶
Get a SQL statement that will return all Identifier IDs equivalent to a given ID at the given confidence threshold. identifier_id_column can be a single Identifier ID, or a column like Edition.primary_identifier_id if the query will be used as a subquery. This uses the function defined in files/recursive_equivalents.sql.
- type¶
- property urn¶
- classmethod valid_as_foreign_identifier(type, id)[source]¶
Return True if the given id can be an Identifier of the given type. This is not a complete implementation; we will add to it as necessary. In general we err on the side of allowing IDs that look invalid (e.g. all Overdrive IDs look like UUIDs, but we currently don’t enforce that). We only reject an ID out of hand if it will cause problems with a third-party API.
- property work¶
Find the Work, if any, associated with this Identifier. Although one Identifier may be associated with multiple LicensePools, all of them must share a Work.
- class core.model.identifier.IdentifierParser[source]¶
Bases:
object
Interface for identifier parsers.
- abstract parse(identifier_string)[source]¶
Parse a string containing an identifier, extract it and determine its type.
- Parameters:
identifier_string (str) – String containing an identifier
- Returns:
2-tuple containing the identifier’s type and identifier itself or None if the string contains an incorrect identifier
- Return type:
Optional[Tuple[str, str]]
core.model.integrationclient module¶
- class core.model.integrationclient.IntegrationClient(**kwargs)[source]¶
Bases:
Base
A client that has authenticated access to this application.
Currently used to represent circulation managers that have access to the metadata wrangler.
- created¶
- data_source¶
- enabled¶
- classmethod for_url(_db, url)[source]¶
Finds the IntegrationClient for the given server URL.
- Returns:
an IntegrationClient. If it didn’t already exist, it will be created. If it didn’t already have a secret, no secret will be set.
- holds¶
- id¶
- last_accessed¶
- loans¶
- classmethod register(_db, url, submitted_secret=None)[source]¶
Creates a new server with client details.
- url¶
core.model.library module¶
- class core.model.library.Library(**kwargs)[source]¶
Bases:
Base
,HasFullTableCache
A library that uses this circulation manager to authenticate its patrons and manage access to its content. A circulation manager may serve many libraries.
- ALLOW_HOLDS = 'allow_holds'¶
- DEFAULT_FACET_KEY_PREFIX = 'facets_default_'¶
- ENABLED_FACETS_KEY_PREFIX = 'facets_enabled_'¶
- EXTERNAL_TYPE_REGULAR_EXPRESSION = 'external_type_regular_expression'¶
- FEATURED_LANE_SIZE = 'featured_lane_size'¶
- MINIMUM_FEATURED_QUALITY = 'minimum_featured_quality'¶
- adminroles¶
- property all_collections¶
- property allow_holds¶
Does this library allow patrons to put items on hold?
- cachedfeeds¶
- cachedmarcfiles¶
- circulation_events¶
- collections¶
- custom_lists¶
- property entrypoints¶
The EntryPoints enabled for this library.
- estimated_holdings_by_language(include_open_access=True)[source]¶
Estimate how many titles this library has in various languages. The estimate is pretty good but should not be relied upon as exact. :return: A Counter mapping languages to the estimated number of titles in that language.
- explain(include_secrets=False)[source]¶
Create a series of human-readable strings to explain a library’s settings.
- Parameters:
include_secrets – For security reasons, secrets are not displayed by default.
- Returns:
A list of explanatory strings.
- property featured_lane_size¶
The minimum quality a book must have to be ‘featured’.
- property has_root_lanes¶
Does this library have any lanes that act as the root lane for a certain patron type?
- Returns:
A boolean
- id¶
- integrations¶
- property is_default¶
- lanes¶
- library_registry_short_name¶
Gets library_registry_short_name from database
- property minimum_featured_quality¶
The minimum quality a book must have to be ‘featured’.
- name¶
- patrons¶
- restrict_to_ready_deliverable_works(query, collection_ids=None, show_suppressed=False)[source]¶
Restrict a query to show only presentation-ready works present in an appropriate collection which the default client can fulfill. Note that this assumes the query has an active join against LicensePool. :param query: The query to restrict. :param collection_ids: Only include titles in the given collections. :param show_suppressed: Include titles that have nothing but suppressed LicensePools.
- setting(key)[source]¶
Find or create a ConfigurationSetting on this Library. :param key: Name of the setting. :return: A ConfigurationSetting
- settings¶
- short_name¶
- uuid¶
core.model.licensing module¶
- class core.model.licensing.DeliveryMechanism(**kwargs)[source]¶
Bases:
Base
,HasFullTableCache
A technique for delivering a book to a patron. There are two parts to this: a DRM scheme and a content type. Either may be identified with a MIME media type (e.g. “application/vnd.adobe.adept+xml” or “application/epub+zip”) or an informal name (“Kindle via Amazon”).
- ADOBE_DRM = 'application/vnd.adobe.adept+xml'¶
- AXISNOW_DRM = 'application/vnd.librarysimplified.axisnow+json'¶
- BEARER_TOKEN = 'application/vnd.librarysimplified.bearer-token+json'¶
- FEEDBOOKS_AUDIOBOOK_DRM = 'http://www.feedbooks.com/audiobooks/access-restriction'¶
- FEEDBOOKS_AUDIOBOOK_PROFILE = ';profile="http://www.feedbooks.com/audiobooks/access-restriction"'¶
- FINDAWAY_DRM = 'application/vnd.librarysimplified.findaway.license+json'¶
- KINDLE_CONTENT_TYPE = 'Kindle via Amazon'¶
- KINDLE_DRM = 'Kindle DRM'¶
- KNOWN_DRM_TYPES = {'Kindle DRM', 'Libby DRM', 'Nook DRM', 'Overdrive DRM', 'Streaming', 'application/vnd.adobe.adept+xml', 'application/vnd.librarysimplified.axisnow+json', 'application/vnd.librarysimplified.findaway.license+json', 'application/vnd.readium.lcp.license.v1.0+json'}¶
- LCP_DRM = 'application/vnd.readium.lcp.license.v1.0+json'¶
- LIBBY_DRM = 'Libby DRM'¶
- MEDIA_TYPES_FOR_STREAMING = {'Streaming Audio': 'text/html', 'Streaming Text': 'text/html'}¶
- NOOK_CONTENT_TYPE = 'Nook via B&N'¶
- NOOK_DRM = 'Nook DRM'¶
- NO_DRM = None¶
- OVERDRIVE_DRM = 'Overdrive DRM'¶
- STREAMING_AUDIO_CONTENT_TYPE = 'Streaming Audio'¶
- STREAMING_DRM = 'Streaming'¶
- STREAMING_PROFILE = ';profile="http://librarysimplified.org/terms/profiles/streaming-media"'¶
- STREAMING_TEXT_CONTENT_TYPE = 'Streaming Text'¶
- STREAMING_VIDEO_CONTENT_TYPE = 'Streaming Video'¶
- compatible_with(other, open_access_rules=False)[source]¶
Can a single loan be fulfilled with both this delivery mechanism and the given one?
- Parameters:
other – A DeliveryMechanism
open_access – If this is True, the rules for open-access fulfillment will be applied. If not, the stricted rules for commercial fulfillment will be applied.
- content_type¶
- property content_type_media_type¶
Return the media type for this delivery mechanism’s content type, assuming it’s represented as a media type.
- default_client_can_fulfill¶
- default_client_can_fulfill_lookup = {('application/pdf', None), ('application/vnd.overdrive.circulation.api+json;profile=audiobook', 'Libby DRM'), (None, 'application/vnd.librarysimplified.findaway.license+json'), ('application/epub+zip', 'application/vnd.librarysimplified.bearer-token+json'), ('application/pdf', 'application/vnd.librarysimplified.bearer-token+json'), ('application/epub+zip', None), ('application/epub+zip', 'application/vnd.adobe.adept+xml'), ('application/audiobook+json', 'application/vnd.librarysimplified.bearer-token+json'), ('application/audiobook+json', None)}¶
- drm_scheme¶
- property drm_scheme_media_type¶
Return the media type for this delivery mechanism’s DRM scheme, assuming it’s represented that way.
- id¶
- property implicit_medium¶
What would be a good setting for EditionConstants.MEDIUM for an edition available through this DeliveryMechanism?
- property is_streaming¶
- license_pool_delivery_mechanisms¶
- property name¶
- class core.model.licensing.License(**kwargs)[source]¶
Bases:
Base
A single license for a work from a given source.
TODO: This currently assumes all licenses for a pool have the same delivery mechanisms, which may not always be true.
- checkout_url¶
- concurrent_checkouts¶
- expires¶
- id¶
- identifier¶
- property is_expired¶
- property is_loan_limited¶
- property is_perpetual¶
- property is_time_limited¶
- license_pool¶
- license_pool_id¶
- loans¶
- remaining_checkouts¶
- status_url¶
- class core.model.licensing.LicensePool(**kwargs)[source]¶
Bases:
Base
A pool of undifferentiated licenses for a work from a given source.
- UNLIMITED_ACCESS = -1¶
- add_link(rel, href, data_source, media_type=None, content=None, content_path=None, rights_status_uri=None, rights_explanation=None, original_resource=None, transformation_settings=None)[source]¶
Add a link between this LicensePool and a Resource.
- Parameters:
rel – The relationship between this LicensePool and the resource on the other end of the link.
href – The URI of the resource on the other end of the link.
media_type – Media type of the representation associated with the resource.
content – Content of the representation associated with the resource.
content_path – Path (relative to DATA_DIRECTORY) of the representation associated with the resource.
rights_status_uri – The URI of the RightsStatus for this resource.
rights_explanation – A free text explanation of why the RightsStatus applies.
original_resource – Another resource that this resource was derived from.
transformation_settings – The settings used to transform the original resource into this resource.
- availability_time¶
- best_available_license()[source]¶
Determine the next license that should be lent out for this pool.
Time-limited licenses and perpetual licenses are the best. It doesn’t matter which is used first, unless a time-limited license would expire within the loan period, in which case it’s better to loan the time-limited license so the perpetual one is still available. We can handle this by always loaning the time-limited one first, followed by perpetual. If there is more than one time-limited license, it’s better to use the one expiring soonest.
If no time-limited or perpetual licenses are available, the next best is a loan-limited license. We should choose the license with the most remaining loans, so that we’ll maximize the number of concurrent checkouts available in the future.
The worst option would be pay-per-use, but we don’t yet support any distributors that offer that model.
- property best_license_link¶
Find the best available licensing link for the work associated with this LicensePool. # TODO: This needs work and may not be necessary anymore.
- property best_open_access_link¶
Find the best open-access link for this LicensePool. Cache it so that the next access will be faster.
- property best_open_access_resource¶
Determine the best open-access Resource currently provided by this LicensePool.
- better_open_access_pool_than(champion)[source]¶
Is this open-access pool generally known for better-quality download files than the passed-in pool?
- calculate_work(known_edition=None, exclude_search=False, even_if_no_title=False)[source]¶
Find or create a Work for this LicensePool. A pool that is not open-access will always have its own Work. Open-access LicensePools will be grouped together with other open-access LicensePools based on the permanent work ID of the LicensePool’s presentation edition. :param even_if_no_title: Ordinarily this method will refuse to create a Work for a LicensePool whose Edition has no title. However, in components that don’t present information directly to readers, it’s sometimes useful to create a Work even if the title is unknown. In that case, pass in even_if_no_title=True and the Work will be created. TODO: I think known_edition is mostly useless. We should either remove it or replace it with a boolean that stops us from calling set_presentation_edition() and assumes we’ve already done that work.
- circulation_changelog(old_licenses_owned, old_licenses_available, old_licenses_reserved, old_patrons_in_hold_queue)[source]¶
Generate a log message describing a change to the circulation. :return: a 2-tuple (message, args) suitable for passing into logging.info or a similar method
- circulation_events¶
- collection¶
- collection_id¶
- complaints¶
- classmethod consolidate_works(_db, batch_size=10)[source]¶
Assign a (possibly new) Work to every unassigned LicensePool.
- data_source¶
- data_source_id¶
- property deliverable¶
This LicensePool can actually be delivered to patrons.
- delivery_mechanisms¶
- classmethod for_foreign_id(_db, data_source, foreign_id_type, foreign_id, rights_status=None, collection=None, autocreate=True)[source]¶
Find or create a LicensePool for the given foreign ID.
- holds¶
- id¶
- identifier¶
- identifier_id¶
- last_checked¶
- license_exception¶
- licenses¶
- licenses_available¶
- licenses_owned¶
- licenses_reserved¶
- loan_to(patron_or_client, start=None, end=None, fulfillment=None, external_identifier=None)[source]¶
- loans¶
- on_hold_to(patron_or_client, start=None, end=None, position=None, external_identifier=None)[source]¶
- open_access¶
- property open_access_download_url¶
Alias for best_open_access_link. If _open_access_download_url is currently None, this will set to a good value if possible.
- property open_access_links¶
Yield all open-access Resources for this LicensePool.
- property open_access_source_priority¶
What priority does this LicensePool’s DataSource have in our list of open-access content sources? e.g. GITenberg books are prefered over Gutenberg books, because there’s a defined process for fixing errors and they are more likely to have good cover art.
- patrons_in_hold_queue¶
- presentation_edition¶
- presentation_edition_id¶
- self_hosted¶
- set_delivery_mechanism(*args, **kwargs)[source]¶
Ensure that this LicensePool (and any other LicensePools for the same book) have a LicensePoolDeliveryMechanism for this media type, DRM scheme, rights status, and resource.
- set_open_access_status()[source]¶
Set .open_access based on whether there is currently an open-access LicensePoolDeliveryMechanism for this LicensePool.
- set_presentation_edition(equivalent_editions=None)[source]¶
Create or update the presentation Edition for this LicensePool. The presentation Edition is made of metadata from all Editions associated with the LicensePool’s identifier. :param equivalent_editions: An optional list of Edition objects that don’t share this LicensePool’s identifier but are associated with its equivalent identifiers in some way. This option is used to create Works on the Metadata Wrangler. :return: A boolean explaining whether any of the presentation information associated with this LicensePool actually changed.
- superceded¶
- suppressed¶
- unlimited_access¶
Returns a Boolean value indicating whether this LicensePool allows unlimited access. For example, in the case of LCP books without explicit licensing information
- Returns:
Boolean value indicating whether this LicensePool allows unlimited access
- Return type:
bool
- update_availability(new_licenses_owned, new_licenses_available, new_licenses_reserved, new_patrons_in_hold_queue, analytics=None, as_of=None)[source]¶
Update the LicensePool with new availability information. Log the implied changes with the analytics provider.
- update_availability_from_delta(event_type, event_date, delta, analytics=None)[source]¶
Call update_availability based on a single change seen in the distributor data, rather than a complete snapshot of distributor information as of a certain time. This information is unlikely to be completely accurate, but it should suffice until more accurate information can be obtained. No CirculationEvent is created until update_availability is called. Events must be processed in chronological order. Any event that happened than LicensePool.last_checked is ignored, and calling this method will update LicensePool.last_checked to the time of the event. :param event_type: A CirculationEvent constant representing the type of change that was seen. :param event_date: A datetime corresponding to when the change was seen. :param delta: The magnitude of the change that was seen.
- classmethod with_complaint(library, resolved=False)[source]¶
Return query for LicensePools that have at least one Complaint.
- classmethod with_no_delivery_mechanisms(_db)[source]¶
Find LicensePools that have no delivery mechanisms.
- Returns:
A query object.
- work¶
- work_id¶
- class core.model.licensing.LicensePoolDeliveryMechanism(**kwargs)[source]¶
Bases:
Base
A mechanism for delivering a specific book from a specific distributor. It’s presumed that all LicensePools for a given DataSource and Identifier have the same set of LicensePoolDeliveryMechanisms. This is mostly an association class between DataSource, Identifier and DeliveryMechanism, but it also may incorporate a specific Resource (i.e. a static link to a downloadable file) which explains exactly where to go for delivery.
- compatible_with(other)[source]¶
Can a single loan be fulfilled with both this LicensePoolDeliveryMechanism and the given one?
- Parameters:
other – A LicensePoolDeliveryMechanism.
- data_source¶
- data_source_id¶
- delivery_mechanism¶
- delivery_mechanism_id¶
- fulfills¶
- id¶
- identifier¶
- identifier_id¶
- property is_open_access¶
Is this an open-access delivery mechanism?
- property license_pools¶
Find all LicensePools for this LicensePoolDeliveryMechanism.
- resource¶
- resource_id¶
- rights_status¶
- rightsstatus_id¶
- classmethod set(data_source, identifier, content_type, drm_scheme, rights_uri, resource=None, autocommit=True)[source]¶
Register the fact that a distributor makes a title available in a certain format.
- Parameters:
data_source – A DataSource identifying the distributor.
identifier – An Identifier identifying the title.
content_type – The title is available in this media type.
drm_scheme – Access to the title is confounded by this DRM scheme.
rights_uri – A URI representing the public’s rights to the title.
resource – A Resource representing the book itself in a freely redistributable form.
autocommit – Commit the database session immediately if anything changes in the database. If you’re already inside a nested transaction, pass in False here to avoid committing prematurely, but understand that if a LicensePool’s open-access status changes as a result of calling this method, the change may not be properly reflected in LicensePool.open_access.
- class core.model.licensing.RightsStatus(**kwargs)[source]¶
Bases:
Base
The terms under which a book has been made available to the general public. This will normally be ‘in copyright’, or ‘public domain’, or a Creative Commons license.
- ALLOWS_DERIVATIVES = ['http://librarysimplified.org/terms/rights-status/public-domain-usa', 'https://creativecommons.org/publicdomain/zero/1.0/', 'http://creativecommons.org/licenses/by/4.0/', 'https://creativecommons.org/licenses/by-sa/4.0', 'https://creativecommons.org/licenses/by-nc/4.0', 'https://creativecommons.org/licenses/by-nc-sa/4.0']¶
- CC0 = 'https://creativecommons.org/publicdomain/zero/1.0/'¶
- CC_BY = 'http://creativecommons.org/licenses/by/4.0/'¶
- CC_BY_NC = 'https://creativecommons.org/licenses/by-nc/4.0'¶
- CC_BY_NC_ND = 'https://creativecommons.org/licenses/by-nc-nd/4.0'¶
- CC_BY_NC_SA = 'https://creativecommons.org/licenses/by-nc-sa/4.0'¶
- CC_BY_ND = 'https://creativecommons.org/licenses/by-nd/4.0'¶
- CC_BY_SA = 'https://creativecommons.org/licenses/by-sa/4.0'¶
- DATA_SOURCE_DEFAULT_RIGHTS_STATUS = {'Axis 360': 'http://librarysimplified.org/terms/rights-status/in-copyright', 'Bibliotheca': 'http://librarysimplified.org/terms/rights-status/in-copyright', 'Gutenberg': 'http://librarysimplified.org/terms/rights-status/public-domain-usa', 'Library Simplified Open Access Content Server': 'http://librarysimplified.org/terms/rights-status/generic-open-access', 'Overdrive': 'http://librarysimplified.org/terms/rights-status/in-copyright', 'Plympton': 'https://creativecommons.org/licenses/by-nc/4.0'}¶
- GENERIC_OPEN_ACCESS = 'http://librarysimplified.org/terms/rights-status/generic-open-access'¶
- IN_COPYRIGHT = 'http://librarysimplified.org/terms/rights-status/in-copyright'¶
- NAMES = {'http://creativecommons.org/licenses/by/4.0/': 'Creative Commons Attribution (CC BY)', 'http://librarysimplified.org/terms/rights-status/generic-open-access': 'Open access with no specific license', 'http://librarysimplified.org/terms/rights-status/in-copyright': 'In Copyright', 'http://librarysimplified.org/terms/rights-status/public-domain-usa': 'Public domain in the USA', 'http://librarysimplified.org/terms/rights-status/unknown': 'Unknown', 'https://creativecommons.org/licenses/by-nc-nd/4.0': 'Creative Commons Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)', 'https://creativecommons.org/licenses/by-nc-sa/4.0': 'Creative Commons Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)', 'https://creativecommons.org/licenses/by-nc/4.0': 'Creative Commons Attribution-NonCommercial (CC BY-NC)', 'https://creativecommons.org/licenses/by-nd/4.0': 'Creative Commons Attribution-NoDerivs (CC BY-ND)', 'https://creativecommons.org/licenses/by-sa/4.0': 'Creative Commons Attribution-ShareAlike (CC BY-SA)', 'https://creativecommons.org/publicdomain/zero/1.0/': 'Creative Commons Public Domain Dedication (CC0)'}¶
- OPEN_ACCESS = ['http://librarysimplified.org/terms/rights-status/public-domain-usa', 'https://creativecommons.org/publicdomain/zero/1.0/', 'http://creativecommons.org/licenses/by/4.0/', 'https://creativecommons.org/licenses/by-sa/4.0', 'https://creativecommons.org/licenses/by-nd/4.0', 'https://creativecommons.org/licenses/by-nc/4.0', 'https://creativecommons.org/licenses/by-nc-sa/4.0', 'https://creativecommons.org/licenses/by-nc-nd/4.0', 'http://librarysimplified.org/terms/rights-status/generic-open-access']¶
- PUBLIC_DOMAIN_UNKNOWN = 'http://librarysimplified.org/terms/rights-status/public-domain-unknown'¶
- PUBLIC_DOMAIN_USA = 'http://librarysimplified.org/terms/rights-status/public-domain-usa'¶
- UNKNOWN = 'http://librarysimplified.org/terms/rights-status/unknown'¶
- id¶
- licensepooldeliverymechanisms¶
- name¶
- resources¶
- uri¶
core.model.listeners module¶
- core.model.listeners.add_work_to_customlists_for_collection(pool_or_work, value, oldvalue, initiator)[source]¶
- core.model.listeners.directly_modified(obj)[source]¶
Return True only if obj has itself been modified, as opposed to having an object added or removed to one of its associated collections.
- core.model.listeners.last_update_time_change(target, value, oldvalue, initator)[source]¶
A Work needs to have its search document re-indexed whenever its last_update_time changes.
Among other things, this happens whenever the LicensePool’s availability information changes.
- core.model.listeners.licensepool_collection_change(target, value, oldvalue, initiator)[source]¶
A LicensePool should never change collections, but if it is, we need to keep the search index up to date.
- core.model.listeners.licensepool_deleted(mapper, connection, target)[source]¶
A LicensePool is deleted only when its collection is deleted. If this happens, we need to keep the Work’s index up to date.
- core.model.listeners.licensepool_removed_from_work(target, value, initiator)[source]¶
When a Work gains or loses a LicensePool, it needs to be reindexed.
- core.model.listeners.licensepool_storage_status_change(target, value, oldvalue, initiator)[source]¶
A Work may need to have its search document re-indexed if one of its LicensePools changes its open-access status.
This shouldn’t ever happen.
- core.model.listeners.site_configuration_has_changed(_db, cooldown=1)[source]¶
Call this whenever you want to indicate that the site configuration has changed and needs to be reloaded.
This is automatically triggered on relevant changes to the data model, but you also should call it whenever you change an aspect of what you consider “site configuration”, just to be safe.
- Parameters:
_db – Either a Session or (to save time in a common case) an ORM object that can turned into a Session.
cooldown – Nothing will happen if it’s been fewer than this number of seconds since the last site configuration change was recorded.
core.model.measurement module¶
- class core.model.measurement.Measurement(**kwargs)[source]¶
Bases:
Base
A measurement of some numeric quantity associated with a Identifier.
- AWARDS = 'http://librarysimplified.org/terms/rel/awards'¶
- DOWNLOADS = 'https://schema.org/UserDownloads'¶
- GUTENBERG_FAVORITE = 'http://librarysimplified.org/terms/rel/lists/gutenberg-favorite'¶
- HOLDINGS = 'http://librarysimplified.org/terms/rel/holdings'¶
- PAGE_COUNT = 'https://schema.org/numberOfPages'¶
- PERCENTILE_SCALES = {'http://librarysimplified.org/terms/rel/editions': {'OCLC Classify': [1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 14, 14, 14, 15, 15, 16, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25, 26, 28, 30, 32, 34, 36, 39, 42, 46, 50, 56, 64, 73, 87, 112, 156, 281, 2812]}, 'http://librarysimplified.org/terms/rel/holdings': {'OCLC Classify': [1, 8, 12, 16, 20, 24, 28, 33, 37, 43, 49, 55, 62, 70, 78, 86, 94, 102, 110, 118, 126, 134, 143, 151, 160, 170, 178, 187, 196, 205, 214, 225, 233, 243, 253, 263, 275, 286, 298, 310, 321, 333, 345, 358, 370, 385, 398, 413, 427, 443, 458, 475, 492, 511, 530, 549, 567, 586, 606, 627, 647, 669, 693, 718, 741, 766, 794, 824, 852, 882, 914, 947, 980, 1018, 1056, 1098, 1142, 1188, 1235, 1288, 1347, 1410, 1477, 1545, 1625, 1714, 1812, 1923, 2039, 2164, 2304, 2479, 2671, 2925, 3220, 3565, 3949, 4476, 5230, 7125, 34811]}, 'http://librarysimplified.org/terms/rel/popularity': {'Amazon': [14937330, 1974074, 1702163, 1553600, 1432635, 1327323, 1251089, 1184878, 1131998, 1075720, 1024272, 978514, 937726, 898606, 868506, 837523, 799879, 770211, 743194, 718052, 693932, 668030, 647121, 627642, 609399, 591843, 575970, 559942, 540713, 524397, 511183, 497576, 483884, 470850, 458438, 444475, 432528, 420088, 408785, 398420, 387895, 377244, 366837, 355406, 344288, 333747, 324280, 315002, 305918, 296420, 288522, 279185, 270824, 262801, 253865, 246224, 238239, 230537, 222611, 215989, 208641, 202597, 195817, 188939, 181095, 173967, 166058, 160032, 153526, 146706, 139981, 133348, 126689, 119201, 112447, 106795, 101250, 96534, 91052, 85837, 80619, 75292, 69957, 65075, 59901, 55616, 51624, 47598, 43645, 39403, 35645, 31795, 27990, 24496, 20780, 17740, 14102, 10498, 7090, 3861], 'Content Cafe': [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, 9, 10, 11, 14, 18, 25, 41, 125, 387], 'Overdrive': [1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 15, 16, 18, 19, 20, 21, 22, 24, 25, 26, 28, 30, 31, 33, 35, 37, 39, 41, 43, 46, 48, 51, 53, 56, 59, 63, 66, 70, 74, 78, 82, 87, 92, 97, 102, 108, 115, 121, 128, 135, 142, 150, 159, 168, 179, 190, 202, 216, 230, 245, 260, 277, 297, 319, 346, 372, 402, 436, 478, 521, 575, 632, 702, 777, 861, 965, 1100, 1248, 1428, 1665, 2020, 2560, 3535, 5805]}, 'https://schema.org/UserDownloads': {'Gutenberg': [0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12, 12, 12, 13, 14, 14, 15, 15, 16, 16, 17, 18, 18, 19, 19, 20, 21, 21, 22, 23, 23, 24, 25, 26, 27, 28, 28, 29, 30, 32, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 46, 48, 50, 52, 55, 57, 60, 62, 65, 69, 72, 76, 79, 83, 87, 93, 99, 106, 114, 122, 130, 140, 152, 163, 179, 197, 220, 251, 281, 317, 367, 432, 501, 597, 658, 718, 801, 939, 1065, 1286, 1668, 2291, 4139]}}¶
- POPULARITY = 'http://librarysimplified.org/terms/rel/popularity'¶
- PUBLISHED_EDITIONS = 'http://librarysimplified.org/terms/rel/editions'¶
- QUALITY = 'http://librarysimplified.org/terms/rel/quality'¶
- RATING = 'http://schema.org/ratingValue'¶
- RATING_SCALES = {'Amazon': [1, 5], 'Library staff': [1, 5], 'NoveList Select': [0, 5], 'Overdrive': [1, 5], 'unglue.it': [1, 5]}¶
- data_source¶
- data_source_id¶
- id¶
- identifier¶
- identifier_id¶
- is_most_recent¶
- property normalized_value¶
Normalize a measured value, possibly using the rating scales in RATING_SCALES or the empirically determined percentile scales in PERCENTILE_SCALES.
- classmethod overall_quality(measurements, popularity_weight=0.3, rating_weight=0.7, default_value=0)[source]¶
Turn a bunch of measurements into an overall measure of quality.
- quantity_measured¶
- taken_at¶
- value¶
- weight¶
core.model.patron module¶
- class core.model.patron.Annotation(**kwargs)[source]¶
Bases:
Base
- BOOKMARKING = 'http://www.w3.org/ns/oa#bookmarking'¶
- IDLING = 'http://librarysimplified.org/terms/annotation/idling'¶
- LS_NAMESPACE = 'http://librarysimplified.org/terms/annotation/'¶
- MOTIVATIONS = ['http://librarysimplified.org/terms/annotation/idling', 'http://www.w3.org/ns/oa#bookmarking']¶
- OA_NAMESPACE = 'http://www.w3.org/ns/oa#'¶
- active¶
- content¶
- classmethod get_one_or_create(_db, patron, *args, **kwargs)[source]¶
Find or create an Annotation, but only if the patron has annotation sync turned on.
- id¶
- identifier¶
- identifier_id¶
- motivation¶
- patron¶
- patron_id¶
- target¶
- timestamp¶
- class core.model.patron.Hold(**kwargs)[source]¶
Bases:
Base
,LoanAndHoldMixin
A patron is in line to check out a book.
- end¶
- external_identifier¶
- id¶
- integration_client¶
- integration_client_id¶
- license_pool¶
- license_pool_id¶
- patron¶
- patron_id¶
- position¶
- start¶
- until(default_loan_period, default_reservation_period)[source]¶
Give or estimate the time at which the book will be available to this patron. This is a very rough estimate that should be treated more or less as a worst case. (Though it could be even worse than this–the library’s license might expire and then you’ll _never_ get the book.)
- class core.model.patron.Loan(**kwargs)[source]¶
Bases:
Base
,LoanAndHoldMixin
- cached_content_type¶
- cached_manifest¶
- end¶
- external_identifier¶
- fulfillment¶
- fulfillment_id¶
- id¶
- integration_client¶
- integration_client_id¶
- license¶
- license_id¶
- license_pool¶
- license_pool_id¶
- patron¶
- patron_id¶
- start¶
- class core.model.patron.LoanAndHoldMixin[source]¶
Bases:
object
- property library¶
Try to find the corresponding library for this Loan/Hold.
- property work¶
Try to find the corresponding work for this Loan/Hold.
- class core.model.patron.Patron(**kwargs)[source]¶
Bases:
Base
- MAX_SYNC_TIME = datetime.timedelta(seconds=43200)¶
- classmethod age_appropriate_match(work_audience, work_target_age, reader_audience, reader_age)[source]¶
Match the audience and target age of a work with that of a reader, and see whether they are an age-appropriate match.
NOTE: What “age-appropriate” means depends on some policy questions that have not been answered and may be library-specific. For now, non-children’s books are age-inappropriate for young children, and children’s books are age-inappropriate for children too young to be in the book’s target age range.
- Parameters:
reader_audience – One of the audience constants from Classifier, representing the general reading audience to which the reader belongs.
reader_age – A number or 2-tuple representing the age or age range of the reader.
- annotations¶
- authorization_expires¶
- authorization_identifier¶
- block_reason¶
- cached_neighborhood¶
- credentials¶
- external_identifier¶
- external_type¶
- fines¶
- holds¶
- id¶
- identifier_to_remote_service(remote_data_source, generator=None)[source]¶
Find or randomly create an identifier to use when identifying this patron to a remote service. :param remote_data_source: A DataSource object (or name of a DataSource) corresponding to the remote service.
- last_external_sync¶
- last_loan_activity_sync¶
When was the last time we asked the vendors about this patron’s loan activity?
- Returns:
A datetime, or None if we know our loan data is stale.
- library¶
- library_id¶
- property loan_activity_max_age¶
In the absence of any other information, how long should loan activity be considered ‘fresh’ for this patron?
We reset Patron.last_loan_activity_sync immediately if we hear about a change to a patron’s loans or holds. This handles cases where patron activity happens where we can’t see it, e.g. on a vendor website or mobile app.
TODO: This is currently a constant, but in the future it could become a per-library setting.
- loans¶
- property root_lane¶
Find the Lane, if any, to be used as the Patron’s root lane.
A patron with a root Lane can only access that Lane and the Lanes beneath it. In addition, a patron with a root lane cannot conduct a transaction on a book intended for an older audience than the one defined by their root lane.
- synchronize_annotations¶
- username¶
- work_is_age_appropriate(work_audience, work_target_age)[source]¶
Is the given audience and target age an age-appropriate match for this Patron?
NOTE: What “age-appropriate” means depends on some policy questions that have not been answered and may be library-specific. For now, it is determined by comparing audience and target age to that of the Patron’s root lane.
This is designed for use when reasoning about works in general. If you have a specific Work in mind, use Work.age_appropriate_for_patron.
- Parameters:
work_audience – One of the audience constants from Classifier, representing the general reading audience to which a putative work belongs.
work_target_age – A number or 2-tuple representing the target age or age range of a putative work.
- Returns:
A boolean
- class core.model.patron.PatronProfileStorage(patron, url_for=None)[source]¶
Bases:
ProfileStorage
Interface between a Patron object and the User Profile Management Protocol.
- property profile_document¶
Create a Profile document representing the patron’s current status.
- update(settable, full)[source]¶
Bring the Patron’s status up-to-date with the given document. Right now this means making sure Patron.synchronize_annotations is up to date.
- property writable_setting_names¶
Return the subset of settings that are considered writable.
core.model.resource module¶
- class core.model.resource.Hyperlink(**kwargs)[source]¶
Bases:
Base
,LinkRelations
A link between an Identifier and a Resource.
- data_source¶
- data_source_id¶
- property default_filename¶
- classmethod generic_uri(data_source, identifier, rel, content=None)[source]¶
Create a generic URI for the other end of this hyperlink. This is useful for resources that are obtained through means other than fetching a single URL via HTTP. It lets us get a URI that’s most likely unique, so we can create a Resource object without violating the uniqueness constraint. If the output of this method isn’t unique in your situation (because the data source provides more than one link with a given link relation for a given identifier), you’ll need some other way of coming up with generic URIs.
- id¶
- identifier¶
- identifier_id¶
- rel¶
- resource¶
- resource_id¶
- classmethod unmirrored(collection)[source]¶
Find all Hyperlinks associated with an item in the given Collection that could be mirrored but aren’t. TODO: We don’t cover the case where an image was mirrored but no thumbnail was created of it. (We do cover the case where the thumbnail was created but not mirrored.)
- class core.model.resource.Representation(**kwargs)[source]¶
Bases:
Base
,MediaTypes
A cached document obtained from (and possibly mirrored to) the Web at large. Sometimes this is a DataSource’s representation of a specific book. Sometimes it’s associated with a database Resource (which has a well-defined relationship to one specific book). Sometimes it’s just a web page that we need a cached local copy of.
- AVOID_WHEN_CAUTIOUS_DOMAINS = ['gutenberg.org', 'books.google.com']¶
- BROWSER_USER_AGENT = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0'¶
- EXERCISE_CAUTION_DOMAINS = ['unglue.it']¶
- property age¶
- property best_thumbnail¶
Find the best thumbnail among all the thumbnails associated with this Representation. Basically, we prefer a thumbnail that has been mirrored.
- classmethod browser_http_get(url, headers, **kwargs)[source]¶
GET the representation that would be displayed to a web browser.
- classmethod cautious_http_get(url, headers, **kwargs)[source]¶
Examine the URL we’re about to GET, possibly going so far as to perform a HEAD request, to avoid making a request (or following a redirect) to a site known to cause problems. The motivating case is that unglue.it contains gutenberg.org links that appear to be direct links to EPUBs, but 1) they’re not direct links to EPUBs, and 2) automated requests to gutenberg.org quickly result in IP bans. So we don’t make those requests.
- property clean_media_type¶
The most basic version of this representation’s media type. No profiles or anything.
- content¶
- content_fh()[source]¶
Return an open filehandle to the representation’s contents. This works whether the representation is kept in the database or in a file on disk.
- default_filename(link=None, destination_type=None)[source]¶
Try to come up with a good filename for this representation.
- etag¶
- extension(destination_type=None)[source]¶
Try to come up with a good file extension for this representation.
- external_content()[source]¶
Return a filehandle to the representation’s contents, as they should be mirrored externally, and the media type to be used when mirroring.
- property external_media_type¶
- fetch_exception¶
- fetched_at¶
- file_size¶
- classmethod get(_db, url, do_get=None, extra_request_headers=None, accept=None, max_age=None, pause_before=0, allow_redirects=True, presumed_media_type=None, debug=True, response_reviewer=None, exception_handler=None, url_normalizer=None)[source]¶
Retrieve a representation from the cache if possible. If not possible, retrieve it from the web and store it in the cache.
- Parameters:
_db – A database connection.
url – The URL to use as the target of any HTTP request.
do_get – A function that takes arguments (url, headers) and retrieves a representation over the network.
accept – A value for the Accept HTTP header.
extra_request_headers – Any additional HTTP headers to include with the request.
max_age – A timedelta object representing the maximum time to consider a cached representation fresh. (We ignore the caching directives from web servers because they’re usually far too conservative for our purposes.)
pause_before – A number of seconds to pause before sending the HTTP request. This is for use in situations where HTTP requests are subject to throttling.
allow_redirects – Not currently used. (TODO: this seems like a problem!)
presumed_media_type – If the response does not contain a Content-Type header, or if the specified Content-Type is too generic to use, the representation will be presumed to be of this media type.
debug – If True, progress reports on the HTTP request will be logged.
response_reviewer – A function that takes a 3-tuple (status_code, headers, content) and raises an exception if the response should not be treated as cacheable.
exception_handler – A function that takes a 3-tuple (Representation, Exception, traceback) and handles an exceptional condition that occured during the HTTP request.
url_normalizer – A function that takes the URL to be used in the HTTP request, and returns the URL to use when storing the corresponding Representation in the database. This can be used to strip irrelevant or sensitive information from URLs to increase the chances of a cache hit.
- Returns:
A 2-tuple (representation, obtained_from_cache)
- classmethod get_would_be_useful(url, headers, do_not_access=None, check_for_redirect=None, head_client=None)[source]¶
Determine whether making a GET request to a given URL is likely to have a useful result.
- Parameters:
URL – URL under consideration.
headers – Headers that would be sent with the GET request.
do_not_access – Domains to which GET requests are not useful.
check_for_redirect – Domains to which we should make a HEAD request, in case they redirect to a do_not_access domain.
head_client – Function for making the HEAD request, if one becomes necessary. Should return requests.Response or a mock.
- classmethod guess_url_media_type_from_path(url)[source]¶
Guess a likely media type from the URL’s path component.
- property has_content¶
- headers¶
- id¶
- image_height¶
- image_width¶
- property is_image¶
- property is_usable¶
Returns True if the Representation has some data or received a status code that’s not in the 5xx series.
- last_modified¶
- local_content_path¶
- property local_path¶
Return the full local path to the representation on disk.
- location¶
- marc_file¶
- media_type¶
- mirror_exception¶
- mirror_url¶
- property mirrorable_media_type¶
Does this Representation look like the kind of thing we create mirrors of? Basically, images and books.
- mirrored_at¶
- pil_format_for_media_type = {'image/gif': 'gif', 'image/jpeg': 'jpeg', 'image/png': 'png'}¶
- classmethod post(_db, url, data, max_age=None, response_reviewer=None, **kwargs)[source]¶
Finds or creates POST request as a Representation
- property public_url¶
Find the best URL to publish when referencing this Representation in a public space. :return: a bytestring
- classmethod record_exception(representation, exception, traceback)[source]¶
Deal with a fetch exception by recording it and moving on.
- classmethod reraise_exception(representation, exception, traceback)[source]¶
Deal with a fetch exception by re-raising it.
- resource¶
- scale(max_height, max_width, destination_url, destination_media_type, force=False)[source]¶
Return a Representation that’s a scaled-down version of this Representation, creating it if necessary. :param destination_url: The URL the scaled-down resource will (eventually) be uploaded to. :return: A 2-tuple (Representation, is_new)
- scale_exception¶
- scaled_at¶
- set_as_mirrored(mirror_url)[source]¶
Record the fact that the representation has been mirrored to the given URL. This should only be called upon successful completion of the mirror operation.
- set_fetched_content(content, content_path=None)[source]¶
Simulate a successful HTTP request for this representation. This is used when the content of the representation is obtained through some other means.
- status_code¶
- thumbnail_of¶
- thumbnail_of_id¶
- property thumbnail_size_quality_penalty¶
- thumbnails¶
- property unicode_content¶
Attempt to convert the content into Unicode. If all attempts fail, we will return None rather than raise an exception.
- update_image_size()[source]¶
Make sure .image_height and .image_width are up to date. Clears .image_height and .image_width if the representation is not an image.
- url¶
- property url_extension¶
The file extension in this representation’s original url.
- class core.model.resource.Resource(**kwargs)[source]¶
Bases:
Base
An external resource that may be mirrored locally. E.g: a cover image, an epub, a description.
- ESTIMATED_QUALITY_WEIGHT = 5¶
- MINIMUM_IMAGE_QUALITY = 0.25¶
- class Work(**kwargs)¶
Bases:
Base
- ALL = 'all'¶
- APPEALS_URI = 'http://librarysimplified.org/terms/appeals/'¶
- CHARACTER_APPEAL = 'Character'¶
- CURRENTLY_AVAILABLE = 'currently_available'¶
- ELASTICSEARCH_TIME_FORMAT = 'YYYY-MM-DD"T"HH24:MI:SS"."MS'¶
- LANGUAGE_APPEAL = 'Language'¶
- LARGE_FIELDS = ['simple_opds_entry', 'verbose_opds_entry', 'marc_record', 'summary_text']¶
- NOT_APPLICABLE_APPEAL = 'Not Applicable'¶
- NO_APPEAL = 'None'¶
- SETTING_APPEAL = 'Setting'¶
- STORY_APPEAL = 'Story'¶
- UNKNOWN_APPEAL = 'Unknown'¶
- active_license_pool()¶
- age_appropriate_for_patron(patron)¶
Is this Work age-appropriate for the given Patron?
- Parameters:
patron – A Patron.
- Returns:
A boolean
- all_editions(policy=None)¶
All Editions identified by an Identifier equivalent to the identifiers of this Work’s license pools.
- Parameters:
policy – A PresentationCalculationPolicy, used to determine how far to go when looking for equivalent Identifiers.
- all_identifier_ids(policy=None)¶
Return all Identifier IDs associated with this Work.
- Parameters:
policy – A PresentationCalculationPolicy.
- Returns:
A set containing all Identifier IDs associated with this Work (as per the rules set down in policy).
- appeal_character¶
- appeal_language¶
- appeal_setting¶
- appeal_story¶
- appeal_type = Enum('Character', 'Language', 'Setting', 'Story', 'Not Applicable', 'None', 'Unknown', name='appeal')¶
- assign_appeals(character, language, setting, story, cutoff=0.2)¶
Assign the given appeals to the corresponding database fields, as well as calculating the primary and secondary appeal.
- assign_genres(identifier_ids, default_fiction=False, default_audience='Adult')¶
Set classification information for this work based on the subquery to get equivalent identifiers. :return: A boolean explaining whether or not any data actually changed.
- assign_genres_from_weights(genre_weights)¶
- audience¶
- property author¶
- cached_feeds¶
- calculate_marc_record()¶
- calculate_opds_entries(verbose=True)¶
- calculate_presentation(policy=None, search_index_client=None, exclude_search=False, default_fiction=None, default_audience=None)¶
Make a Work ready to show to patrons. Call calculate_presentation_edition() to find the best-quality presentation edition that could represent this work. Then determine the following information, global to the work: * Subject-matter classifications for the work. * Whether or not the work is fiction. * The intended audience for the work. * The best available summary for the work. * The overall popularity of the work.
- calculate_presentation_edition(policy=None)¶
Which of this Work’s Editions should be used as the default? First, every LicensePool associated with this work must have its presentation edition set. Then, we go through the pools, see which has the best presentation edition, and make it our presentation edition.
- calculate_quality(identifier_ids, default_quality=0)¶
- classifications_with_genre()¶
- property complaints¶
- property cover_full_url¶
- property cover_thumbnail_url¶
- coverage_records¶
- custom_list_entries¶
- default_quality_by_data_source = {'Axis 360': 0.65, 'Bibliotheca': 0.65, 'Gutenberg': 0, 'Overdrive': 0.4, 'Plympton': 0.5, 'RBdigital': 0.4, 'Standard Ebooks': 0.8, 'unglue.it': 0.4}¶
- delete(search_index=None)¶
Delete the work from both the DB and search index.
- property detailed_representation¶
A description of this work more detailed than repr()
- external_index_needs_updating()¶
Mark this work as needing to have its search document reindexed. This is a more efficient alternative to reindexing immediately, since these WorkCoverageRecords are handled in large batches.
- fiction¶
- classmethod for_unchecked_subjects(_db)¶
- classmethod from_identifiers(_db, identifiers, base_query=None, policy=None)¶
Returns all of the works that have one or more license_pools associated with either an identifier in the given list or an identifier considered equivalent to one of those listed.
- Parameters:
policy – A PresentationCalculationPolicy, used to determine how far to go when looking for equivalent Identifiers. By default, this method will be very strict about equivalencies.
- genres = ObjectAssociationProxyInstance(AssociationProxy('work_genres', 'genre'))¶
- property has_open_access_license¶
- id¶
- property imprint¶
- property language¶
- property language_code¶
A single 2-letter language code for display purposes.
- last_update_time¶
- license_pools¶
- make_exclusive_open_access_for_permanent_work_id(pwid, medium, language)¶
Ensure that every open-access LicensePool associated with this Work has the given PWID and medium. Any non-open-access LicensePool, and any LicensePool with a different PWID or a different medium, is kicked out and assigned to a different Work. LicensePools with no presentation edition or no PWID are kicked out. In most cases this Work will be the _only_ work for this PWID, but inside open_access_for_permanent_work_id this is called as a preparatory step for merging two Works, and after the call (but before the merge) there may be two Works for a given PWID.
- marc_record¶
- mark_licensepools_as_superceded()¶
Make sure that all but the single best open-access LicensePool for this Work are superceded. A non-open-access LicensePool should never be superceded, and this method will mark them as un-superceded.
- merge_into(other_work)¶
Merge this Work into another Work and delete it.
- classmethod missing_coverage_from(_db, operation=None, count_as_covered=None, count_as_missing_before=None)¶
Find Works which have no WorkCoverageRecord for the given operation.
- needs_full_presentation_recalculation()¶
Mark this work as needing to have its presentation completely recalculated.
This shifts the time spent recalculating presentation to a script dedicated to this purpose, rather than a script that interacts with APIs. It’s also more efficient, since a work might be flagged multiple times before we actually get around to recalculating the presentation.
- needs_new_presentation_edition()¶
Mark this work as needing to have its presentation edition regenerated. This is significantly less work than calling needs_full_presentation_recalculation, but it will not update a Work’s quality score, summary, or genre classification.
- classmethod open_access_for_permanent_work_id(_db, pwid, medium, language)¶
Find or create the Work encompassing all open-access LicensePools whose presentation Editions have the given permanent work ID, the given medium, and the given language. This may result in the consolidation or splitting of Works, if a book’s permanent work ID has changed without calculate_work() being called, or if the data is in an inconsistent state for any other reason.
- popularity¶
- presentation_edition¶
- presentation_edition_id¶
- presentation_ready¶
- presentation_ready_attempt¶
- presentation_ready_exception¶
- primary_appeal¶
- property publisher¶
- property pwids¶
Return the set of permanent work IDs associated with this Work. There should only be one permanent work ID associated with a given work, but if there is more than one, this will find all of them.
- quality¶
- rating¶
- reject_cover(search_index_client=None)¶
Suppresses the current cover of the Work
- classmethod reject_covers(_db, works_or_identifiers, search_index_client=None)¶
Suppresses the currently visible covers of a number of Works
- classmethod restrict_to_custom_lists(_db, base_query, custom_lists, on_list_as_of=None)¶
Annotate a query that joins Work against Edition to match only Works that are on one of the given custom lists.
- classmethod restrict_to_custom_lists_from_data_source(_db, base_query, data_source, on_list_as_of=None)¶
Annotate a query that joins Work against Edition to match only Works that are on a custom list from the given data source.
- secondary_appeal¶
- property series¶
- property series_position¶
- set_presentation_edition(new_presentation_edition)¶
Sets presentation edition and lets owned pools and editions know. Raises exception if edition to set to is None.
- set_presentation_ready(as_of=None, search_index_client=None, exclude_search=False)¶
Set this work as presentation-ready, no matter what.
This assumes that we know the work has the minimal information necessary to be found with typical queries and that patrons will be able to understand what work we’re talking about.
In most cases you should call set_presentation_ready_based_on_content instead, which runs those checks.
- set_presentation_ready_based_on_content(search_index_client=None)¶
Set this work as presentation ready, if it appears to be ready based on its data.
Presentation ready means the book is ready to be shown to patrons and (pending availability) checked out. It doesn’t necessarily mean the presentation is complete.
The absolute minimum data necessary is a title, a language, and a medium. We don’t need a cover or an author – we can fill in that info later if it exists.
TODO: search_index_client is redundant here.
- set_summary(resource)¶
- simple_opds_entry¶
- property sort_author¶
- property sort_title¶
- property subtitle¶
- summary¶
- summary_id¶
- summary_text¶
- target_age¶
- classmethod target_age_query(foreign_work_id_field)¶
- property target_age_string¶
- property title¶
- to_search_document()¶
Generate a search document for this Work.
- classmethod to_search_documents(works, policy=None)¶
Generate search documents for these Works. This is done by constructing an extremely complicated SQL query. The code is ugly, but it’s about 100 times faster than using python to create documents for each work individually. When working on the search index, it’s very important for this to be fast.
- Parameters:
policy – A PresentationCalculationPolicy to use when deciding how deep to go to find Identifiers equivalent to these works.
- top_genre()¶
- update_external_index(client, add_coverage_record=True)¶
Create a WorkCoverageRecord so that this work’s entry in the search index can be modified or deleted. This method is deprecated – call external_index_needs_updating() instead.
- verbose_opds_entry¶
- classmethod with_genre(_db, genre)¶
Find all Works classified under the given genre.
- classmethod with_no_genres(q)¶
Modify a query so it finds only Works that are not classified under any genre.
- work_genres¶
- add_quality_votes(quality, weight=1)[source]¶
Record someone’s vote as to the quality of this resource.
- approve()[source]¶
Approve a rejected Resource by making its human-generated voted_quality positive while taking its rejection into account.
- as_delivery_mechanism_for(licensepool)[source]¶
If this Resource is used in a LicensePoolDeliveryMechanism for the given LicensePool, return that LicensePoolDeliveryMechanism.
- cover_editions¶
- data_source¶
- data_source_id¶
- derived_through¶
- estimated_quality¶
- property final_url¶
URL to the final, mirrored version of this resource, suitable for serving to the client. :return: A URL, or None if the resource has no mirrored representation.
- id¶
- classmethod image_type_priority(media_type)[source]¶
Where does the given image media type rank on our list of preferences? :return: A lower number is better. None means it’s not an image type or we don’t care about it at all.
- licensepooldeliverymechanisms¶
- links¶
- quality¶
- property quality_as_thumbnail_image¶
Determine this image’s suitability for use as a thumbnail image.
- reject()[source]¶
Reject a Resource by making its voted_quality negative. If the Resource is a cover, this rejection will render it unusable to all Editions and Identifiers. Even if the cover is later approved a rejection impacts the overall weight of the vote_quality.
- representation¶
- representation_id¶
- rights_explanation¶
- rights_status¶
- rights_status_id¶
- set_fetched_content(media_type, content, content_path)[source]¶
Simulate a successful HTTP request for a representation of this resource. This is used when the content of the representation is obtained through some other means.
- summary_works¶
- transformations¶
- update_quality()[source]¶
Combine computer-generated estimated_quality with human-generated voted_quality to form overall quality.
- url¶
- voted_quality¶
- votes_for_quality¶
core.model.work module¶
- class core.model.work.Work(**kwargs)[source]¶
Bases:
Base
- ALL = 'all'¶
- APPEALS_URI = 'http://librarysimplified.org/terms/appeals/'¶
- CHARACTER_APPEAL = 'Character'¶
- CURRENTLY_AVAILABLE = 'currently_available'¶
- ELASTICSEARCH_TIME_FORMAT = 'YYYY-MM-DD"T"HH24:MI:SS"."MS'¶
- LANGUAGE_APPEAL = 'Language'¶
- LARGE_FIELDS = ['simple_opds_entry', 'verbose_opds_entry', 'marc_record', 'summary_text']¶
- NOT_APPLICABLE_APPEAL = 'Not Applicable'¶
- NO_APPEAL = 'None'¶
- SETTING_APPEAL = 'Setting'¶
- STORY_APPEAL = 'Story'¶
- UNKNOWN_APPEAL = 'Unknown'¶
- age_appropriate_for_patron(patron)[source]¶
Is this Work age-appropriate for the given Patron?
- Parameters:
patron – A Patron.
- Returns:
A boolean
- all_editions(policy=None)[source]¶
All Editions identified by an Identifier equivalent to the identifiers of this Work’s license pools.
- Parameters:
policy – A PresentationCalculationPolicy, used to determine how far to go when looking for equivalent Identifiers.
- all_identifier_ids(policy=None)[source]¶
Return all Identifier IDs associated with this Work.
- Parameters:
policy – A PresentationCalculationPolicy.
- Returns:
A set containing all Identifier IDs associated with this Work (as per the rules set down in policy).
- appeal_character¶
- appeal_language¶
- appeal_setting¶
- appeal_story¶
- appeal_type = Enum('Character', 'Language', 'Setting', 'Story', 'Not Applicable', 'None', 'Unknown', name='appeal')¶
- assign_appeals(character, language, setting, story, cutoff=0.2)[source]¶
Assign the given appeals to the corresponding database fields, as well as calculating the primary and secondary appeal.
- assign_genres(identifier_ids, default_fiction=False, default_audience='Adult')[source]¶
Set classification information for this work based on the subquery to get equivalent identifiers. :return: A boolean explaining whether or not any data actually changed.
- audience¶
- property author¶
- cached_feeds¶
- calculate_presentation(policy=None, search_index_client=None, exclude_search=False, default_fiction=None, default_audience=None)[source]¶
Make a Work ready to show to patrons. Call calculate_presentation_edition() to find the best-quality presentation edition that could represent this work. Then determine the following information, global to the work: * Subject-matter classifications for the work. * Whether or not the work is fiction. * The intended audience for the work. * The best available summary for the work. * The overall popularity of the work.
- calculate_presentation_edition(policy=None)[source]¶
Which of this Work’s Editions should be used as the default? First, every LicensePool associated with this work must have its presentation edition set. Then, we go through the pools, see which has the best presentation edition, and make it our presentation edition.
- property complaints¶
- property cover_full_url¶
- property cover_thumbnail_url¶
- coverage_records¶
- custom_list_entries¶
- default_quality_by_data_source = {'Axis 360': 0.65, 'Bibliotheca': 0.65, 'Gutenberg': 0, 'Overdrive': 0.4, 'Plympton': 0.5, 'RBdigital': 0.4, 'Standard Ebooks': 0.8, 'unglue.it': 0.4}¶
- property detailed_representation¶
A description of this work more detailed than repr()
- external_index_needs_updating()[source]¶
Mark this work as needing to have its search document reindexed. This is a more efficient alternative to reindexing immediately, since these WorkCoverageRecords are handled in large batches.
- fiction¶
- classmethod from_identifiers(_db, identifiers, base_query=None, policy=None)[source]¶
Returns all of the works that have one or more license_pools associated with either an identifier in the given list or an identifier considered equivalent to one of those listed.
- Parameters:
policy – A PresentationCalculationPolicy, used to determine how far to go when looking for equivalent Identifiers. By default, this method will be very strict about equivalencies.
- genres = ObjectAssociationProxyInstance(AssociationProxy('work_genres', 'genre'))¶
- property has_open_access_license¶
- id¶
- property imprint¶
- property language¶
- property language_code¶
A single 2-letter language code for display purposes.
- last_update_time¶
- license_pools¶
- make_exclusive_open_access_for_permanent_work_id(pwid, medium, language)[source]¶
Ensure that every open-access LicensePool associated with this Work has the given PWID and medium. Any non-open-access LicensePool, and any LicensePool with a different PWID or a different medium, is kicked out and assigned to a different Work. LicensePools with no presentation edition or no PWID are kicked out. In most cases this Work will be the _only_ work for this PWID, but inside open_access_for_permanent_work_id this is called as a preparatory step for merging two Works, and after the call (but before the merge) there may be two Works for a given PWID.
- marc_record¶
- mark_licensepools_as_superceded()[source]¶
Make sure that all but the single best open-access LicensePool for this Work are superceded. A non-open-access LicensePool should never be superceded, and this method will mark them as un-superceded.
- classmethod missing_coverage_from(_db, operation=None, count_as_covered=None, count_as_missing_before=None)[source]¶
Find Works which have no WorkCoverageRecord for the given operation.
- needs_full_presentation_recalculation()[source]¶
Mark this work as needing to have its presentation completely recalculated.
This shifts the time spent recalculating presentation to a script dedicated to this purpose, rather than a script that interacts with APIs. It’s also more efficient, since a work might be flagged multiple times before we actually get around to recalculating the presentation.
- needs_new_presentation_edition()[source]¶
Mark this work as needing to have its presentation edition regenerated. This is significantly less work than calling needs_full_presentation_recalculation, but it will not update a Work’s quality score, summary, or genre classification.
- classmethod open_access_for_permanent_work_id(_db, pwid, medium, language)[source]¶
Find or create the Work encompassing all open-access LicensePools whose presentation Editions have the given permanent work ID, the given medium, and the given language. This may result in the consolidation or splitting of Works, if a book’s permanent work ID has changed without calculate_work() being called, or if the data is in an inconsistent state for any other reason.
- popularity¶
- presentation_edition¶
- presentation_edition_id¶
- presentation_ready¶
- presentation_ready_attempt¶
- presentation_ready_exception¶
- primary_appeal¶
- property publisher¶
- property pwids¶
Return the set of permanent work IDs associated with this Work. There should only be one permanent work ID associated with a given work, but if there is more than one, this will find all of them.
- quality¶
- rating¶
- classmethod reject_covers(_db, works_or_identifiers, search_index_client=None)[source]¶
Suppresses the currently visible covers of a number of Works
- classmethod restrict_to_custom_lists(_db, base_query, custom_lists, on_list_as_of=None)[source]¶
Annotate a query that joins Work against Edition to match only Works that are on one of the given custom lists.
- classmethod restrict_to_custom_lists_from_data_source(_db, base_query, data_source, on_list_as_of=None)[source]¶
Annotate a query that joins Work against Edition to match only Works that are on a custom list from the given data source.
- secondary_appeal¶
- property series¶
- property series_position¶
- set_presentation_edition(new_presentation_edition)[source]¶
Sets presentation edition and lets owned pools and editions know. Raises exception if edition to set to is None.
- set_presentation_ready(as_of=None, search_index_client=None, exclude_search=False)[source]¶
Set this work as presentation-ready, no matter what.
This assumes that we know the work has the minimal information necessary to be found with typical queries and that patrons will be able to understand what work we’re talking about.
In most cases you should call set_presentation_ready_based_on_content instead, which runs those checks.
- set_presentation_ready_based_on_content(search_index_client=None)[source]¶
Set this work as presentation ready, if it appears to be ready based on its data.
Presentation ready means the book is ready to be shown to patrons and (pending availability) checked out. It doesn’t necessarily mean the presentation is complete.
The absolute minimum data necessary is a title, a language, and a medium. We don’t need a cover or an author – we can fill in that info later if it exists.
TODO: search_index_client is redundant here.
- simple_opds_entry¶
- property sort_author¶
- property sort_title¶
- property subtitle¶
- summary¶
- summary_id¶
- summary_text¶
- target_age¶
- property target_age_string¶
- property title¶
- classmethod to_search_documents(works, policy=None)[source]¶
Generate search documents for these Works. This is done by constructing an extremely complicated SQL query. The code is ugly, but it’s about 100 times faster than using python to create documents for each work individually. When working on the search index, it’s very important for this to be fast.
- Parameters:
policy – A PresentationCalculationPolicy to use when deciding how deep to go to find Identifiers equivalent to these works.
- update_external_index(client, add_coverage_record=True)[source]¶
Create a WorkCoverageRecord so that this work’s entry in the search index can be modified or deleted. This method is deprecated – call external_index_needs_updating() instead.
- verbose_opds_entry¶
- classmethod with_no_genres(q)[source]¶
Modify a query so it finds only Works that are not classified under any genre.
- work_genres¶
Module contents¶
- class core.model.PresentationCalculationPolicy(choose_edition=True, set_edition_metadata=True, classify=True, choose_summary=True, calculate_quality=True, choose_cover=True, regenerate_opds_entries=False, regenerate_marc_record=False, update_search_index=False, verbose=True, equivalent_identifier_levels=3, equivalent_identifier_threshold=0.5, equivalent_identifier_cutoff=1000)[source]¶
Bases:
object
Which parts of the Work or Edition’s presentation are we actually looking to update?
- DEFAULT_CUTOFF = 1000¶
- DEFAULT_LEVELS = 3¶
- DEFAULT_THRESHOLD = 0.5¶
- class core.model.SessionManager[source]¶
Bases:
object
- RECURSIVE_EQUIVALENTS_FUNCTION = 'recursive_equivalents.sql'¶
- engine_for_url = {}¶
- classmethod initialize(url, initialize_data=True, initialize_schema=True)[source]¶
Initialize the database.
This includes the schema, the custom functions, and the initial content.
- core.model.flush(db)[source]¶
Flush the database connection unless it’s known to already be flushing.
- core.model.get_one(db, model, on_multiple='error', constraint=None, **kwargs)[source]¶
Gets an object from the database based on its attributes.
- Parameters:
constraint – A single clause that can be passed into sqlalchemy.Query.filter to limit the object that is returned.
- Returns:
object or None
- core.model.get_one_or_create(db, model, create_method='', create_method_kwargs=None, **kwargs)[source]¶