api.sip package

Submodules

api.sip.client module

A simple SIP2 client.

Implementation is guided by the SIP2 specification:

http://multimedia.3m.com/mws/media/355361O/sip2-protocol.pdf

This client implements a very small part of SIP2 but is easily extensible.

This client is based on sip2talk.py. Here is the original licensing information for sip2talk.py:

Copyright [2010] [Eli Fulkerson]

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

class api.sip.client.Constants[source]

Bases: object

DEFAULT_ENCODING = 'cp850'
ENGLISH = '001'
TERMINATOR_CHAR = '\r'
UNKNOWN_LANGUAGE = '000'
class api.sip.client.MockSIPClient(**kwargs)[source]

Bases: SIPClient

A SIP client that relies on canned responses rather than a socket connection.

connect()[source]

Create a socket connection to a SIP server.

disconnect()[source]

Close the connection to the SIP server.

do_send(data)[source]

Actually send data over the socket.

This method exists only to be subclassed by MockSIPClient.

queue_response(response)[source]
read_message(max_size=1048576)[source]

Read a response message off the queue.

class api.sip.client.MockSIPClientFactory[source]

Bases: object

Pass this into SIP2AuthenticationProvider to instantiate a MockSIPClient based on its configuration, _and_ to use that MockSIPClient every single time; normally SIP2AuthenticationProvider will instantiate a different client for every simulated server interaction, making it impossible to queue responses or look at the results.

exception api.sip.client.RequestResend[source]

Bases: OSError

There was an error transmitting a message and the server has requested that it be resent.

class api.sip.client.SIPClient(target_server, target_port, login_user_id=None, login_password=None, location_code=None, institution_id='', separator=None, use_ssl=False, ssl_cert=None, ssl_key=None, encoding='cp850', dialect=<class 'api.sip.dialect.GenericILS'>)[source]

Bases: Constants

CARD_REPORTED_LOST = 'card reported lost'
CHARGE_PRIVILEGES_DENIED = 'charge privileges denied'
EXCESSIVE_FEES = 'excessive outstanding fees'
EXCESSIVE_FINES = 'excessive outstanding fines'
HOLD_PRIVILEGES_DENIED = 'hold privileges denied'
MAXIMUM_RETRIES = 5
PATRON_STATUS_FIELDS = ['charge privileges denied', 'renewal privileges denied', 'recall privileges denied', 'hold privileges denied', 'card reported lost', 'too many items charged', 'too many items overdue', 'too many renewals', 'too many claims of items returned', 'too many items lost', 'excessive outstanding fines', 'excessive outstanding fees', 'recall overdue', 'too many items billed']
PATRON_STATUS_FIELDS_THAT_DENY_BORROWING_PRIVILEGES = ['charge privileges denied', 'card reported lost', 'too many items charged', 'too many items overdue', 'too many items lost', 'excessive outstanding fines', 'excessive outstanding fees', 'recall overdue', 'too many items billed']
RECALL_OVERDUE = 'recall overdue'
RECALL_PRIVILEGES_DENIED = 'recall privileges denied'
RENEWAL_PRIVILEGES_DENIED = 'renewal privileges denied'
TOO_MANY_ITEMS_BILLED = 'too many items billed'
TOO_MANY_ITEMS_CHARGED = 'too many items charged'
TOO_MANY_ITEMS_OVERDUE = 'too many items overdue'
TOO_MANY_LOST = 'too many items lost'
TOO_MANY_RENEWALS = 'too many renewals'
TOO_MANY_RETURN_CLAIMS = 'too many claims of items returned'
append_checksum(text, include_sequence_number=True)[source]

Calculates checksum for passed-in message, and returns the message with the checksum appended.

Parameters:

include_sequence_number – If this is true, include the current sequence number in the message, just before the checksum, and increment the sequence number. If this is false, do not include or increment the sequence number.

When error checking is enabled between the ACS and the SC, each SC->ACS message is labeled with a sequence number (0, 1, 2, …). When responding, the ACS tells the SC which sequence message it’s responding to.

connect()[source]

Create a socket connection to a SIP server.

consume_status_code(data, expected, in_progress)[source]

Pull the status code (the first two characters) off the given response string, and verify that it’s as expected.

disconnect()[source]

Close the connection to the SIP server.

do_send(data)[source]

Actually send data over the socket.

This method exists only to be subclassed by MockSIPClient.

end_session(*args, **kwargs)[source]

Send end session message.

end_session_message(patron_identifier, patron_password='', terminal_password='')[source]

This message will be sent when a patron has completed all of their transactions. The ACS may, upon receipt of this command, close any open files or deallocate data structures pertaining to that patron. The ACS should respond with an End Session Response message.

Format of message to send to ILS: 35<transaction date><institution id><patron identifier> <terminal password><patron password> transaction date: 18-char, YYYYMMDDZZZZHHMMSS, required institution id: AO, variable length, required patron identifier: AA, variable length, required terminal password: AC, variable length, optional patron password: AD, variable length, optional

end_session_response_parser(message)[source]

Parse the response from a end session message.

log = <Logger SIPClient (WARNING)>
login()[source]

Log in to the SIP server if required.

login_message(login_user_id, login_password, location_code='', uid_algorithm='0', pwd_algorithm='0')[source]

Generate a message for logging in to a SIP server.

login_response_parser(message)[source]

Parse the response from a login message.

make_insecure_connection()[source]

Actually set up a socket connection.

make_request(message_creator, parser, *args, **kwargs)[source]

Send a request to a SIP server and parse the response.

Parameters:
  • connection – Socket to send data over.

  • message_creator – A function that creates the message to send.

  • parser – A function that parses the response message.

make_secure_connection()[source]

Create an SSL-enabled socket connection.

now()[source]

Return the current time, formatted as SIP expects it.

classmethod parse_patron_status(status_string)[source]

Parse the raw 14-character patron_status string.

Returns:

A 14-element dictionary mapping flag names to boolean values.

parse_response(data, expect_status_code, *fields)[source]

Verify that the given response string starts with the expected status code. Then extract the values of both fixed-width and named fields.

Parameters:

return – A dictionary containing the parsed-out information.

patron_information(*args, **kwargs)[source]

Get information about a patron.

patron_information_parser(data)[source]

Parse the message sent in response to a patron information request.

Format of message expected from ILS: 64<patron status><language><transaction date><hold items count><overdue items count> <charged items count><fine items count><recall items count><unavailable holds count> <institution id><patron identifier><personal name><hold items limit><overdue items limit> <charged items limit><valid patron><valid patron password><currency type><fee amount> <fee limit><items><home address><e-mail address><home phone number><screen message><print line>

patron status: 14-char, required language: 3-char, req transaction date: 18-char, YYYYMMDDZZZZHHMMSS, required hold items count: 4-char, required overdue items count: 4-char, required charged items count: 4-char, required fine items count: 4-char, required recall items count: 4-char, required unavailable holds count: 4-char, required institution id: AO, variable-length, required patron identifier: AA, var-length, req personal name: AE, var-length, req hold items limit: BZ, 4-char, optional overdue items limit: CA, 4-char, optional charged items limit: CB, 4-char, optional valid patron: BL, 1-char, Y/N, optional valid patron password: CQ, 1-char, Y/N, optional currency type: BH, 3-char, optional fee amount: BV, var-length. The amount of fees owed by this patron. fee limit: CC, variable-length, optional items: 0 or more instances of one of the following, based on “summary” field of patron information message hold items: AS, var-length opt (should be sent for each hold item) overdue items: AT, var-length opt (should be sent for each overdue item) charged items: AU, var-length opt (should be sent for each charged item) fine items: AV, var-length opt (should be sent for each fine item) recall items: BU, var-length opt (should be sent for each recall item) unavailable hold items: CD, var-length opt (should be sent for each unavailable hold item) home address: BD, variable-length, optional email address: VE, variable-length, optional home phone number: BF, variable-length optional

screen message: AF, var-length, optional print line: AG, var-length, optional

patron_information_request(patron_identifier, patron_password='', terminal_password='', language=None, summary=None)[source]

A superset of patron status request.

Format of message to send to ILS: 63<language><transaction date><summary><institution id><patron identifier> <terminal password><patron password><start item><end item> language: 3-char, required transaction date: 18-char, YYYYMMDDZZZZHHMMSS, required summary: 10-char, required institution id: AO, variable length, required patron identifier: AA, variable length, required terminal password: AC, variable length, optional patron password: AD, variable length, optional start item: BP, variable length, optional end item: BQ, variable length, optional

read_message(max_size=1048576)[source]

Read a SIP2 message from the socket connection.

A SIP2 message ends with a r character.

reset_connection_state()[source]

Reset connection-specific state. Specifically, the sequence number.

send(data)[source]

Send a message over the socket and update the sequence index.

summary(hold_items=False, overdue_items=False, charged_items=False, fine_items=False, recall_items=False, unavailable_holds=False)[source]

Generate the SIP summary field: a 10-character query string for requesting detailed information about a patron’s relationship with items.

class api.sip.client.fixed(internal_name, length)[source]

Bases: object

A fixed-width field in a SIP2 response.

charged_items_count = <api.sip.client.fixed object>
consume(data, in_progress)[source]

Remove the value of this field from the beginning of the input string, and store it in the given dictionary.

Parameters:

in_progress – A dictionary mapping field names to values. The value of this field will be stored in this dictionary.

Returns:

The original input string, after the value of this field has been removed.

end_session = <api.sip.client.fixed object>
fine_items_count = <api.sip.client.fixed object>
hold_items_count = <api.sip.client.fixed object>
language = <api.sip.client.fixed object>
login_ok = <api.sip.client.fixed object>
overdue_items_count = <api.sip.client.fixed object>
patron_status = <api.sip.client.fixed object>
recall_items_count = <api.sip.client.fixed object>
transaction_date = <api.sip.client.fixed object>
unavailable_holds_count = <api.sip.client.fixed object>
class api.sip.client.named(internal_name, sip_code, required=False, length=None, allow_multiple=False)[source]

Bases: object

A variable-length field in a SIP2 response.

charged_items = <api.sip.client.named object>
charged_items_limit = <api.sip.client.named object>
consume(value, in_progress)[source]

Process the given value for this field.

Unlike fixed.consume, this does not modify the value – it’s assumed that this particular field value has already been isolated from the response string.

Parameters:

in_progress – A dictionary mapping field names to values. The value of this field will be stored in this dictionary.

currency_type = <api.sip.client.named object>
email_address = <api.sip.client.named object>
fee_amount = <api.sip.client.named object>
fee_limit = <api.sip.client.named object>
fine_items = <api.sip.client.named object>
hold_items = <api.sip.client.named object>
hold_items_limit = <api.sip.client.named object>
home_address = <api.sip.client.named object>
institution_id = <api.sip.client.named object>
overdue_items = <api.sip.client.named object>
overdue_items_limit = <api.sip.client.named object>
patron_identifier = <api.sip.client.named object>
personal_name = <api.sip.client.named object>
phone_number = <api.sip.client.named object>
polaris_patron_birthdate = <api.sip.client.named object>
polaris_patron_expiration = <api.sip.client.named object>
polaris_patron_expired = <api.sip.client.named object>
polaris_postal_code = <api.sip.client.named object>
print_line = <api.sip.client.named object>
recall_items = <api.sip.client.named object>
property required

Create a variant of this field which is required.

Most variable-length fields are not required, but certain fields may be required in the responses to specific types of requests.

To check whether a specific field actually is required, check field.req.

screen_message = <api.sip.client.named object>
sequence_number = <api.sip.client.named object>
sipserver_internal_id = <api.sip.client.named object>
sipserver_internet_privileges = <api.sip.client.named object>
sipserver_patron_class = <api.sip.client.named object>
sipserver_patron_expiration = <api.sip.client.named object>
unavailable_hold_items = <api.sip.client.named object>
valid_patron = <api.sip.client.named object>
valid_patron_password = <api.sip.client.named object>

api.sip.dialect module

class api.sip.dialect.AutoGraphicsVerso[source]

Bases: Dialect

sendEndSession = False
class api.sip.dialect.Dialect[source]

Bases: object

Describe a SIP2 dialect.

AG_VERSO = 'AutoGraphicsVerso'
GENERIC_ILS = 'GenericILS'
static load_dialect(dialect)[source]
sendEndSession = None
class api.sip.dialect.GenericILS[source]

Bases: Dialect

sendEndSession = True

Module contents

api.sip.AuthenticationProvider

alias of SIP2AuthenticationProvider

class api.sip.SIP2AuthenticationProvider(library, integration, analytics=None, client=<class 'api.sip.client.SIPClient'>, connect=True)[source]

Bases: BasicAuthenticationProvider

DATE_FORMATS = ['%Y%m%d', '%Y%m%d%Z%H%M%S', '%Y%m%d    %H%M%S']
DEFAULT_ENCODING = 'cp850'
ENCODING = 'encoding'
FIELD_SEPARATOR = 'field separator'
ILS = 'ils'
LOCATION_CODE = 'location code'
NAME = 'SIP2'
PATRON_STATUS_BLOCK = 'patron status block'
PORT = 'port'
SETTINGS = [{'key': 'url', 'label': l'Server', 'required': True}, {'key': 'port', 'label': l'Port', 'required': True, 'type': 'number'}, {'key': 'username', 'label': l'Login User ID'}, {'key': 'password', 'label': l'Login Password'}, {'key': 'location code', 'label': l'Location Code'}, {'key': 'encoding', 'label': l'Data encoding', 'default': 'cp850', 'description': l'By default, SIP2 servers encode outgoing data using the Code Page 850 encoding, but some ILSes allow some other encoding to be used, usually UTF-8.'}, {'key': 'use_ssl', 'label': l'Connect over SSL?', 'description': l'Some SIP2 servers require or allow clients to connect securely over SSL. Other servers don't support SSL, and require clients to use an ordinary socket connection.', 'type': 'select', 'options': [{'key': 'true', 'label': l'Connect to the SIP2 server over SSL'}, {'key': 'false', 'label': l'Connect to the SIP2 server over an ordinary socket connection'}], 'default': 'false', 'required': True}, {'key': 'ils', 'label': l'ILS', 'description': l'Some ILS require specific SIP2 settings. If the ILS you are using is in the list please pick it otherwise select 'Generic ILS'.', 'type': 'select', 'options': [{'key': 'GenericILS', 'label': l'Generic ILS'}, {'key': 'AutoGraphicsVerso', 'label': l'Auto-Graphics VERSO'}], 'default': 'GenericILS', 'required': True}, {'key': 'ssl_certificate', 'label': l'SSL Certificate', 'description': l'The SSL certificate used to securely connect to an SSL-enabled SIP2 server. Not all SSL-enabled SIP2 servers require a custom certificate, but some do. This should be a string beginning with <code>-----BEGIN CERTIFICATE-----</code> and ending with <code>-----END CERTIFICATE-----</code>', 'type': 'textarea'}, {'key': 'ssl_key', 'label': l'SSL Key', 'description': l'The private key, if any, used to sign the SSL certificate above. If present, this should be a string beginning with <code>-----BEGIN PRIVATE KEY-----</code> and ending with <code>-----END PRIVATE KEY-----</code>', 'type': 'textarea'}, {'key': 'field separator', 'label': l'Field Separator', 'default': '|', 'required': True}, {'key': 'patron status block', 'label': l'SIP2 Patron Status Block', 'description': l'Block patrons from borrowing based on the status of the SIP2 <em>patron status</em> field.', 'type': 'select', 'options': [{'key': 'true', 'label': l'Block based on patron status field'}, {'key': 'false', 'label': l'No blocks based on patron status field'}], 'default': 'true'}, {'key': 'test_identifier', 'label': l'Test Identifier', 'description': l'A valid identifier that can be used to test that patron authentication is working. An optional Test Password for this identifier can be set in the next section.', 'required': True}, {'key': 'test_password', 'label': l'Test Password', 'description': l'The password for the Test Identifier (above, in previous section).'}, {'key': 'identifier_barcode_format', 'label': l'Patron identifier barcode format', 'description': l'Many libraries render patron identifiers as barcodes on physical library cards. If you specify the barcode format, patrons will be able to scan their library cards with a camera instead of manually typing in their identifiers.', 'type': 'select', 'options': [{'key': 'Codabar', 'label': l'Patron identifiers are are rendered as barcodes in Codabar format'}, {'key': '', 'label': l'Patron identifiers are not rendered as barcodes'}], 'default': '', 'required': True}, {'key': 'identifier_regular_expression', 'label': l'Identifier Regular Expression', 'description': l'A patron's identifier will be immediately rejected if it doesn't match this regular expression.'}, {'key': 'password_regular_expression', 'label': l'Password Regular Expression', 'description': l'A patron's password will be immediately rejected if it doesn't match this regular expression.'}, {'key': 'identifier_keyboard', 'label': l'Keyboard for identifier entry', 'type': 'select', 'options': [{'key': 'Default', 'label': l'System default'}, {'key': 'Email address', 'label': l'Email address entry'}, {'key': 'Number pad', 'label': l'Number pad'}], 'default': 'Default', 'required': True}, {'key': 'password_keyboard', 'label': l'Keyboard for password entry', 'type': 'select', 'options': [{'key': 'Default', 'label': l'System default'}, {'key': 'Number pad', 'label': l'Number pad'}, {'key': 'No input', 'label': l'Patrons have no password and should not be prompted for one.'}], 'default': 'Default'}, {'key': 'identifier_maximum_length', 'label': l'Maximum identifier length', 'type': 'number'}, {'key': 'password_maximum_length', 'label': l'Maximum password length', 'type': 'number'}, {'key': 'identifier_label', 'label': l'Label for identifier entry'}, {'key': 'password_label', 'label': l'Label for password entry'}]
SPECIFIC_BLOCK_REASONS = {'card reported lost': 'card reported lost', 'charge privileges denied': 'no borrowing privileges', 'excessive outstanding fees': 'excessive fees', 'excessive outstanding fines': 'excessive fines', 'recall overdue': 'recall overdue', 'too many items billed': 'too many items billed', 'too many items charged': 'too many active loans', 'too many items lost': 'too many items lost', 'too many items overdue': 'too many items overdue', 'too many renewals': 'too many renewals'}
SSL_CERTIFICATE = 'ssl_certificate'
SSL_KEY = 'ssl_key'
USE_SSL = 'use_ssl'
info_to_patrondata(info, validate_password=True)[source]

Convert the SIP-specific dictionary obtained from SIPClient.patron_information() to an abstract, authenticator-independent PatronData object.

classmethod parse_date(value)[source]

Try to parse value using any of several common date formats.

patron_information(username, password)[source]
remote_authenticate(username, password)[source]

Authenticate a patron with the SIP2 server.

Parameters:
  • username – The patron’s username/barcode/card number/authorization identifier.

  • password – The patron’s password/pin/access code.