コード例 #1
0
def get_datatype_constraints(onto_class_uri, triples):
    '''
    Build and return a DatatypeConstraints object for the specified list of triples

    Arguments:
        onto_class_uri  An rdflib.term.URIRef for the source ontology class
        triples         A list of triples from an ontospy.core.entities.OntoClass (e.g. onto_class.triples)

    Return:
        A tuple (DatatypeConstraints, LIST of ErrorMessage objects)
    '''
    # Convert list of triples to {subject:{predicate:{objects}}}
    spo_dict = get_spo_dict(triples)

    # Get parent_po_dict.  It's the only one whose key is not a BNode
    non_bnode_keys = [
        key for key in spo_dict.keys()
        if not isinstance(key, rdflib.term.BNode)
    ]
    if not non_bnode_keys:
        errmsg = OntologyError(message='No parent node in triples: {}'.format(
            pprint.pformat(triples)),
                               onto_class_uri=onto_class_uri)
        return None, [errmsg]
    elif len(non_bnode_keys) > 1:
        errmsg = OntologyError(
            message='Multiple parent nodes in triples: {}'.format(
                pprint.pformat(triples)),
            onto_class_uri=onto_class_uri)
        return None, [errmsg]
    else:
        parent_po_dict = spo_dict[non_bnode_keys[0]]

    # Proceed depending on database constraint type

    # Case 1: List of strings
    if parent_po_dict.get(RDFS.subClassOf) == {
            RDFS.Resource
    } and parent_po_dict.get(OWL.oneOf):
        try:
            datatype_constraints = VocabularyDatatypeConstraints(
                onto_class_uri, spo_dict)
            errmsgs = []
        except DatatypeError as exc:
            datatype_constraints = None
            errmsgs = exc.error_messages

    # Case 2: TBD

    # Otherwise
    else:
        datatype_constraints = None
        errmsg = UnsupportedFeature(
            message='Cannot figure out datatype for these triples:\n{}'.format(
                pprint.pformat(triples)),
            onto_class_uri=onto_class_uri)
        errmsgs = [errmsg]

    # Return results
    return datatype_constraints, errmsgs
コード例 #2
0
def _get_property_ranges(ontospy_property_ranges, ontospy_property_triples,
                         context):
    '''
    This function belongs in the Ontology class.
    It is implemented outside the Ontology class to facilitate unit testing

    Compute the range for all properties in the ontology.
    Add error message for each property with no or multiple ranges,
       and omit those from the returned property_ranges

    Arguments:
        ontospy_property_ranges   {property_uri:[range_uris]}
        ontospy_property_triples  {property_uri:[triples]}
        context                   Ontology Context object

    Return:
        property_ranges  {property_uri:range_uri|None}
        errmsgs          List of error messages
    '''
    # Start with empty list of error messages
    error_messages = []
    property_ranges = {}

    # Do for all properties in ontospy.all_properties
    for property_uri, range_uris in ontospy_property_ranges.items():
        property_ranges[property_uri] = None

        # If ontospy thinks there are no ranges for this property...
        if not range_uris:

            # If the property has a triple with predicate RDFS.range, this is an unsupported feature
            if RDFS.range in [
                    pred for subj, pred, obj in
                    ontospy_property_triples[property_uri]
            ]:
                error_messages.append(
                    UnsupportedFeature(
                        message='Property has unsupported property type',
                        property_uri=property_uri))
            else:
                error_messages.append(
                    OntologyError(message='Property has no ranges',
                                  property_uri=property_uri))
            continue

        # If ontospy thinks there multiple ranges for this property, error message
        if len(range_uris) > 1:
            error_messages.append(
                OntologyError(message='Property has {} ranges {}'.format(
                    len(range_uris), context.format(range_uris)),
                              property_uri=property_uri))
            continue

        # If we are here, there is one range.  Add it to return value
        property_ranges[property_uri] = range_uris[0]

    # Return property_ranges and error messages
    return property_ranges, error_messages
 def _get_ontology_error(self, message):
     '''
     Arguments:
         message  A message string describing some kind of error condition
     Return:
         An OntologyError object using self.onto_class_uri and self.property_uri with the message
     '''
     return OntologyError(message='constraint violation: ' + message,
                          onto_class_uri=self.onto_class_uri,
                          property_uri=self.property_uri)
コード例 #4
0
    def __init__(self, onto_class_uri, spo_dict):
        '''
        Create and initialize an instance of this class

        Arguments:
            onto_class_uri   The ontology class that is this Datatype
            spo_dict         Ontology {subject:{predicate:{value})}

        Raise:
            DatatypeError([ErrorMessage]): an Exception containing a LIST of ErrorMessages

        Algorithm:
            Naively assume the BNodes form a simple linked list, where the object
            of every RDF.first predicate is a Literal.  You don't have to follow the links
            in order, just catch all the RDF.firsts.
        '''
        super().__init__(onto_class_uri)
        errmsgs = []
        self.vocabulary = set()
        for subject, po_dict in spo_dict.items():
            if not isinstance(subject, rdflib.term.BNode):
                continue
            literals = po_dict.get(RDF.first)
            if len(literals) > 1:
                errmsgs.append(
                    OntologyError(
                        message=
                        'Multiple values for RDF.first in linked list: {}'.
                        format(literals),
                        onto_class_uri=onto_class_uri))
            elif literals:
                self.vocabulary.update([str(literal) for literal in literals
                                        ])  # nondestructive

        if errmsgs:
            raise DatatypeError(errmsgs)
コード例 #5
0
def get_class_constraints(onto_class_uri, triples):
    '''
    Build and return a ClassConstraints object for the specified list of triples

    Arguments:
        onto_class_uri   A rdflib.term.URIRef of the source ontology class (used ONLY for building messages)
        triples          A list of triples from an ontospy.core.entities.OntoClass (e.g. onto_class.triples)

    Return:
        A tuple (ClassConstraints object, LIST of ErrorMessage objects)
    '''
    # Create empty ClassConstraints object and empty list of ErrorMessages
    class_constraints = ClassConstraints(onto_class_uri)
    error_messages = []

    # Convert list of triples to {subject:{predicate:{objects}}}
    spo_dict = get_spo_dict(triples)

    # Do for each node...
    for subject, po_dict in spo_dict.items():

        # Skip this node if it is the parent node
        if not isinstance(subject, rdflib.term.BNode):
            continue

        # If we're here, then subject is a BNode
        # Remove and report predicates with multiple objects for this BNode
        # Sets of a single object by that single object.
        for predicate in set(po_dict.keys()):
            objects = po_dict[predicate]
            if len(objects) > 1:
                error_messages.append(OntologyError(
                    message = 'conflicting specification of {} as {}'.format(predicate, objects),
                    onto_class_uri = onto_class_uri,
                    property_uri = po_dict.get(OWL.onProperty)))
                po_dict.pop(predicate)          # remove predicate and its multiple object from dictionary
            else:
                po_dict[predicate] = objects.pop()  # replace set of one object by the one object


        # Now po_dict maps predicates to single objects: {predicate:object}
        # Get RDF.type and onProperty for this BNode
        constraints_type = po_dict.get(RDF.type)
        property_uri = po_dict.get(OWL.onProperty)

        # Skip this BNode if its RDF.type is not OWL.Restriction
        if constraints_type != OWL.Restriction:
            error_messages.append(UnsupportedFeature(
                onto_class_uri = onto_class_uri,
                property_uri = property_uri,
                message='unsupported constraints type {}'.format(constraints_type)))
            continue

        # Skip this BNode if its OWL.onProperty is missing
        if not property_uri:
            error_messages.append(OntologyError(
                onto_class_uri = onto_class_uri,
                message='constraints has no property value'))
            continue

        # Create empty PropertyConstraints object and attach to property in Constraints object
        property_constraints = PropertyConstraints(onto_class_uri, property_uri)
        class_constraints.set_property_constraints(property_uri, property_constraints)

        # Do for each predicate
        for predicate, obj in po_dict.items():

            # Skip RDF.type and OWL.onProperty because we already accounted for them
            if predicate in (RDF.type, OWL.onProperty):
                continue

            # Populate property_constraints depending on the predicate and its value
            if predicate == OWL.onDataRange:
                error_messages.extend(property_constraints.add_value_range(obj))
            elif predicate == OWL.onClass:
                error_messages.extend(property_constraints.add_value_range(obj))
            elif predicate == OWL.minCardinality:
                error_messages.extend(property_constraints.add_min_cardinality(int(obj.value)))
            elif predicate == OWL.maxCardinality:
                error_messages.extend(property_constraints.add_max_cardinality(int(obj.value)))
            elif predicate == OWL.cardinality:
                error_messages.extend(property_constraints.add_cardinality(int(obj.value)))
            elif predicate == OWL.minQualifiedCardinality:
                error_messages.extend(property_constraints.add_qualified_min_cardinality(int(obj.value)))
            elif predicate == OWL.maxQualifiedCardinality:
                error_messages.extend(property_constraints.add_qualified_max_cardinality(int(obj.value)))
            elif predicate == OWL.qualifiedCardinality:
                error_messages.extend(property_constraints.add_qualified_cardinality(int(obj.value)))
            else:
                error_messages.append(UnsupportedFeature(
                    message = 'unsupported predicate {}'.format(predicate),
                    onto_class_uri = onto_class_uri,
                    property_uri = property_uri))

        # Check property constraints for consistency
        error_messages.extend(property_constraints.check_consistency())

    # Return the Constraints object and the ErrorMessages
    return class_constraints, error_messages
コード例 #6
0
def _check_range_consistency(all_constraints, property_ranges, context):
    '''
    This function belongs in the Ontology class.
    It is implemented outside the Ontology class to facilitate unit testing

    Make sure the property range constraints are consistent with the
    property ranges directly specified in the *-da files.

    Arguments:
        all_constraints   {URIRef(onto_class):ClassConstraints|DatatypeConstraints|None} (ontology.constraints)
        property_ranges   {property_uri:range_uri|None}    (derived from ontospy.all_properties)
        context           Ontology Context object

    Return:
        List of ErrorMessage objects
    '''
    # Start with empty list of error messages
    error_messages = []

    # Do for all owl property constraints
    for onto_class_uri, constraints in all_constraints.items():

        # We are only checking ClassConstraints for now.
        if not isinstance(constraints, ClassConstraints):
            #error_messages.append(UnsupportedFeature(
            #    message='range consistency check not implemented for {}'.format(type(constraints)),
            #    onto_class_uri=onto_class_uri))
            continue

        # For each property constraint in the class constrain...
        class_constraints = constraints
        for property_uri, property_constraints in class_constraints.property_constraints_dict.items(
        ):

            # If property has no range constraint, nothing to check
            if property_constraints.value_range is None:
                continue

            # Make sure property is in onto_property_ranges
            if not property_uri in property_ranges:
                error_messages.append(
                    OntologyError(
                        message='property {} missing from ontology property list'
                        .format(context.format(property_uri)),
                        onto_class_uri=onto_class_uri,
                        property_uri=property_uri))
                continue

            # Get ontospy's opinion of this property's range constraint
            range_uri = property_ranges.get(property_uri)  # could be None

            # Compare the owl property constraint with ontospy's opinion
            if range_uri and range_uri != property_constraints.value_range:
                error_messages.append(
                    OntologyError(
                        message=
                        'owl subclass constraint {} does not match property constraint {}'
                        .format(
                            context.format(property_constraints.value_range),
                            context.format(range_uri)),
                        onto_class_uri=onto_class_uri,
                        property_uri=property_uri))

    return error_messages