diff --git a/CHANGELOG.md b/CHANGELOG.md index 8967b4f..4b82fea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ # Changelog +## Unreleased +- Add KBO organizations report to report generation service +- Add organizations detail report (addresses and contact info) to report generation service ## 1.38.4 (2026-02-26) ### Backend diff --git a/config/reports/index.js b/config/reports/index.js index b659fa5..4b22474 100644 --- a/config/reports/index.js +++ b/config/reports/index.js @@ -1,7 +1,11 @@ import OrganizationsReport from './organizations-report' import RelatedOrganizationsReport from './related-organizations-report' +import KboOrganizationsReport from './kbo-organizations-report' +import OrganizationsDetailReport from './organizations-detail-report' export default [ OrganizationsReport, RelatedOrganizationsReport, + KboOrganizationsReport, + OrganizationsDetailReport, ]; diff --git a/config/reports/kbo-organizations-report.js b/config/reports/kbo-organizations-report.js new file mode 100644 index 0000000..3f46884 --- /dev/null +++ b/config/reports/kbo-organizations-report.js @@ -0,0 +1,108 @@ +import { generateReportFromData, batchedQuery } from "../helpers.js"; +import { PREFIXES } from "./utils"; + +export default { + cronPattern: "0 00 4 * * *", + name: "kboOrganizations", + execute: async () => { + const reportData = { + title: "kbo organizations", + description: "List of KBO Organizations with contact information", + filePrefix: "exports/kbo-organizations", + }; + + console.log("Generating KBO organizations report"); + + const queryString = ` + ${PREFIXES} + + SELECT DISTINCT + ?org + ?org_legal_name + ?org_classification_label + ?org_status_label + ?org_kbo_number + ?org_contact_point + ?org_full_address + ?org_email + ?org_telephone + ?website + WHERE { + ?org . + ?org a . + + OPTIONAL { ?org regorg:legalName ?org_legal_name . } + + OPTIONAL { + ?org ?org_classification_label . + } + + OPTIONAL { + ?org regorg:orgStatus ?org_status . + ?org_status skos:prefLabel ?org_status_label . + } + + OPTIONAL { + ?org adms:identifier ?identifier . + ?identifier skos:notation "KBO nummer" ; + generiek:gestructureerdeIdentificator ?id1 . + ?id1 generiek:lokaleIdentificator ?org_kbo_number . + } + + OPTIONAL { + ?org schema:contactPoint ?org_contact_point . + + OPTIONAL { + ?org_contact_point locn:address ?org_address . + ?org_address locn:fullAddress ?org_full_address . + } + + OPTIONAL { ?org_contact_point schema:email ?org_email . } + OPTIONAL { ?org_contact_point schema:telephone ?org_telephone . } + OPTIONAL { ?org_contact_point foaf:page ?website . } + } + } + `; + + const queryResponse = await batchedQuery(queryString); + + const data = queryResponse.results.bindings.map((row) => ({ + org: getSafeValue(row, "org"), + org_legal_name: getSafeValue(row, "org_legal_name"), + org_classification_label: getSafeValue(row, "org_classification_label"), + org_status_label: getSafeValue(row, "org_status_label"), + org_kbo_number: getSafeValue(row, "org_kbo_number"), + org_contact_point: getSafeValue(row, "org_contact_point"), + org_full_address: getSafeValue(row, "org_full_address"), + org_email: getSafeValue(row, "org_email"), + org_telephone: getSafeValue(row, "org_telephone"), + website: getSafeValue(row, "website"), + })); + + await generateReportFromData( + data, + [ + "org", + "org_legal_name", + "org_classification_label", + "org_status_label", + "org_kbo_number", + "org_contact_point", + "org_full_address", + "org_email", + "org_telephone", + "website", + ], + reportData, + ); + }, +}; + +function getSafeValue(entry, property) { + return entry[property] ? wrapInQuote(entry[property].value) : null; +} + +// Some values might contain commas; wrapping them in escapes quotes doesn't disrupt the columns. +function wrapInQuote(value) { + return `\"${value}\"`; +} diff --git a/config/reports/organizations-detail-report.js b/config/reports/organizations-detail-report.js new file mode 100644 index 0000000..77d48b2 --- /dev/null +++ b/config/reports/organizations-detail-report.js @@ -0,0 +1,147 @@ +import { generateReportFromData, batchedQuery } from "../helpers.js"; +import { PREFIXES } from "./utils"; + +export default { + cronPattern: "0 00 4 * * *", + name: "organizationsDetail", + execute: async () => { + const reportData = { + title: "organizations detail", + description: + "List of Organizations with address and contact information", + filePrefix: "exports/organizations-detail", + }; + + console.log("Generating organizations detail report"); + + const queryString = ` + ${PREFIXES} + + SELECT DISTINCT ?org + ?org_pref_label ?org_classification_label ?org_status_label ?org_kbo_number + + ?primary_email + ?website + ?primary_telephone + ?secondary_telephone + + ?full_address + ?land + ?gemeente + ?straat + ?huisnummer + ?busnummer + ?postcode + ?province + + WHERE { + ?org a org:Organization . + OPTIONAL { ?org regorg:legalName ?org_legal_name . } + OPTIONAL { ?org skos:prefLabel ?org_pref_label . } + OPTIONAL { + ?org org:classification ?org_classification . + ?org_classification skos:prefLabel ?org_classification_label . + } + OPTIONAL { + ?org regorg:orgStatus ?org_status . + ?org_status skos:prefLabel ?org_status_label . + } + OPTIONAL { + ?org adms:identifier ?identifier . + ?identifier skos:notation "KBO nummer" ; + generiek:gestructureerdeIdentificator ?id1 . + ?id1 generiek:lokaleIdentificator ?org_kbo_number . + } + OPTIONAL { + ?org org:hasPrimarySite ?org_site . + ?org_site organisatie:bestaatUit ?org_address . + ?org_site org:siteAddress ?org_contact_point . + + OPTIONAL { + ?org_contact_point schema:email ?primary_email . + ?org_contact_point schema:contactType "primary" . + } + OPTIONAL { + ?org_contact_point schema:telephone ?primary_telephone . + ?org_contact_point schema:contactType "primary" . + } + OPTIONAL { + ?org_contact_point schema:telephone ?secondary_telephone . + ?org_contact_point schema:contactType "secondary" . + } + + OPTIONAL { + ?org_contact_point foaf:page ?website . + ?org_contact_point schema:contactType "primary" . + } + + OPTIONAL { ?org_contact_point schema:contactType ?contact_type . } + + OPTIONAL { ?org_address locn:fullAddress ?full_address . } + OPTIONAL { ?org_address adres:land ?land . } + OPTIONAL { ?org_address adres:gemeentenaam ?gemeente . } + OPTIONAL { ?org_address locn:thoroughfare ?straat . } + OPTIONAL { ?org_address adres:Adresvoorstelling.huisnummer ?huisnummer . } + OPTIONAL { ?org_address adres:Adresvoorstelling.busnummer ?busnummer . } + OPTIONAL { ?org_address locn:postCode ?postcode . } + OPTIONAL { ?org_address locn:adminUnitL2 ?province . } + } + } + `; + + const queryResponse = await batchedQuery(queryString); + + const data = queryResponse.results.bindings.map((row) => ({ + org: getSafeValue(row, "org"), + org_pref_label: getSafeValue(row, "org_pref_label"), + org_classification_label: getSafeValue(row, "org_classification_label"), + org_status_label: getSafeValue(row, "org_status_label"), + org_kbo_number: getSafeValue(row, "org_kbo_number"), + primary_email: getSafeValue(row, "primary_email"), + website: getSafeValue(row, "website"), + primary_telephone: getSafeValue(row, "primary_telephone"), + secondary_telephone: getSafeValue(row, "secondary_telephone"), + full_address: getSafeValue(row, "full_address"), + land: getSafeValue(row, "land"), + gemeente: getSafeValue(row, "gemeente"), + straat: getSafeValue(row, "straat"), + huisnummer: getSafeValue(row, "huisnummer"), + busnummer: getSafeValue(row, "busnummer"), + postcode: getSafeValue(row, "postcode"), + province: getSafeValue(row, "province"), + })); + + await generateReportFromData( + data, + [ + "org", + "org_pref_label", + "org_classification_label", + "org_status_label", + "org_kbo_number", + "primary_email", + "website", + "primary_telephone", + "secondary_telephone", + "full_address", + "land", + "gemeente", + "straat", + "huisnummer", + "busnummer", + "postcode", + "province", + ], + reportData, + ); + }, +}; + +function getSafeValue(entry, property) { + return entry[property] ? wrapInQuote(entry[property].value) : null; +} + +// Some values might contain commas; wrapping them in escapes quotes doesn't disrupt the columns. +function wrapInQuote(value) { + return `\"${value}\"`; +}