public function run() { cecho("\n\n"); cecho("Data validation test: " . $this->description . "...\n\n", 'LIGHT_BLUE'); $sparql = new SparqlQuery($this->network); $from = ''; foreach ($this->checkOnDatasets as $dataset) { $from .= 'from named <' . $dataset . '> '; } foreach ($this->checkUsingOntologies as $ontology) { $from .= 'from <' . $ontology . '> '; } // Get the list of all the object properties used within the datasets $sparql->mime("application/sparql-results+json")->query('select distinct ?p ?range ' . $from . ' where { graph ?g { ?s ?p ?o . filter(isIRI(?o)) } optional { ?p <http://www.w3.org/2000/01/rdf-schema#range> ?range . } filter(str(?p) != "http://purl.org/dc/terms/isPartOf" && str(?p) != "http://www.w3.org/1999/02/22-rdf-syntax-ns#value" && str(?p) != "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") }')->send(); if ($sparql->isSuccessful()) { $results = json_decode($sparql->getResultset(), TRUE); if (isset($results['results']['bindings']) && count($results['results']['bindings']) > 0) { $objectProperties = array(); $thereAreEmptyRanges = FALSE; foreach ($results['results']['bindings'] as $result) { $objectProperty = $result['p']['value']; $objectProperties[$objectProperty] = ''; if (isset($result['range'])) { $objectProperties[$objectProperty] = $result['range']['value']; } else { $thereAreEmptyRanges = TRUE; } } // Display warnings if ($thereAreEmptyRanges) { cecho("The following object properties are used to describe records, but their range is not specified in the ontologies. owl:Thing is assumed as the range, but you may want to define it further and re-run this check:\n", 'YELLOW'); foreach ($objectProperties as $objectProperty => $range) { if (empty($range)) { cecho(' -> object property: ' . $objectProperty . "\n", 'YELLOW'); $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-50', 'type' => 'warning', 'objectProperty' => $objectProperty); } } } // Now, for each object properties that have a range defined, // we: // // (a) List all values used for a given property // (b) For each of these values, make sure they comply with what is defined in the ontology as // the range of the foreach ($objectProperties as $objectProperty => $range) { // If the range is empty, we consider it owl:Thing. // If the range is owl:Thing, then we simply skip this check since everything is an owl:Thing if (!empty($range) && $range != 'http://www.w3.org/2002/07/owl#Thing') { $values = array(); $sparql = new SparqlQuery($this->network); $from = ''; foreach ($this->checkOnDatasets as $dataset) { $from .= 'from <' . $dataset . '> '; $from .= 'from named <' . $dataset . '> '; } foreach ($this->checkUsingOntologies as $ontology) { $from .= 'from <' . $ontology . '> '; } $sparql->mime("application/sparql-results+json")->query('select distinct ?value ?value_type ' . $from . ' where { graph ?g { ?s <' . $objectProperty . '> ?value. filter(isIRI(?value)) } optional { ?value a ?value_type } }')->send(); if ($sparql->isSuccessful()) { // Create the array of object-values/types $results = json_decode($sparql->getResultset(), TRUE); if (isset($results['results']['bindings']) && count($results['results']['bindings']) > 0) { foreach ($results['results']['bindings'] as $result) { $value = $result['value']['value']; $type = ''; if (isset($result['value_type'])) { $type = $result['value_type']['value']; } if (!isset($values[$value])) { $values[$value] = array(); } if (!empty($type) && !in_array($type, $values[$value])) { $values[$value][] = $type; } } } // For each value/type(s), we do validate that the range is valid foreach ($values as $value => $types) { // First, check if we have a type defined for the value. If not, then we infer it is owl:Thing if (empty($types)) { $types = array('http://www.w3.org/2002/07/owl#Thing'); } // Then, check if the $range and the $types directly match if (in_array($range, $types)) { continue; } else { // If they are not, then we check in the ontology to see if the range is not // one of the super class of one of the type(s) $superClasses = array(); foreach ($types as $type) { $ontologyURI = $this->getTypeOntology($type); if ($ontologyURI !== FALSE) { $ontologyRead = new OntologyReadQuery($this->network); $getSuperClassesFunc = new GetSuperClassesFunction(); $getSuperClassesFunc->allSuperClasses()->getClassesUris()->uri($type); $ontologyRead->enableReasoner()->ontology($ontologyURI)->getSuperClasses($getSuperClassesFunc)->mime('resultset')->send(); if ($ontologyRead->isSuccessful()) { $scs = $ontologyRead->getResultset()->getResultset(); // If empty, then there is no super-classes if (!empty($scs)) { $scs = $scs[key($scs)]; foreach ($scs as $superClass => $description) { if (!in_array($superClass, $superClasses)) { $superClasses[] = $superClass; } } } } else { cecho("We couldn't get the list of super-classes of a target type from the structWSF instance\n", 'YELLOW'); // Log a warning // Can't get the super classes of the target type $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-51', 'type' => 'warning', 'objectProperty' => $objectProperty); } } else { cecho("We couldn't find the ontology where the {$type} is defined on the structWSF instance\n", 'YELLOW'); // Log a warning // Can't find ontology where the type $type is defined $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-52', 'type' => 'warning', 'objectProperty' => $objectProperty); } } $rangeMatch = FALSE; foreach ($superClasses as $superClass) { if ($superClass == $range) { $rangeMatch = TRUE; break; } } if (!$rangeMatch) { // Log an error // Couldn't match one of the super classe with the specified range cecho(' -> Object property "' . $objectProperty . '" doesn\'t match range "' . $range . '" for value "' . $value . '"' . "\n", 'LIGHT_RED'); $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-100', 'type' => 'error', 'objectProperty' => $objectProperty, 'definedRange' => $range, 'value' => $value, 'valueTypes' => $types, 'valueSuperTypes' => $superClasses, 'affectedRecords' => $this->getAffectedRecords($objectProperty, $value)); } } } } else { cecho("We couldn't get the range of the {$objectProperty} object property from the structWSF instance\n", 'YELLOW'); $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-54', 'type' => 'warning'); } } } } } else { cecho("We couldn't get the list of object properties from the structWSF instance\n", 'YELLOW'); $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-53', 'type' => 'warning'); } }