@foerderfunke/matching-engine
Advanced tools
Comparing version 0.7.0 to 0.7.1
@@ -12,3 +12,4 @@ import { | ||
extractDatafieldsMetadata, | ||
convertUserProfileToTurtle | ||
convertUserProfileToTurtle, | ||
getAllTriplesContainingUri | ||
} from "./src/utils.js"; | ||
@@ -35,3 +36,4 @@ | ||
transformRulesFromRequirementProfile, | ||
getAllTriplesContainingUri, | ||
RuleType | ||
} |
{ | ||
"name": "@foerderfunke/matching-engine", | ||
"version": "0.7.0", | ||
"version": "0.7.1", | ||
"description": "Checks eligibilities by validating a user profile against requirement profiles", | ||
@@ -5,0 +5,0 @@ "author": "@foerderfunke", |
@@ -5,3 +5,3 @@ # matching-engine | ||
In use in the [foerderfunke-app](https://github.com/Citizen-Knowledge-Graph/foerderfunke-app). | ||
In use in the [FörderFunke app](https://github.com/Citizen-Knowledge-Graph/foerderfunke-react-app). | ||
@@ -8,0 +8,0 @@ ## Local development |
@@ -13,3 +13,3 @@ import { | ||
getDeferments, | ||
rdfStringToStore | ||
rdfStringToStore, getModifiableDatafields | ||
} from "./utils.js" | ||
@@ -21,2 +21,3 @@ import { Store } from "n3" | ||
INELIGIBLE: "ineligible", | ||
INELIGIBLE_RECTIFIABLE: "ineligible_rectifiable", | ||
UNDETERMINABLE: "undeterminable" | ||
@@ -126,2 +127,31 @@ } | ||
export async function validateAllUserProfilesAgainstOneRp(userProfileStrMap, rpStr, datafieldsStr, materializationStr, debug = false) { | ||
let rpUri = await extractRpUriFromRpString(rpStr) | ||
let result = { | ||
rpUri: rpUri, | ||
[ValidationResult.ELIGIBLE]: [], | ||
[ValidationResult.INELIGIBLE]: [], | ||
[ValidationResult.UNDETERMINABLE]: [], | ||
[ValidationResult.INELIGIBLE_RECTIFIABLE]: [], | ||
} | ||
for (let [filename, userProfileStr] of Object.entries(userProfileStrMap)) { | ||
let report = await validateOne(userProfileStr, rpStr, datafieldsStr, materializationStr, debug) | ||
switch (report.result) { | ||
case ValidationResult.ELIGIBLE: | ||
result[ValidationResult.ELIGIBLE].push(filename) | ||
break | ||
case ValidationResult.INELIGIBLE: | ||
result[ValidationResult.INELIGIBLE].push(filename) | ||
break | ||
case ValidationResult.UNDETERMINABLE: | ||
result[ValidationResult.UNDETERMINABLE].push(filename) | ||
break | ||
case ValidationResult.INELIGIBLE_RECTIFIABLE: | ||
result[ValidationResult.INELIGIBLE_RECTIFIABLE].push(filename) | ||
break | ||
} | ||
} | ||
return result | ||
} | ||
export async function validateOne(userProfile, requirementProfile, datafieldsStr, materializationStr, debug = false) { | ||
@@ -153,3 +183,5 @@ | ||
let violations = collectViolations(validationReport, true) | ||
const modifiableDFs = await getModifiableDatafields(store) | ||
let [violations, rectifiableViolations] = collectViolations(validationReport, true, modifiableDFs) | ||
if (violations.length > 0) { | ||
@@ -166,2 +198,13 @@ delete materializationReport.spPairs | ||
if (rectifiableViolations.length > 0) { | ||
delete materializationReport.spPairs | ||
return { | ||
result: ValidationResult.INELIGIBLE_RECTIFIABLE, | ||
violations: rectifiableViolations, | ||
missingUserInput: [], | ||
materializationReport: materializationReport, | ||
containsDeferredMissingUserInput: containsDeferredMissingUserInput | ||
} | ||
} | ||
// ----- collect missing data points ----- | ||
@@ -228,10 +271,11 @@ let missingList = {} | ||
function collectViolations(report, skipMinCountAndNode) { | ||
function collectViolations(report, skipMinCountAndNode, modifiableDFs) { | ||
// ignore HasValueConstraintComponent if they have an equivalent MinCountConstraintComponent? | ||
// the problem of them both occurring can be avoided by using e.g. "sh:in (true)" instead of "sh:hasValue true", not sure that's the best solution though | ||
let violations = [] | ||
let rectifiableViolations = [] | ||
for (let result of report.results) { | ||
const comp = result.constraintComponent.value.split("#")[1] | ||
if (skipMinCountAndNode && (comp === "MinCountConstraintComponent" || comp === "QualifiedMinCountConstraintComponent" || comp === "NodeConstraintComponent")) continue | ||
violations.push({ | ||
let violation = { | ||
constraint: result.constraintComponent.value, | ||
@@ -242,5 +286,10 @@ focusNode: result.focusNode?.value ?? "", | ||
message: result.message?.[0]?.value ?? "" | ||
}) | ||
} | ||
if (modifiableDFs.includes(violation.path)) { | ||
rectifiableViolations.push(violation) | ||
} else { | ||
violations.push(violation) | ||
} | ||
} | ||
return violations | ||
return [violations, rectifiableViolations] | ||
} | ||
@@ -247,0 +296,0 @@ |
import { Store, Parser as N3Parser, Writer as N3Writer, DataFactory } from "n3" | ||
const { namedNode, literal } = DataFactory | ||
import Validator from "shacl-engine/Validator.js" | ||
import { validations as sparqlValidations } from "shacl-engine/sparql.js" | ||
import rdf from "rdf-ext" | ||
@@ -73,3 +74,3 @@ import { QueryEngine } from "@comunica/query-sparql-rdfjs" | ||
let dataset = rdf.dataset(store.getQuads()) | ||
let validator = new Validator(dataset, { factory: rdf, debug: false }) | ||
let validator = new Validator(dataset, { factory: rdf, debug: false, validations: sparqlValidations }) | ||
return await validator.validate({ dataset: dataset }) | ||
@@ -330,1 +331,40 @@ } | ||
} | ||
export async function getModifiableDatafields(store) { | ||
let query = ` | ||
PREFIX ff: <https://foerderfunke.org/default#> | ||
SELECT * WHERE { | ||
?df a ff:DataField ; | ||
ff:modifiable true . | ||
}` | ||
let rows = await runSparqlSelectQueryOnStore(query, store) | ||
let dfs = [] | ||
for (let row of rows) { | ||
dfs.push(row.df) | ||
} | ||
return dfs | ||
} | ||
export async function getAllTriplesContainingUri(uri, rdfStrings) { | ||
let store = await rdfStringsToStore(rdfStrings) | ||
let query = ` | ||
SELECT * WHERE { | ||
<${uri}> ?p ?o . | ||
}` | ||
let asSubject = await runSparqlSelectQueryOnStore(query, store) | ||
query = ` | ||
SELECT * WHERE { | ||
?s <${uri}> ?o . | ||
}` | ||
let asPredicate = await runSparqlSelectQueryOnStore(query, store) | ||
query = ` | ||
SELECT * WHERE { | ||
?s ?p <${uri}> . | ||
}` | ||
let asObject = await runSparqlSelectQueryOnStore(query, store) | ||
return { | ||
asSubject: asSubject, | ||
asPredicate: asPredicate, | ||
asObject: asObject | ||
} | ||
} |
Sorry, the diff of this file is too big to display
2387482
1012