Source code for data2neo.core.factories.factory_wrappers

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Factory Wrappers that allow to insert functionality before or after a factory. Allows generic extension of functionality.
A Factory Wrapper itself can be wrapped in a Wrapper.

authors: Julian Minder
"""

from abc import ABC, abstractmethod
from typing import Callable


from .resource import Resource
from ...neo4j.graph_elements import Attribute, GraphElement, Subgraph
from .factory import AttributeFactory, Factory, SubgraphFactory
from .registrar import register_factory


class FactoryWrapper(Factory, ABC):
    """Abstract FactoryWrapper that allows to insert pre and postprocessor functions that are called before and after 
    .construct of the wrapped factory. 
    
    Attributes:
        factory: The wrapped factory
    """
    @abstractmethod
    def __init__(self, factory: Factory, 
                 preprocessor: Callable[[Resource], Resource] = None,
                 postprocessor: Callable[[GraphElement], GraphElement] = None) -> None:
        """Inits the FactoryWrapper with an factory and a post and preprocessor
        
        Args:
            factory: The factory that should be wrapped
            preprocessor: A callable that takes a Resource, processes it and returns a resource (which is then passed to the factory). (default: None) 
            postprocessor: A callabel that takes the GraphElement, processes it and returns another GraphElement. (default: None) 
        """
        self._factory = factory
        self._preprocessor = preprocessor
        self._postprocessor = postprocessor

    @property
    def factory(self) -> Factory:
        """The wrapped factory"""
        return self._factory

    @abstractmethod
    def construct(self, resource: Resource) -> GraphElement:
        """Runs the preprocessor on the resource, uses the factory to construct an GraphElement from the resource
        and runs the postprocessor on this GraphElement

        Args:
            resource: A Resource containing any information needed for the construction       
        """
        processed_resource = resource
        if self._preprocessor is not None:
            processed_resource = self._preprocessor(resource)
        graph_element =  self.factory.construct(processed_resource)
        if self._postprocessor is not None:
            graph_element = self._postprocessor(graph_element)
        return graph_element
    

[docs]@register_factory class SubgraphFactoryWrapper(FactoryWrapper, SubgraphFactory): """Factory Wrapper for any SubgraphFactory. Allows to insert pre and post processor functions that are called on the resource/subgraph respectivelly before and after the .construct function of the wrapped factory. This factory behaves like a normal SubgraphFactory and can be wrapped again. Attributes: factory: The wrapped SubgraphFactory """
[docs] def __init__(self, factory: SubgraphFactory, preprocessor: Callable[[Resource], Resource] = None, postprocessor: Callable[[Subgraph], Subgraph] = None, identifier: str = None) -> None: """Inits the SubGraphFactoryWrapper with an factory and a post and preprocessor Args: identifier: A string identifying this Factory instance, must be unique factory: The SubgraphFactory that should be wrapped preprocessor: A callable that takes a Resource, processes it and returns a resource (which is then passed to the factory). (default: None) postprocessor: A callabel that takes the Subgraph, processes it and returns another Subgraph. (default: None) identifier: A string identifying this Factory instance, must be unique. Can be None if factory doesn't need to save unique supplies """ # super will call the First Parent class (first element in mro) -> FactoryWrapper init FactoryWrapper.__init__(self, factory, preprocessor=preprocessor, postprocessor=postprocessor) SubgraphFactory.__init__(self, identifier if identifier is not None else factory.id)
[docs] def construct(self, resource: Resource) -> Subgraph: """Runs the preprocessor on the resource, uses the factory to construct an SubGraph from the resource and runs the postprocessor on this SubGraph. If resource is None then this method returns an emtpy SubGraph Args: resource: A Resource containing any information needed for the construction """ if resource is None: return Subgraph() return FactoryWrapper.construct(self, resource)
[docs]@register_factory class AttributeFactoryWrapper(FactoryWrapper, AttributeFactory): """Factory Wrapper for any AttributeFactory. Allows to insert pre and post processor functions that are called on the resource/attribute respectivelly before and after the .construct function of the wrapped factory. This factory behaves like a normal AttributeFactory and can be wrapped again. Attributes: identifier: A string identifying this Factory instance, must be unique factory: The wrapped AttributeFactory attribute_key: The key that any produced Attribute has. static_attribute_value: If this is set, then any attribute produced by this factory will have this string as its value entity_attribute: A key of an attribute of the expected resource entity. """
[docs] def __init__(self, factory: AttributeFactory, preprocessor: Callable[[Resource], Resource] = None, postprocessor: Callable[[Attribute], Attribute] = None, identifier: str = None) -> None: """Inits the AtttributeFactoryWrapper with an factory and a post and preprocessor Args: factory: The AttributeFactory that should be wrapped preprocessor: A callable that takes a Resource, processes it and returns a resource (which is then passed to the factory). (default: None) postprocessor: A callabel that takes the Attribute, processes it and returns another Attribute. (default: None) identifier: A string identifying this Factory instance, must be unique. Can be None if factory doesn't need to save unique supplies """ FactoryWrapper.__init__(self, factory, preprocessor=preprocessor, postprocessor=postprocessor) AttributeFactory.__init__(self, self.factory.attribute_key, self.factory.entity_attribute, self.factory.static_attribute_value, identifier if identifier is not None else factory.id)
[docs] def construct(self, resource: Resource) -> Attribute: """Runs the preprocessor on the resource, uses the factory to construct an Attribute from the resource and runs the postprocessor on this Attribute. If resource is None then this method returns None Args: resource: A Resource containing any information needed for the construction """ if resource is None: return None return FactoryWrapper.construct(self, resource)