Source code for ciowarehouse.models.dbhandler
"""SQLAlchemy-powered model definitions for file handlers."""
from sqlalchemy import Column, ForeignKey, String, Integer
from sqlalchemy.orm import relationship
from lxml import etree
from chrysalio.lib.utils import make_id
from chrysalio.models import ID_LEN, DBDeclarativeClass
from chrysalio.models.dbbase import DBBaseClass
from ..relaxng import RELAXNG_CIOWAREHOUSE
from ..lib.i18n import _
from ..models.dbindexfield import INDEXFIELD_DEFAULTS
# =============================================================================
[docs]class DBHandler(DBDeclarativeClass, DBBaseClass):
"""SQLAlchemy-powered file handler class."""
suffix = 'ciohdl'
__tablename__ = 'wrh_handlers'
__table_args__ = {'mysql_engine': 'InnoDB'}
handler_id = Column(String(ID_LEN), primary_key=True)
indexers = relationship('DBHandlerIndexer', cascade='all, delete')
# -------------------------------------------------------------------------
[docs] @classmethod
def xml2db(cls, dbsession, handler_elt, error_if_exists=True, kwargs=None):
"""Load a file handler from a XML element.
:type dbsession: sqlalchemy.orm.session.Session
:param dbsession:
SQLAlchemy session.
:type handler_elt: lxml.etree.Element
:param handler_elt:
File handler XML element.
:param bool error_if_exists: (default=True)
It returns an error if file handler already exists.
:param dict kwargs: (optional)
Dictionary of keyword arguments.
:rtype: :class:`pyramid.i18n.TranslationString` or ``None``
:return:
Error message or ``None``.
"""
# Check if already exists
handler_id = make_id(handler_elt.get('id'), 'token', ID_LEN)
dbhandler = dbsession.query(cls).filter_by(
handler_id=handler_id).first()
if dbhandler is not None:
if error_if_exists:
return _(
'File handler "${h}" already exists.', {'h': handler_id})
return None
# Create handler
record = cls.record_from_xml(handler_id)
error = cls.record_format(record)
if error:
return error
dbhandler = cls(**record)
dbsession.add(dbhandler)
# Add indexers
dbsession.flush()
namespace = RELAXNG_CIOWAREHOUSE['namespace']
if kwargs is None:
kwargs = {}
refs = kwargs.get('indexrefs') or INDEXFIELD_DEFAULTS
done = set()
for elt in handler_elt.xpath(
'ns0:indexer', namespaces={'ns0': namespace}):
ref = elt.get('indexref')
if ref not in done and ref in refs:
done.add(ref)
dbsession.add(DBHandlerIndexer(
handler_id=handler_id, indexfield_id=ref,
argument=elt.text.strip() if elt.text else None,
limit=int(elt.get('limit', '0')) or None))
return None
# -------------------------------------------------------------------------
[docs] @classmethod
def record_from_xml(cls, handler_id):
"""Convert a handler XML element into a dictionary.
:param str handler_id:
Handler ID.
:rtype: dict
"""
return {'handler_id': handler_id}
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
[docs] def db2xml(self, dbsession=None):
"""Serialize a file handler to a XML representation.
:type dbsession: sqlalchemy.orm.session.Session
:param dbsession: (optional)
SQLAlchemy session.
:rtype: lxml.etree.Element
"""
# pylint: disable = unused-argument
handler_elt = etree.Element('handler')
handler_elt.set('id', self.handler_id)
for dbindexer in self.indexers:
elt = etree.SubElement(
handler_elt, 'indexer', indexref=dbindexer.indexfield_id)
if dbindexer.limit:
elt.set('limit', str(dbindexer.limit))
if dbindexer.argument:
elt.text = dbindexer.argument
return handler_elt
# =============================================================================
[docs]class DBHandlerIndexer(DBDeclarativeClass):
"""Class to link handlers with their indexers (one-to-many)."""
# pylint: disable = too-few-public-methods
__tablename__ = 'wrh_handlers_indexers'
__table_args__ = {'mysql_engine': 'InnoDB'}
handler_id = Column(
String(ID_LEN),
ForeignKey('wrh_handlers.handler_id', ondelete='CASCADE'),
primary_key=True)
indexfield_id = Column(String(ID_LEN), primary_key=True)
argument = Column(String(255))
limit = Column(Integer())