diff --git a/app/config/custom-inflector-rules.js b/app/config/custom-inflector-rules.js index e64dd6539..4dfe4e736 100644 --- a/app/config/custom-inflector-rules.js +++ b/app/config/custom-inflector-rules.js @@ -71,3 +71,6 @@ inflector.irregular('global-system-message', 'global-system-messages'); inflector.irregular('werkingsgebied', 'werkingsgebieden'); inflector.irregular('bestuurseenheid-contact', 'bestuurseenheid-contacten'); + +inflector.irregular('report', 'reports'); +inflector.irregular('validationresult', 'validationresults'); diff --git a/app/controllers/report.js b/app/controllers/report.js new file mode 100644 index 000000000..465c5c94c --- /dev/null +++ b/app/controllers/report.js @@ -0,0 +1,67 @@ +import Controller from '@ember/controller'; + +import { action } from '@ember/object'; +import { service } from '@ember/service'; + +export default class ReportController extends Controller { + @service store; + + get targetClasses() { + return Array.from(this.model.resultsByTargetClass.keys()); + } + + getValueFromValidationResult(result) { + let value = ''; + if (result.value) { + value = result.value; + } else if (result.resultPath) { + value = result.resultPath; + } + return value; + } + + @action + filterResultsByTargetClass(targetClass) { + return this.model.resultsByTargetClass.get(targetClass); + } + + @action + lengthOfResultsByTargetClass(targetClass) { + return this.filterResultsByTargetClass(targetClass).length; + } + + @action + async getFocusNodeContext(focusNode, targetClass) { + const mapTargetClassToRoute = { + 'http://data.vlaanderen.be/ns/mandaat#Mandataris': { + route: 'mandatarissen.mandataris', + model: 'mandataris', + }, + 'http://www.w3.org/ns/person#Person': { + route: 'mandatarissen.persoon.mandaten', + model: 'persoon', + }, + 'http://data.vlaanderen.be/ns/besluit#Bestuursorgaan': { + route: 'organen.orgaan', + model: 'bestuursorgaan', + }, + }; + return { + route: mapTargetClassToRoute[targetClass].route, + modelId: await this.searchModelIdForFocusNode( + mapTargetClassToRoute[targetClass].model, + focusNode + ), + }; + } + + async searchModelIdForFocusNode(modelName, focusNode) { + return ( + await this.store.query(modelName, { + filter: { + ':uri:': focusNode, + }, + }) + )?.at(0)?.id; + } +} diff --git a/app/models/report.js b/app/models/report.js new file mode 100644 index 000000000..0d5c2374b --- /dev/null +++ b/app/models/report.js @@ -0,0 +1,12 @@ +import Model, { attr, hasMany } from '@ember-data/model'; + +export default class ReportModel extends Model { + @attr('datetime') created; + @attr('boolean') conforms; + + @hasMany('validationresult', { + async: true, + inverse: null, + }) + validationresults; +} diff --git a/app/models/validationresult.js b/app/models/validationresult.js new file mode 100644 index 000000000..7307da91d --- /dev/null +++ b/app/models/validationresult.js @@ -0,0 +1,12 @@ +import Model, { attr } from '@ember-data/model'; + +export default class ValidationresultModel extends Model { + @attr('string') focusNode; + @attr('string') resultSeverity; + @attr('string') sourceConstraintComponent; + @attr('string') sourceShape; + @attr('string') resultMessage; + @attr('string') resultPath; + @attr('string') value; + @attr('string') targetClassOfFocusNode; +} diff --git a/app/router.js b/app/router.js index 3121d16ae..e41c41baa 100644 --- a/app/router.js +++ b/app/router.js @@ -74,4 +74,5 @@ Router.map(function () { path: '/*wildcard', }); this.route('session-expired'); + this.route('report'); }); diff --git a/app/routes/report.js b/app/routes/report.js new file mode 100644 index 000000000..a6c0cb0aa --- /dev/null +++ b/app/routes/report.js @@ -0,0 +1,43 @@ +import Route from '@ember/routing/route'; + +import { service } from '@ember/service'; + +export default class ReportRoute extends Route { + @service store; + @service router; + @service features; + @service session; + beforeModel(transition) { + this.session.requireAuthentication(transition, 'login'); + + if (!this.features.isEnabled('shacl-report')) { + this.router.replaceWith('index'); + } + } + + async model() { + const latestReport = ( + await this.store.query('report', { + sort: '-created', + page: { size: 1 }, + include: 'validationresults', + }) + )[0]; + + return { + report: latestReport, + resultsByTargetClass: await this.getMappedResults(latestReport), + }; + } + + async getMappedResults(report) { + const results = (await report?.validationresults) ?? []; + const map = new Map(); + + for (const result of results) { + const currentResult = map.get(result.targetClassOfFocusNode) ?? []; + map.set(result.targetClassOfFocusNode, currentResult.concat(result)); + } + return map; + } +} diff --git a/app/templates/report.hbs b/app/templates/report.hbs new file mode 100644 index 000000000..c845aabcd --- /dev/null +++ b/app/templates/report.hbs @@ -0,0 +1,53 @@ +{{page-title "Report"}} + + + SHACL validatierapport ({{this.model.report.id}} + - + {{moment-format this.model.report.created "DD-MM-YYYY HH:mm:ss"}}) + + +{{#each this.targetClasses as |targetClass|}} + + + + {{targetClass}} + + + + + + + + Entiteit + Boodschap + Waarde + + + + + {{#let (await (this.getFocusNodeContext result.focusNode result.targetClassOfFocusNode)) as |context|}} + + {{result.focusNode}} + + {{/let}} + + + {{result.resultMessage}} + + + {{this.getValueFromValidationResult result}} + + + + +{{/each}} \ No newline at end of file diff --git a/config/environment.js b/config/environment.js index a681dd122..adf120f2e 100644 --- a/config/environment.js +++ b/config/environment.js @@ -64,6 +64,8 @@ module.exports = function (environment) { // ENV.APP.LOG_TRANSITIONS = true; // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; // ENV.APP.LOG_VIEW_LOOKUPS = true; + + ENV.features['shacl-report'] = true; } if (environment === 'test') { @@ -76,10 +78,13 @@ module.exports = function (environment) { ENV.APP.rootElement = '#ember-testing'; ENV.APP.autoboot = false; + + ENV.features['shacl-report'] = false; } if (environment === 'production') { // here you can enable a production-specific feature + ENV.features['shacl-report'] = false; } ENV.features['show-iv-module'] = true; diff --git a/tests/unit/controllers/report-test.js b/tests/unit/controllers/report-test.js new file mode 100644 index 000000000..4eac4765d --- /dev/null +++ b/tests/unit/controllers/report-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'frontend-lmb/tests/helpers'; + +module('Unit | Controller | report', function (hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function (assert) { + let controller = this.owner.lookup('controller:report'); + assert.ok(controller); + }); +}); diff --git a/tests/unit/models/report-test.js b/tests/unit/models/report-test.js new file mode 100644 index 000000000..2d5b509f3 --- /dev/null +++ b/tests/unit/models/report-test.js @@ -0,0 +1,14 @@ +import { module, test } from 'qunit'; + +import { setupTest } from 'frontend-lmb/tests/helpers'; + +module('Unit | Model | report', function (hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord('report', {}); + assert.ok(model); + }); +}); diff --git a/tests/unit/models/validationresult-test.js b/tests/unit/models/validationresult-test.js new file mode 100644 index 000000000..b7a017318 --- /dev/null +++ b/tests/unit/models/validationresult-test.js @@ -0,0 +1,14 @@ +import { module, test } from 'qunit'; + +import { setupTest } from 'frontend-lmb/tests/helpers'; + +module('Unit | Model | validationresult', function (hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord('validationresult', {}); + assert.ok(model); + }); +}); diff --git a/tests/unit/routes/report-test.js b/tests/unit/routes/report-test.js new file mode 100644 index 000000000..4ef218c24 --- /dev/null +++ b/tests/unit/routes/report-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'frontend-lmb/tests/helpers'; + +module('Unit | Route | report', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:report'); + assert.ok(route); + }); +});