def __init__(self, schema_graph=None, attr_extractor=None, oper_extractor=None, declare_default_operation_functions=True): self._attr_extractor = attr_extractor if attr_extractor is not None else OMAttributeExtractor() self._operation_extractor = oper_extractor if oper_extractor is not None else HydraOperationExtractor() self._schema_graph = schema_graph self._operation_functions = {} self._registry = ModelRegistry() self._logger = logging.getLogger(__name__) self._include_reversed_attributes = False # TODO: examine their relevance if declare_default_operation_functions: self.declare_operation_function(append_to_hydra_collection, HYDRA_COLLECTION_IRI, HTTP_POST) self.declare_operation_function(append_to_hydra_paged_collection, HYDRA_PAGED_COLLECTION_IRI, HTTP_POST)
class ModelManager(object): """ TODO: update this documentation The `model_manager` creates and registers :class:`~oldman.model.Model` objects. Internally, it owns a :class:`~oldman.resource.registry.ModelRegistry` object. :param schema_graph: :class:`rdflib.Graph` object containing all the schema triples. :param data_store: :class:`~oldman.store.datastore.DataStore` object. :param attr_extractor: :class:`~oldman.parsing.attribute.OMAttributeExtractor` object that will extract :class:`~oldman.attribute.OMAttribute` for generating new :class:`~oldman.model.Model` objects. Defaults to a new instance of :class:`~oldman.parsing.attribute.OMAttributeExtractor`. :param oper_extractor: TODO: describe. :param declare_default_operation_functions: TODO: describe. """ def __init__(self, schema_graph=None, attr_extractor=None, oper_extractor=None, declare_default_operation_functions=True): self._attr_extractor = attr_extractor if attr_extractor is not None else OMAttributeExtractor() self._operation_extractor = oper_extractor if oper_extractor is not None else HydraOperationExtractor() self._schema_graph = schema_graph self._operation_functions = {} self._registry = ModelRegistry() self._logger = logging.getLogger(__name__) self._include_reversed_attributes = False # TODO: examine their relevance if declare_default_operation_functions: self.declare_operation_function(append_to_hydra_collection, HYDRA_COLLECTION_IRI, HTTP_POST) self.declare_operation_function(append_to_hydra_paged_collection, HYDRA_PAGED_COLLECTION_IRI, HTTP_POST) # # Create "anonymous" models # if schema_graph is not None: # self._create_anonymous_models() @property def include_reversed_attributes(self): """Is `True` if at least one of its models use some reversed attributes.""" return self._include_reversed_attributes @property def models(self): """TODO: describe.""" return self._registry.models @property def non_default_models(self): """TODO: describe.""" return self._registry.non_default_models def has_default_model(self): return self._registry.default_model is not None def declare_operation_function(self, func, class_iri, http_method): """ TODO: comment """ if self._registry.has_specific_models(): raise OMExpiredMethodDeclarationTimeSlotError(u"Operation declaration cannot occur after model creation.") http_method = http_method.upper() if class_iri in self._operation_functions: if http_method in self._methods[class_iri]: self._logger.warn(u"Operation %s of %s is overloaded." % (http_method, class_iri)) self._operation_functions[class_iri][http_method] = func else: self._operation_functions[class_iri] = {http_method: func} def find_models_and_types(self, type_set): """See :func:`oldman.resource.registry.ModelRegistry.find_models_and_types`.""" return self._registry.find_models_and_types(type_set) def find_descendant_models(self, top_ancestor_name_or_iri): """TODO: explain. Includes the top ancestor. """ return self._registry.find_descendant_models(top_ancestor_name_or_iri) def create_model(self, class_name_or_iri, context_iri_or_payload, data_store, iri_prefix=None, iri_fragment=None, iri_generator=None, untyped=False, incremental_iri=False, is_default=False, context_file_path=None): """Creates a :class:`~oldman.model.Model` object. TODO: remove data_store from the constructor! To create it, they are three elements to consider: 1. Its class IRI which can be retrieved from `class_name_or_iri`; 2. Its JSON-LD context for mapping :class:`~oldman.attribute.OMAttribute` values to RDF triples; 3. The :class:`~oldman.iri.IriGenerator` object that generates IRIs from new :class:`~oldman.resource.Resource` objects. The :class:`~oldman.iri.IriGenerator` object is either: * directly given: `iri_generator`; * created from the parameters `iri_prefix`, `iri_fragment` and `incremental_iri`. :param class_name_or_iri: IRI or JSON-LD term of a RDFS class. :param context_iri_or_payload: `dict`, `list` or `IRI` that represents the JSON-LD context . :param iri_generator: :class:`~oldman.iri.IriGenerator` object. If given, other `iri_*` parameters are ignored. :param iri_prefix: Prefix of generated IRIs. Defaults to `None`. If is `None` and no `iri_generator` is given, a :class:`~oldman.iri.BlankNodeIriGenerator` is created. :param iri_fragment: IRI fragment that is added at the end of generated IRIs. For instance, `"me"` adds `"#me"` at the end of the new IRI. Defaults to `None`. Has no effect if `iri_prefix` is not given. :param incremental_iri: If `True` an :class:`~oldman.iri.IncrementalIriGenerator` is created instead of a :class:`~oldman.iri.RandomPrefixedIriGenerator`. Defaults to `False`. Has no effect if `iri_prefix` is not given. :param context_file_path: TODO: describe. """ # Only for the DefaultModel if untyped: class_iri = None ancestry = ClassAncestry(class_iri, self._schema_graph) om_attributes = {} else: context_file_path_or_payload = context_file_path if context_file_path is not None \ else context_iri_or_payload class_iri = _extract_class_iri(class_name_or_iri, context_file_path_or_payload) ancestry = ClassAncestry(class_iri, self._schema_graph) om_attributes = self._attr_extractor.extract(class_iri, ancestry.bottom_up, context_file_path_or_payload, self._schema_graph) if iri_generator is not None: id_generator = iri_generator elif iri_prefix is not None: if incremental_iri: id_generator = IncrementalIriGenerator(iri_prefix, data_store, class_iri, fragment=iri_fragment) else: id_generator = PrefixedUUIDIriGenerator(iri_prefix, fragment=iri_fragment) else: id_generator = BlankNodeIriGenerator() operations = self._operation_extractor.extract(ancestry, self._schema_graph, self._operation_functions) model = Model(class_name_or_iri, class_iri, ancestry.bottom_up, context_iri_or_payload, om_attributes, id_generator, operations=operations, local_context=context_file_path) self._add_model(model, is_default=is_default) # Reversed attributes awareness if not self._include_reversed_attributes: self._include_reversed_attributes = model.has_reversed_attributes # Anonymous classes derived from hydra:Link properties self._create_anonymous_models(model, context_file_path, data_store) return model def get_model(self, class_name_or_iri): return self._registry.get_model(class_name_or_iri) def _add_model(self, model, is_default=False): self._registry.register(model, is_default=is_default) def _create_anonymous_models(self, model, context_iri_or_payload, data_store): """ These classes are typically derived from hydra:Link. Their role is just to support some operations. """ classes = {attr.om_property.link_class_iri for attr in model.om_attributes.values()}.difference({None}) for cls_iri in classes: if self._registry.get_model(cls_iri) is None: self.create_model(cls_iri, context_iri_or_payload, data_store)