Exemple #1
0
    def generate(self):
        for parent in ensure_list(self.data.get('extends')):
            parent = self.model.get(parent)
            parent.generate()

            for name, prop in parent.properties.items():
                if name not in self.properties:
                    self.properties[name] = prop

            self.extends.add(parent)
            for ancestor in parent.schemata:
                self.schemata.add(ancestor)
                self.names.add(ancestor.name)
                ancestor.descendants.add(self)

        for prop in list(self.properties.values()):
            prop.generate()

        for featured in self.featured:
            if self.get(featured) is None:
                raise InvalidModel("Missing featured property: %s" % featured)

        for caption in self.caption:
            if self.get(caption) is None:
                raise InvalidModel("Missing caption property: %s" % caption)

        if self.edge:
            if self.get(self.edge_source) is None:
                msg = "Missing edge source: %s" % self.edge_source
                raise InvalidModel(msg)

            if self.get(self.edge_target) is None:
                msg = "Missing edge target: %s" % self.edge_target
                raise InvalidModel(msg)
Exemple #2
0
    def __init__(self, schema, name, data):
        self.schema = schema
        self.model = schema.model

        self.name = stringify(name)
        self.qname = '%s:%s' % (schema.name, self.name)
        if self.name in self.RESERVED:
            raise InvalidModel("Reserved name: %s" % self.name)

        self.data = data
        self._label = data.get('label', name)
        self._description = data.get('description')
        self.required = data.get('required', False)
        self.hidden = data.get('hidden', False)
        self.stub = data.get('stub', False)

        type_ = data.get('type', 'string')
        self.type = registry.get(type_)
        if self.type is None:
            raise InvalidModel("Invalid type: %s" % type_)

        self.matchable = data.get('matchable', self.type.matchable)
        self.range = None
        self.reverse = None
        self.uri = URIRef(data.get('rdf', NS[self.qname]))
    def __init__(self, schema: "Schema", name: str, data: PropertySpec) -> None:
        self.model = schema.model

        #: The schema which the property is defined for. This is always the
        #: most abstract schema that has this property, not the possible
        #: child schemata that inherit it.
        self.schema = schema

        #: Machine-readable name for this property.
        self.name = name

        #: Qualified property name, which also includes the schema name.
        self.qname = "%s:%s" % (schema.name, self.name)
        if self.name in self.RESERVED:
            raise InvalidModel("Reserved name: %s" % self.name)

        self._hash = hash("<Property(%r)>" % self.qname)

        self._label = data.get("label", name)
        self._description = data.get("description")

        #: This property should not be shown or mentioned in the user interface.
        self.hidden = as_bool(data.get("hidden", False))

        type_ = data.get("type", "string")
        if type_ is None or type_ not in registry.named:
            raise InvalidModel("Invalid type: %s" % type_)

        #: The data type for this property.
        self.type = registry[type_]

        #: Whether this property should be used for matching and cross-referencing.
        _matchable = data.get("matchable")
        if _matchable is not None:
            self.matchable = as_bool(data.get("matchable"))
        else:
            self.matchable = self.type.matchable

        #: If the property is of type ``entity``, the set of valid schema to be added
        #: in this property can be constrained. For example, an asset can be owned,
        #: but a person cannot be owned.
        self._range = data.get("range")
        self.range: Optional["Schema"] = None

        #: When a property points to another schema, a reverse property is added for
        #: various administrative reasons. These properties are, however, not real
        #: and cannot be written to. That's why they are marked as stubs and adding
        #: values to them will raise an exception.
        self.stub: Optional[bool] = False

        #: When a property points to another schema, a stub reverse property is
        #: added as a place to store metadata to help display the link in inverted
        #: views.
        self._reverse = data.get("reverse")
        self.reverse: Optional["Property"] = None

        #: RDF term for this property (i.e. the predicate URI).
        self.uri = URIRef(cast(str, data.get("rdf", NS[self.qname])))
Exemple #4
0
 def _add_edge(self, proxy: EntityProxy, source: str, target: str) -> None:
     if proxy.schema.source_prop is None:
         raise InvalidModel("Invalid edge entity: %r" % proxy)
     source_node = self._get_node_stub(proxy.schema.source_prop, source)
     if proxy.schema.target_prop is None:
         raise InvalidModel("Invalid edge entity: %r" % proxy)
     target_node = self._get_node_stub(proxy.schema.target_prop, target)
     edge = Edge(self, source_node, target_node, proxy=proxy)
     self.edges[edge.id] = edge
Exemple #5
0
    def generate(self):
        range_ = self.data.get('schema', 'Thing')
        if range_:
            self.range = self.schema.model.get(range_)
            if self.range is None:
                raise InvalidModel("Cannot find range: %s" % self._range)

        reverse_ = self.data.get('reverse')
        if self.range and reverse_:
            if not is_mapping(reverse_):
                raise InvalidModel("Invalid reverse: %s" % self)
            self.reverse = self.range._add_reverse(reverse_, self)
Exemple #6
0
    def precise_schema(self, left, right):
        """Select the most narrow of two schemata.

        When indexing data from a dataset, an entity may be declared as a
        LegalEntity in one query, and as a Person in another. This function
        will select the most specific of two schemata offered. In the example,
        that would be Person.
        """
        if left == right:
            return left
        lefts = self.get(left)
        if lefts is None:
            return right
        if right in lefts.names:
            return left

        rights = self.get(right)
        if rights is None:
            return left
        if left in rights.names:
            return right

        # Find a common ancestor:
        for left in lefts.names:
            for right in rights.names:
                if left == right:
                    return left

        raise InvalidModel("No common ancestor: %s and %s" % (left, right))
Exemple #7
0
    def generate(self):
        for prop in self._own_properties:
            prop.generate()

        for featured in self.featured:
            if self.get(featured) is None:
                raise InvalidModel("Missing featured property: %s" % featured)
Exemple #8
0
 def _load(self, filepath):
     with open(filepath, 'r') as fh:
         data = yaml.safe_load(fh)
         if not isinstance(data, dict):
             raise InvalidModel('Model file is not a mapping.')
         for name, config in data.items():
             self.schemata[name] = Schema(self, name, config)
Exemple #9
0
 def extends(self):
     """Return the inherited schemata."""
     for base in self._extends:
         basecls = self._model.get(base)
         if basecls is None:
             raise InvalidModel("No such schema: %s" % base)
         yield basecls
Exemple #10
0
 def type_name(self) -> str:
     """Return a machine-readable descripton of the type of the edge.
     This is either a property name or a schema name."""
     if self.schema is not None:
         return self.schema.name
     if self.prop is None:
         raise InvalidModel("Invalid edge: %r" % self)
     return self.prop.name
Exemple #11
0
 def source_prop(self) -> Property:
     """Get the entity property originating this edge."""
     if self.schema is not None and self.schema.source_prop is not None:
         if self.schema.source_prop.reverse is not None:
             return self.schema.source_prop.reverse
     if self.prop is None:
         raise InvalidModel("Contradiction: %r" % self)
     return self.prop
Exemple #12
0
 def _load(self, filepath: str) -> None:
     with open(filepath, "r", encoding="utf-8") as fh:
         data = yaml.safe_load(fh)
         if not isinstance(data, dict):
             raise InvalidModel("Model file is not a mapping: %s" %
                                filepath)
         for name, config in data.items():
             self.schemata[name] = Schema(self, name, config)
    def generate(self) -> None:
        """While loading the schema, this function will validate and
        load the hierarchy, properties, and flags of the definition."""
        for extends in self._extends:
            parent = self.model.get(extends)
            if parent is None:
                raise InvalidData("Invalid extends: %r" % extends)
            parent.generate()

            for name, prop in parent.properties.items():
                if name not in self.properties:
                    self.properties[name] = prop

            self.extends.add(parent)
            for ancestor in parent.schemata:
                self.schemata.add(ancestor)
                self.names.add(ancestor.name)
                ancestor.descendants.add(self)

        for prop in list(self.properties.values()):
            prop.generate()

        for featured in self.featured:
            if self.get(featured) is None:
                raise InvalidModel("Missing featured property: %s" % featured)

        for caption in self.caption:
            if self.get(caption) is None:
                raise InvalidModel("Missing caption property: %s" % caption)

        for required in self.required:
            if self.get(required) is None:
                raise InvalidModel("Missing required property: %s" % required)

        if self.edge:
            if self.source_prop is None:
                msg = "Missing edge source: %s" % self.edge_source
                raise InvalidModel(msg)

            if self.target_prop is None:
                msg = "Missing edge target: %s" % self.edge_target
                raise InvalidModel(msg)
Exemple #14
0
    def generate(self):
        self.model.properties.add(self)

        if self.range is None and self.type == registry.entity:
            self.range = self.model.get(self.data.get('range'))

        reverse_ = self.data.get('reverse')
        if self.reverse is None and self.range and reverse_:
            if not is_mapping(reverse_):
                raise InvalidModel("Invalid reverse: %s" % self)
            self.reverse = self.range._add_reverse(reverse_, self)
    def generate(self):
        """Setup method used when loading the model in order to build out the reverse
        links of the property."""
        self.model.properties.add(self)

        if self.range is None and self.type == registry.entity:
            self.range = self.model.get(self.data.get("range"))

        reverse_ = self.data.get("reverse")
        if self.reverse is None and self.range and reverse_:
            if not is_mapping(reverse_):
                raise InvalidModel("Invalid reverse: %s" % self)
            self.reverse = self.range._add_reverse(reverse_, self)
    def generate(self) -> None:
        """Setup method used when loading the model in order to build out the reverse
        links of the property."""
        self.model.properties.add(self)

        if self.type == registry.entity:
            if self.range is None and self._range is not None:
                self.range = self.model.get(self._range)

            if self.reverse is None and self.range and self._reverse:
                if not is_mapping(self._reverse):
                    raise InvalidModel("Invalid reverse: %s" % self)
                self.reverse = self.range._add_reverse(self._reverse, self)
Exemple #17
0
    def __init__(self, schema, name, data):
        self.schema = schema
        self.model = schema.model

        self.name = stringify(name)
        self.qname = "%s:%s" % (schema.name, self.name)
        if self.name in self.RESERVED:
            raise InvalidModel("Reserved name: %s" % self.name)

        self.data = data
        self._label = data.get("label", name)
        self._description = data.get("description")
        self.hidden = data.get("hidden", False)
        self.stub = data.get("stub", False)

        type_ = data.get("type", "string")
        self.type = registry.get(type_)
        if self.type is None:
            raise InvalidModel("Invalid type: %s" % type_)

        self.matchable = data.get("matchable", self.type.matchable)
        self.range = None
        self.reverse = None
        self.uri = URIRef(data.get("rdf", NS[self.qname]))
Exemple #18
0
    def _add_reverse(self, data, other):
        name = data.get('name', None)
        if name is None:
            raise InvalidModel("Unnamed reverse: %s" % other)

        prop = self.get(name)
        if prop is None:
            data.update({
                'type': registry.entity.name,
                'reverse': {
                    'name': other.name
                },
                'schema': other.schema.name,
                'stub': True
            })
            prop = Property(self, name, data)
            prop.generate()
        return prop
Exemple #19
0
 def __init__(self, schema, name, data):
     self.schema = schema
     self.name = name.strip()
     self.qname = '%s:%s' % (schema.name, self.name)
     self.data = data
     self.label = data.get('label', name)
     self.hidden = data.get('hidden', False)
     self.required = data.get('required', False)
     self.is_multiple = data.get('multiple', False)
     self.type_name = data.get('type', 'string')
     self.range = data.get('schema', 'Thing')
     self.is_country = self.type_name == 'country'
     self.is_entity = self.type_name == 'entity'
     try:
         self.type = TYPES[self.type_name].type
         self.invert = TYPES[self.type_name].invert
     except KeyError:
         raise InvalidModel("Invalid type: %s" % self.type_name)
Exemple #20
0
    def _add_reverse(self, data, other):
        name = data.pop('name', None)
        if name is None:
            raise InvalidModel("Unnamed reverse: %s" % other)

        prop = self.get(name)
        if prop is None:
            data.update({
                'type': 'entity',
                'reverse': {'name': other.name},
                'schema': other.schema.name
            })
            prop = Property(self, name, data, stub=True)
            prop.generate()
            self._own_properties.append(prop)
            self._flush_properties()
        assert prop.type == registry.entity, prop.type
        return prop
Exemple #21
0
    def _add_reverse(self, data: ReverseSpec, other: Property) -> Property:
        name = data.get("name")
        if name is None:
            raise InvalidModel("Unnamed reverse: %s" % other)

        prop = self.get(name)
        if prop is None:
            spec: PropertySpec = {
                "label": data.get("label"),
                "type": registry.entity.name,
                "reverse": {"name": other.name},
                "range": other.schema.name,
                "hidden": data.get("hidden", other.hidden),
            }
            prop = Property(self, name, spec)
            prop.stub = True
            prop.generate()
            self.properties[name] = prop
        return prop
    def _add_reverse(self, data, other):
        name = data.get("name", None)
        if name is None:
            raise InvalidModel("Unnamed reverse: %s" % other)

        prop = self.get(name)
        if prop is None:
            data.update({
                "type": registry.entity.name,
                "reverse": {
                    "name": other.name
                },
                "range": other.schema.name,
                "stub": True,
            })
            data["hidden"] = data.get("hidden", other.hidden)
            prop = Property(self, name, data)
            prop.generate()
            self.properties[name] = prop
        return prop
Exemple #23
0
    def __init__(self, schema, name, data, stub=False):
        self.schema = schema
        self.name = name.strip()
        self.qname = '%s:%s' % (schema.name, self.name)
        self.data = data
        self._label = data.get('label', name)
        self._description = data.get('description')
        self.caption = data.get('caption', False)
        self.required = data.get('required', False)
        self._type = data.get('type', 'text')
        self.type = registry.get(self._type)
        if self.type is None:
            raise InvalidModel("Invalid type: %s" % self._type)

        self.range = None
        self.reverse = None
        self.stub = stub

        self.uri = NAMESPACE[self.qname]
        if 'rdf' in data:
            self.uri = URIRef(data.get('rdf'))
Exemple #24
0
    def generate(self):
        for parent in ensure_list(self.data.get('extends')):
            parent = self.model.get(parent)
            parent.generate()

            for name, prop in parent.properties.items():
                if name not in self.properties:
                    self.properties[name] = prop

            self.extends.add(parent)
            for ancestor in parent.schemata:
                self.schemata.add(ancestor)
                self.names.add(ancestor.name)
                ancestor.descendants.add(self)

        for prop in self.properties.values():
            prop.generate()

        for featured in self.featured:
            if self.get(featured) is None:
                raise InvalidModel("Missing featured property: %s" % featured)