Source code for invenio_sip2.ext

# -*- coding: utf-8 -*-
#
# INVENIO-SIP2
# Copyright (C) 2020 UCLouvain
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Flask extension for Invenio-SIP2."""

from __future__ import absolute_import, print_function

from copy import deepcopy
from datetime import datetime, timezone

from flask import current_app
from werkzeug.utils import cached_property

from . import config
from .actions.actions import Action
from .errors import SelfCheckActionError
from .helpers import MessageTypeFixedField, MessageTypeVariableField
from .utils import convert_bool_to_char


[docs]class InvenioSIP2(object): """Invenio-SIP2 extension.""" def __init__(self, app=None): """Extension initialization.""" if app: self.init_app(app)
[docs] def init_app(self, app): """Flask application initialization.""" self.init_config(app) app.extensions['invenio-sip2'] = self
[docs] def init_config(self, app): """Initialize configuration.""" # Use theme's base template if theme is installed app.config.setdefault( 'SIP2_BASE_TEMPLATE', app.config.get('BASE_TEMPLATE', 'invenio_sip2/base.html') ) for k in dir(config): if k.startswith('SIP2_'): app.config.setdefault(k, getattr(config, k)) self.load_fixed_field(app) self.load_variable_field(app)
[docs] def load_fixed_field(self, app): """Load fixed field configuration.""" for name, field in app.config["SIP2_FIXED_FIELD_DEFINITION"].items(): setattr(MessageTypeFixedField, name, MessageTypeFixedField( field_id=name, length=field.get('length'), label=field.get('label') ))
[docs] def load_variable_field(self, app): """Load variable field configuration.""" for name, field \ in app.config["SIP2_VARIABLE_FIELD_DEFINITION"].items(): setattr(MessageTypeVariableField, name, MessageTypeVariableField( field_id=field.get('field_id'), length=field.get('length', None), label=field.get('label') ))
[docs] @cached_property def sip2(self): """Return the SIP2 action machine.""" return _SIP2( action_config=deepcopy( current_app.config["SIP2_MESSAGE_ACTIONS"] ) )
[docs] @cached_property def sip2_message_types(self): """Message type configuration.""" return _Sip2MessageType( message_type_config=deepcopy( current_app.config['SIP2_SELFCHECK_MESSAGE_TYPES'] ) )
@property def sip2_current_date(self): """Get current date from system.""" return datetime.now(timezone.utc).strftime( current_app.config['SIP2_DATE_FORMAT'] )
[docs] @cached_property def supported_protocol(self): """Supported protocol by the automated circulation system.""" return current_app.config['SIP2_PROTOCOL']
[docs] @cached_property def support_checkin(self): """Support of checkin by the automated circulation system.""" return convert_bool_to_char( current_app.config['SIP2_SUPPORT_CHECKIN'] )
[docs] @cached_property def support_checkout(self): """Support of checkout by the automated circulation system.""" return convert_bool_to_char( current_app.config['SIP2_SUPPORT_CHECKOUT'] )
[docs] @cached_property def support_online_status(self): """Support of online status by the automated circulation system.""" return convert_bool_to_char( current_app.config['SIP2_SUPPORT_ONLINE_STATUS'] )
[docs] @cached_property def support_offline_status(self): """Support of offline status by the automated circulation system.""" return convert_bool_to_char( current_app.config['SIP2_SUPPORT_OFFLINE_STATUS'] )
[docs] @cached_property def support_status_update(self): """Support of status update by the automated circulation system.""" return convert_bool_to_char( current_app.config['SIP2_SUPPORT_STATUS_UPDATE'] )
[docs] @cached_property def support_renewal_policy(self): """Support of renewal policy by the automated circulation system.""" return convert_bool_to_char( current_app.config['SIP2_SUPPORT_RENEWAL_POLICY'] )
[docs] @cached_property def timeout_period(self): """Timeout period allowed by the automated circulation system.""" return current_app.config['SIP2_TIMEOUT_PERIOD']
[docs] @cached_property def retries_allowed(self): """Number of retries allowed by the automated circulation system.""" return current_app.config['SIP2_RETRIES_ALLOWED']
[docs] @cached_property def supported_messages(self): """Supported messages by the automated circulation system.""" # TODO: return supported message type from config return 'YYYYYYYYYYYYYYYY'
class _SIP2(object): """SIP2 action machine.""" def __init__(self, action_config): """Constructor.""" self.actions = {} for src_command, action in action_config.items(): self.actions.setdefault(src_command, []) _cls = action.pop('action', Action) instance = _cls(**dict(action, command=src_command)) self.actions[src_command] = instance def execute(self, msg, **kwargs): """Execute action on message.""" try: action = self.actions[msg.command] return action.execute(msg, **kwargs) except SelfCheckActionError: pass class _Sip2MessageType(object): message_types = {} def __init__(self, message_type_config): """Constructor.""" for command, message_type in message_type_config.items(): self.message_types[command] = _MessageType(command, **message_type) def get_by_command(self, command): return self.message_types.get(command, None) class _MessageType(object): def __init__(self, command, **kwargs): self.command = command self.fixed_fields = [] for fixed_field in kwargs.pop('fixed_fields', []): field = MessageTypeFixedField.get(fixed_field) self.fixed_fields.append( field ) for key, value in kwargs.items(): setattr(self, key, value)