From 78699e44a6c20e7f027f5ae686cd1c1084fe456d Mon Sep 17 00:00:00 2001 From: bartwr Date: Mon, 18 Nov 2024 23:37:01 +0100 Subject: [PATCH] Merge attributes from multiple sources + Clean up code --- README.md | 2 +- app/api/common.ts | 99 +++++++++++++----------- app/api/gwsw.ts | 2 +- app/components/EditObject/EditObject.tsx | 19 ++--- app/components/Map/Map.tsx | 2 - app/helpers/triples.ts | 2 + app/queries/gwsw-attributes.rq.js | 36 ++++----- app/types/index.ts | 10 ++- 8 files changed, 91 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index d5757d5..e2206ea 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ De SPARQL tool voor GWSW: Er was het probleem dat GitHub Pages een foutmelding gaf: `y is not defined`. Dit is opgelost met [deze oplossing](https://github.com/alex3165/react-mapbox-gl/issues/931#issuecomment-826135957). -### Deployment process using github +### Deployment process using GitHub Actions "Here are the steps you can follow:" https://stackoverflow.com/a/76168716 diff --git a/app/api/common.ts b/app/api/common.ts index 59ecae9..f9fe5ea 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -1,4 +1,4 @@ -import {Source} from '../types' +import {Attribute, Source} from '../types' import {getPhysicalObjects as getImborPhysicalObject} from './imbor'; import {getPhysicalObjects as getGwswPhysicalObject} from './gwsw'; @@ -23,7 +23,7 @@ export const getPhysicalObjectsForSource = async (source: Source) => { let query = ''; if(source.title === 'IMBOR kern') { query = query_imbor_kern__objects(); - } else if(source.title === 'GWSW Basis v161') { + } else if(source.title === 'GWSW Basis v1.6.1') { query = query_gwsw_basis_v161__objects(); } if(! query) return false; @@ -38,60 +38,25 @@ export const getPhysicalObjectsForSource = async (source: Source) => { try { const response = await doRequest(url, requestOptions); - // const response: {[index: string]: any} = await getKern(encodeURIComponent(query)); return response; } catch (error) { return false; } - - // const imbor_objects = await getImborPhysicalObject(); - // const gwsw_objects = await getGwswPhysicalObject(); - - // // Merge both objects - // const merged = { - // head: imbor_objects.head, - // results: { - // bindings: [ - // ...imbor_objects.results.bindings, - // ...gwsw_objects.results.bindings - // ] - // } - // } - - // // And return - // return merged; -} - -export const getPhysicalObjects = async () => { - const imbor_objects = await getImborPhysicalObject(); - const gwsw_objects = await getGwswPhysicalObject(); - - // Merge both objects - const merged = { - head: imbor_objects.head, - results: { - bindings: [ - ...imbor_objects.results?.bindings, - ...gwsw_objects.results?.bindings - ] - } - } - - // And return - return merged; } -export const getAttributesForClass = async (classUri: string) => { +export const getAttributesForClass = async (source: Source, classUri: string) => { const imbor_attributes = await getImborAttributesForClass(classUri); - // const gwsw_attributes = await getGwswAttributesForClass(classUri); - + const gwsw_attributes = await getGwswAttributesForClass(classUri); + // Merge both objects + let merged_bindings = mergeAttributes( + gwsw_attributes.results?.bindings, + imbor_attributes.results?.bindings + ); const merged = { head: imbor_attributes.head, results: { - bindings: [ - ...imbor_attributes.results?.bindings - ] + bindings: merged_bindings } } @@ -108,3 +73,47 @@ export const getEnumsForAttribute = async (attributeUri: string) => { // And return return merged; } + +// mergeAttributes :: Merge attributes based on entry_text +const mergeAttributes = (bindings1: Attribute[], bindings2: Attribute[]) => { + if (!bindings1 && !bindings2) return []; + if (!bindings1) return bindings2; + if (!bindings2) return bindings1; + + let merged: Attribute[] = bindings1; + + // Add bindings2 entries, if not present in bindings1 dataset + bindings2.forEach((x2: Attribute) => { + const related_attribute: Attribute | undefined = bindings1.find((x1: Attribute) => x1.entry_text?.value === x2.entry_text?.value); + if(! related_attribute) merged.push(x2); + }); + + (() => { + // Check if 'identificatie' exists as an attribute field + const identificatie_exists = merged.find((attribute: Attribute) => attribute?.entry_text?.value === 'identificatie'); + // If not: Add 'identificatie' object to beginning of the array + if(! identificatie_exists) merged.unshift({ + "entry_iri": { + "type": "uri", + "value": "https://data.crow.nl/imbor/def/5f430c8d-7503-4a69-9e2f-f0b6e6c7f54e" + }, + "entry_text": { + "xml:lang": "nl", + "type": "literal", + "value": "identificatie" + }, + "entry_definition": { + "xml:lang": "nl", + "type": "literal", + "value": "Uniek nummer van het object (GUID), een numerieke identificatie. Conform NEN3610 zijn identificatiecodes persistent: ze wijzigen niet gedurende de levensduur van een object." + }, + "group_iri": { + "type": "uri", + "value": "https://w3id.org/nen2660/def#Object" + } + }); + })(); + + return merged; +} + diff --git a/app/api/gwsw.ts b/app/api/gwsw.ts index cd3737e..22d72a6 100644 --- a/app/api/gwsw.ts +++ b/app/api/gwsw.ts @@ -1,7 +1,7 @@ import {config} from '../config' import {query as exampleQuery} from '../queries/gwsw-example.js'; import {query as physicalObjectsQuery} from '../queries/gwsw-PhysicalObjects.rq.js'; -import {query as attributesQuery} from '../queries/nen2660-attributes.rq.js'; +import {query as attributesQuery} from '../queries/gwsw-attributes.rq.js'; // doRequest :: string -> json const doRequest = async (url: string, query: string) => { diff --git a/app/components/EditObject/EditObject.tsx b/app/components/EditObject/EditObject.tsx index 78826e9..75272c5 100644 --- a/app/components/EditObject/EditObject.tsx +++ b/app/components/EditObject/EditObject.tsx @@ -26,15 +26,9 @@ import { } from '../../api/imbor' import { getAttributesForClass, - // getPhysicalObjects, getPhysicalObjectsForSource } from '../../api/common' import { - getExample, - getGwsw -} from '../../api/gwsw' -import { - makeTriple, makeTriplesObject, getUniquePhysicalObjects } from '../../helpers/triples'; @@ -49,9 +43,9 @@ import { setGeoClasses, } from './editObjectSlice'; -// import '../MapTools/MapTools.css' import './EditObject.css' import SourceLabel from '../SourceLabel/SourceLabel'; +import { Source } from '@/app/types'; interface ImborResponse { head: object, @@ -106,9 +100,9 @@ const EditObject = () => { } // Fetch attributes for a specific FysicalObject class - const fetchAttributesForClass = async (classUri: URL) => { + const fetchAttributesForClass = async (source: Source, classUri: URL) => { if(! classUri) return; - const response = await getAttributesForClass(classUri); + const response = await getAttributesForClass(source, classUri); const triples = makeTriplesObject(response); // Make ID the first attribute const sortedTriples = triples.sort((a: any, b: any) => { @@ -120,7 +114,6 @@ const EditObject = () => { // Function that runs if component loads useEffect(() => { - // fetchPhysicalObjects(); fetchGeoClasses(); }, []); @@ -167,7 +160,7 @@ const EditObject = () => { setGeometry(object.geometry); } // Fetch attributes for object type - const attributes = await fetchAttributesForClass(object.uri); + const attributes = await fetchAttributesForClass(source, object.uri); // Fill in all attributes after a few milliseconds (so state can update first) setTimeout(() => { if(! object.attributes) return; @@ -237,7 +230,7 @@ const EditObject = () => { useEffect(() => { if(! selectedObjectType) return; - fetchAttributesForClass(selectedObjectType.label); + fetchAttributesForClass(config.sources[selectedSource.id], selectedObjectType.label); }, [selectedObjectType]) // Function that prepares data to be used for DatalistInput @@ -375,7 +368,7 @@ const EditObject = () => { const showAttributes = selectedObjectType && geometry && geometry.inputs; // If physical objects did not load: Show loading text - if(selectedSource && ! physicalObjects || physicalObjects.length <= 0) { + if(selectedSource && (! physicalObjects || physicalObjects.length <= 0)) { return (
Bezig met laden van objecttypen... diff --git a/app/components/Map/Map.tsx b/app/components/Map/Map.tsx index 70e4aeb..0eb8fe5 100644 --- a/app/components/Map/Map.tsx +++ b/app/components/Map/Map.tsx @@ -73,7 +73,6 @@ const Map = () => { const addDraggableMarker = (theMap: any) => { if(! theMap) return; if(activeMarker) return; - console.log('process.env', process.env) const onDragEnd = async () => { // Get lng/lat of this marker @@ -127,7 +126,6 @@ const Map = () => { // Remove marker const removeMarker = (theMap: any) => { - console.log('removeMarker') if(! activeMarker) return; // Remove it activeMarker.remove(); diff --git a/app/helpers/triples.ts b/app/helpers/triples.ts index f762f9d..e188727 100644 --- a/app/helpers/triples.ts +++ b/app/helpers/triples.ts @@ -21,6 +21,8 @@ const getUniquePhysicalObjects = (input: { [x: string]: any; }) => { // Only keep unique ones, remove duplicates let uniqueLabels: any[] = []; let uniqueTriples: any[] = []; + + if(! input) return uniqueTriples; Object.keys(input).forEach((key) => { const triple = input[key]; if(uniqueLabels.indexOf(triple.label.value) > -1) { diff --git a/app/queries/gwsw-attributes.rq.js b/app/queries/gwsw-attributes.rq.js index d671649..53d4c3b 100644 --- a/app/queries/gwsw-attributes.rq.js +++ b/app/queries/gwsw-attributes.rq.js @@ -1,22 +1,22 @@ export const query = (classUri) => ` -PREFIX gwsw: -PREFIX rdfs: -PREFIX owl: -PREFIX skos: -select * + PREFIX gwsw: + PREFIX rdfs: + PREFIX owl: + PREFIX skos: + select * -where { - values $group_iri { <${classUri}> } - - $group_iri rdfs:subClassOf+ [ - a owl:Restriction ; - owl:onClass ?entry_iri ; - owl:onProperty gwsw:hasAspect ; - ]. - - ?entry_iri rdfs:label ?enrty_text . - OPTIONAL {?entry_iri skos:definition ?entry_definition} - -} limit 100 + where { + values $group_iri { <${classUri}> } + + $group_iri rdfs:subClassOf+ [ + a owl:Restriction ; + owl:onClass ?entry_iri ; + owl:onProperty gwsw:hasAspect ; + ]. + + ?entry_iri rdfs:label ?entry_text . + OPTIONAL {?entry_iri skos:definition ?entry_definition} + + } limit 100 ` diff --git a/app/types/index.ts b/app/types/index.ts index 7363eaf..4039a26 100644 --- a/app/types/index.ts +++ b/app/types/index.ts @@ -14,7 +14,15 @@ type Config = { sources: Sources; } +type Attribute = { + entry_definition?: any; + entry_iri?: any; + entry_text?: any; + group_iri?: any; +} + export type { Config, - Source + Source, + Attribute }