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)
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])))
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
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)
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))
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)
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)
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
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
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
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)
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)
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]))
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
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)
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
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
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'))
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)