From f6b049b107a290796aae6e0776ce0be0763d54e2 Mon Sep 17 00:00:00 2001 From: Lars Musters Date: Tue, 13 Dec 2022 13:28:17 +0000 Subject: [PATCH] Resolve "Dashboard pagina, design van Roos" --- backend/app/config/resource.py | 65 ++++ backend/app/main.py | 6 +- backend/app/routers/aggregations.py | 79 ++++ frontend/assets/styles/main.scss | 4 + frontend/components/SearchResultCard.vue | 2 +- .../components/algoritme/AlgoritmeFilters.vue | 2 +- .../components/dashboard/Completeness.vue | 100 +++++ .../components/dashboard/CountPerType.vue | 154 ++++++++ frontend/components/views/AppHeader.vue | 4 + frontend/config/config.ts | 4 + frontend/locales/en.json | 357 +++++++++-------- frontend/locales/nl.json | 359 ++++++++++-------- frontend/pages/algoritme/[slug].vue | 10 +- frontend/pages/dashboard.vue | 63 +++ frontend/services/algoritme.ts | 32 +- 15 files changed, 928 insertions(+), 313 deletions(-) create mode 100644 backend/app/config/resource.py create mode 100644 backend/app/routers/aggregations.py create mode 100644 frontend/components/dashboard/Completeness.vue create mode 100644 frontend/components/dashboard/CountPerType.vue create mode 100644 frontend/pages/dashboard.vue diff --git a/backend/app/config/resource.py b/backend/app/config/resource.py new file mode 100644 index 00000000..22c88dd6 --- /dev/null +++ b/backend/app/config/resource.py @@ -0,0 +1,65 @@ +from enum import Enum + + +class Columns(str, Enum): + all = "*" + + algoritme_organization = "algoritme.organization" + algoritme_name = "algoritme.name" + algoritme_department = "algoritme.department" + algoritme_description_short = "algoritme.description_short" + algoritme_type = "algoritme.type" + algoritme_category = "algoritme.category" + algoritme_website = "algoritme.website" + algoritme_status = "algoritme.status" + algoritme_uuid = "algoritme.uuid" + algoritme_toegevoegd_op = "algoritme.toegevoegd_op" + algoritme_slug = "algoritme.slug" + + inzet_goal = "inzet.goal" + inzet_impact = "inzet.impact" + inzet_proportionality = "inzet.proportionality" + inzet_decision_making_process = "inzet.decision_making_process" + inzet_documentation = "inzet.documentation" + inzet_toegevoegd_op = "inzet.toegevoegd_op" + + juridisch_competent_authority = "juridisch.competent_authority" + juridisch_lawful_basis = "juridisch.lawful_basis" + juridisch_iama = "juridisch.iama" + juridisch_iama_description = "juridisch.iama_description" + juridisch_dpia = "juridisch.dpia" + juridisch_dpia_description = "juridisch.dpia_description" + juridisch_objection_procedure = "juridisch.objection_procedure" + juridisch_toegevoegd_op = "juridisch.toegevoegd_op" + + metadata_schema = "metadata.schema" + metadata_uuid = "metadata.uuid" + metadata_url = "metadata.url" + metadata_contact_email = "metadata.contact_email" + metadata_area = "metadata.area" + metadata_lang = "metadata.lang" + metadata_revision_date = "metadata.revision_date" + metadata_toegevoegd_op = "metadata.toegevoegd_op" + + toepassing_description = "toepassing.description" + toepassing_application_url = "toepassing.application_url" + toepassing_publiccode = "toepassing.publiccode" + toepassing_mprd = "toepassing.mprd" + toepassing_source_data = "toepassing.source_data" + toepassing_methods_and_models = "toepassing.methods_and_models" + toepassing_toegevoegd_op = "toepassing.toegevoegd_op" + + toezicht_monitoring = "toezicht.monitoring" + toezicht_human_intervention = "toezicht.human_intervention" + toezicht_risks = "toezicht.risks" + toezicht_performance_standard = "toezicht.performance_standard" + toezicht_toegevoegd_op = "toezicht.toegevoegd_op" + + +# columns=metadata.contact_email +# columns=metadata.toegevoegd_op + +# columns=toepassing.toegevoegd_op + +# columns=toezicht.human_intervention +# columns=toezicht.toegevoegd_op diff --git a/backend/app/main.py b/backend/app/main.py index f094b4d6..44b51e30 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,7 +1,7 @@ from fastapi import FastAPI from starlette.middleware.cors import CORSMiddleware from fastapi import APIRouter -from app.routers import default +from app.routers import default, aggregations from app.etl.load import load from app.util.logger import get_logger @@ -14,8 +14,10 @@ app = FastAPI(docs_url="/api-docs", title="Application API") router = APIRouter() -app.include_router(default.router, prefix="/api") +app.include_router(default.router, prefix="/api", tags=["default"]) +router = APIRouter() +app.include_router(aggregations.router, prefix="/api", tags=["aggregations"]) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"] diff --git a/backend/app/routers/aggregations.py b/backend/app/routers/aggregations.py new file mode 100644 index 00000000..a21870e6 --- /dev/null +++ b/backend/app/routers/aggregations.py @@ -0,0 +1,79 @@ +from fastapi import APIRouter, Query +from fastapi import Depends +from app.middleware.middleware import get_db +from sqlalchemy.orm import Session +from sqlalchemy import text +from app.config.resource import Columns +import logging + +router = APIRouter() +logger = logging.getLogger(__name__) + + +@router.get("/columns/") +async def get_columns(db: Session = Depends(get_db)): + stmt = text( + """SELECT table_name, column_name, is_nullable + FROM information_schema.columns + WHERE table_schema = 'public' + AND column_name != 'algoritme_id' + AND column_name != 'id' + AND table_name != 'alembic_version' + ORDER BY table_name, ordinal_position + """ + ) + return db.execute(stmt).all() + + +@router.get("/db-count/") +async def get_total_count(db: Session = Depends(get_db)) -> int: + stmt = text("SELECT count(id) FROM algoritme") + return int(db.execute(stmt).all()[0][0]) + + +@router.get("/db-count/{column}") +async def get_count_per_type(column: Columns, db: Session = Depends(get_db)): + stmt = text( + f""" + SELECT count(1), {column.value} as descriptor + FROM algoritme + LEFT JOIN inzet ON algoritme.id=inzet.algoritme_id + LEFT JOIN juridisch ON algoritme.id=juridisch.algoritme_id + LEFT JOIN metadata ON algoritme.id=metadata.algoritme_id + LEFT JOIN toepassing ON algoritme.id=toepassing.algoritme_id + LEFT JOIN toezicht ON algoritme.id=toezicht.algoritme_id + GROUP BY {column.value} + ORDER BY count(1) desc + LIMIT 10 + """ + ) + return db.execute(stmt).all() + + +@router.get("/completeness/") +async def get_count_with_filled_columns( + columns: list[Columns] | None = Query(default=None), db: Session = Depends(get_db) +): + selection_string = ", ".join(columns) + stmt = text( + f"""SELECT {selection_string} + FROM algoritme + LEFT JOIN inzet ON algoritme.id=inzet.algoritme_id + LEFT JOIN juridisch ON algoritme.id=juridisch.algoritme_id + LEFT JOIN metadata ON algoritme.id=metadata.algoritme_id + LEFT JOIN toepassing ON algoritme.id=toepassing.algoritme_id + LEFT JOIN toezicht ON algoritme.id=toezicht.algoritme_id + """ + ) + + table = db.execute(stmt).all() + + def is_filled(cell: any) -> bool: + return [""].count(cell) == 0 + + compliantRows = [] + for row in table: + compliantRow = all([is_filled(cell) for cell in row]) + if compliantRow: + compliantRows.append(row) + return len(compliantRows) diff --git a/frontend/assets/styles/main.scss b/frontend/assets/styles/main.scss index cee7d432..74329979 100644 --- a/frontend/assets/styles/main.scss +++ b/frontend/assets/styles/main.scss @@ -36,6 +36,10 @@ cursor: pointer; } +.block-info { + max-width: 600px; +} + .noselect { user-select: none; -moz-user-select: none; diff --git a/frontend/components/SearchResultCard.vue b/frontend/components/SearchResultCard.vue index eb0d9b09..3b36144c 100644 --- a/frontend/components/SearchResultCard.vue +++ b/frontend/components/SearchResultCard.vue @@ -16,7 +16,7 @@
-
{{ t(`algorithmProperties.algemeneInformatie.${sT}.label`) }}
+
{{ t(`algorithmProperties.${sT}.label`) }}
{{ algoritme[sT as keyof typeof algoritme] }}
diff --git a/frontend/components/algoritme/AlgoritmeFilters.vue b/frontend/components/algoritme/AlgoritmeFilters.vue index d06eec04..92622552 100644 --- a/frontend/components/algoritme/AlgoritmeFilters.vue +++ b/frontend/components/algoritme/AlgoritmeFilters.vue @@ -35,7 +35,7 @@

{{ $t( - `algorithmProperties.algemeneInformatie.${aggregationType.aggregationAttribute}.label` + `algorithmProperties.${aggregationType.aggregationAttribute}.label` ) }}

diff --git a/frontend/components/dashboard/Completeness.vue b/frontend/components/dashboard/Completeness.vue new file mode 100644 index 00000000..2f2e7203 --- /dev/null +++ b/frontend/components/dashboard/Completeness.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/frontend/components/dashboard/CountPerType.vue b/frontend/components/dashboard/CountPerType.vue new file mode 100644 index 00000000..7b91c2fc --- /dev/null +++ b/frontend/components/dashboard/CountPerType.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/frontend/components/views/AppHeader.vue b/frontend/components/views/AppHeader.vue index 64cb1aeb..a0db3713 100644 --- a/frontend/components/views/AppHeader.vue +++ b/frontend/components/views/AppHeader.vue @@ -74,6 +74,10 @@ const navigationItems = computed(() => [ label: t('navigation.algorithmRegister'), routeName: 'algoritme', }, + // { + // label: 'DEV_dashboard', + // routeName: 'dashboard', + // }, ]) const currentRoute = useRoute() const menuExpanded = ref(false) diff --git a/frontend/config/config.ts b/frontend/config/config.ts index bfaa9d87..95e8645c 100644 --- a/frontend/config/config.ts +++ b/frontend/config/config.ts @@ -50,6 +50,10 @@ const navigationItems = [ localeName: 'navigation.toegankelijkheid', routeName: 'toegankelijkheid', }, + { + localeName: 'navigation.db', + routeName: 'dashboard', + }, ] export { summaryTiles, keys, navigationItems } diff --git a/frontend/locales/en.json b/frontend/locales/en.json index be6ba401..16ac4700 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -3,7 +3,7 @@ "home-highlight-algorithm-text": "Or go directly to one of these algorithms", "explanation": "Explanation", "you-are-here": "You are here", - "Ontbreekt": "Not filled out", + "ontbreekt": "Not filled out", "locale-en": "English", "locale-nl": "Dutch", "selectLanguage": "Choose language", @@ -57,177 +57,234 @@ "contact": "Contact", "vragen": "FAQ", "privacyverklaring": "Privacy", - "toegankelijkheid": "Accessibility" + "toegankelijkheid": "Accessibility", + "db": "Dashboard" }, "pagination": { "goTo": "Go to page {n}" }, + "dashboard": { + "informationType": "Information type", + "select": "Select", + "searchFor": "Search For", + "dashboardHeading": "Insight into the register data", + "countPerTypeTitle": "Algorithms in the register per type", + "dateToday": "Date", + "completenessTitle": "Level of completeness", + "totalCountText": "Algorithms", + "tableHeader": "Number of registered algorithms with", + "fullyComplete": "All mandatory and optional fields filled", + "mandatoryComplete": "Only all mandatory fields filled", + "mandatoryMissing": "Some mandatory fields left open", + "value": "Value", + "numberOfMatches": "Matches", + "loadingText": "Loading" + }, "algorithmProperties": { + "toegevoegd_op": { + "label": "Toegevoegd op" + }, + "slug": { + "label": "" + }, "algemeneInformatie": { "label": "General information", - "name": { - "label": "Name", - "description": "** nog te vertalen **" - }, - "organization": { - "label": "Organization", - "description": "** nog te vertalen **" - }, - "department": { - "label": "Department", - "description": "** nog te vertalen **" - }, - "description_short": { - "label": "Short description", - "description": "** nog te vertalen **" - }, - "type": { - "label": "Algorithm type", - "description": "** nog te vertalen **" - }, - "category": { - "label": "Category", - "description": "** nog te vertalen **" - }, - "website": { - "label": "Website", - "description": "** nog te vertalen **" - }, - "status": { - "label": "Status", - "description": "** nog te vertalen **" - } + "description": "No description" + }, + "name": { + "label": "Name", + "description": "** nog te vertalen **" + }, + "organization": { + "label": "Organization", + "description": "** nog te vertalen **" + }, + "department": { + "label": "Department", + "description": "** nog te vertalen **" + }, + "description_short": { + "label": "Short description", + "description": "** nog te vertalen **" + }, + "type": { + "label": "Algorithm type", + "description": "** nog te vertalen **" + }, + "category": { + "label": "Category", + "description": "** nog te vertalen **" + }, + "website": { + "label": "Website", + "description": "** nog te vertalen **" + }, + "status": { + "label": "Status", + "description": "** nog te vertalen **" }, "inzet": { "label": "Application", - "goal": { - "label": "Goal", - "description": "** nog te vertalen **" - }, - "impact": { - "label": "Impact", - "description": "** nog te vertalen **" - }, - "proportionality": { - "label": "Proportionality", - "description": "** nog te vertalen **" - }, - "decision_making_process": { - "label": "Decision making process", - "description": "** nog te vertalen **" - }, - "documentation": { - "label": "Documentation", - "description": "** nog te vertalen **" - } + "description": "No description" + }, + "goal": { + "label": "Goal", + "description": "** nog te vertalen **" + }, + "impact": { + "label": "Impact", + "description": "** nog te vertalen **" + }, + "proportionality": { + "label": "Proportionality", + "description": "** nog te vertalen **" + }, + "decision_making_process": { + "label": "Decision making process", + "description": "** nog te vertalen **" + }, + "documentation": { + "label": "Documentation", + "description": "** nog te vertalen **" }, "toepassing": { "label": "Use-case", - "description": { - "label": "Description", - "description": "** nog te vertalen **" - }, - "application_url": { - "label": "Application URL", - "description": "** nog te vertalen **" - }, - "publiccode": { - "label": "** Onbekend ** publiccode", - "description": "** Onbekend **" - }, - "mprd": { - "label": "Coupling with basic registration", - "description": "** nog te vertalen **" - }, - "source_data": { - "label": "Source data", - "description": "** nog te vertalen **" - }, - "methods_and_models": { - "label": "** Onbekend ** methods_and_models", - "description": "** Onbekend **" - } + "description": "No description" + }, + "description": { + "label": "Description", + "description": "** nog te vertalen **" + }, + "application_url": { + "label": "Application URL", + "description": "** nog te vertalen **" + }, + "publiccode": { + "label": "Codebase URL", + "description": "** Onbekend **" + }, + "mprd": { + "label": "Coupling with basic registration", + "description": "** nog te vertalen **" + }, + "source_data": { + "label": "Source data", + "description": "** nog te vertalen **" + }, + "methods_and_models": { + "label": "Methods and models", + "description": "** Onbekend **" }, "toezicht": { "label": "Supervision", - "monitoring": { - "label": "Monitoring", - "description": "** nog te vertalen **" - }, - "human_intervention": { - "label": "Human intervention", - "description": "** nog te vertalen **" - }, - "risks": { - "label": "Risks", - "description": "** nog te vertalen **" - }, - "performance_standard": { - "label": "Performance standard", - "description": "** nog te vertalen **" - } + "description": "No description" + }, + "monitoring": { + "label": "Monitoring", + "description": "** nog te vertalen **" + }, + "human_intervention": { + "label": "Human intervention", + "description": "** nog te vertalen **" + }, + "risks": { + "label": "Risks", + "description": "** nog te vertalen **" + }, + "performance_standard": { + "label": "Performance standard", + "description": "** nog te vertalen **" }, "juridisch": { "label": "Legal", - "competent_authority": { - "label": "Competent authority", - "description": "** nog te vertalen **" - }, - "lawful_basis": { - "label": "Lawful basis", - "description": "** nog te vertalen **" - }, - "iama": { - "label": "IAMA", - "description": "** nog te vertalen **" - }, - "iama_description": { - "label": "Description of the IAMA", - "description": "** nog te vertalen **" - }, - "dpia": { - "label": "Data protection impact assessment", - "description": "** nog te vertalen **" - }, - "dpia_description": { - "label": "Description of the DPIA", - "description": "** nog te vertalen **" - }, - "objection_procedure": { - "label": "Objection procedure", - "description": "** nog te vertalen **" - } + "description": "No description" + }, + "competent_authority": { + "label": "Competent authority", + "description": "** nog te vertalen **" + }, + "lawful_basis": { + "label": "Lawful basis", + "description": "** nog te vertalen **" + }, + "iama": { + "label": "IAMA", + "description": "** nog te vertalen **" + }, + "iama_description": { + "label": "Description of the IAMA", + "description": "** nog te vertalen **" + }, + "dpia": { + "label": "Data protection impact assessment", + "description": "** nog te vertalen **" + }, + "dpia_description": { + "label": "Description of the DPIA", + "description": "** nog te vertalen **" + }, + "objection_procedure": { + "label": "Objection procedure", + "description": "** nog te vertalen **" }, "metadata_algorithm": { "label": "Metadata", - "schema_metadata": { - "label": "Schema", - "description": "** nog te vertalen **" - }, - "uuid": { - "label": "UUID", - "description": "** nog te vertalen **" - }, - "url": { - "label": "Source registration URL", - "description": "** nog te vertalen **" - }, - "contact_email": { - "label": "Email for point of contact", - "description": "** nog te vertalen **" - }, - "area": { - "label": "Geographical area", - "description": "** nog te vertalen **" - }, - "lang": { - "label": "Language", - "description": "** nog te vertalen **" - }, - "revision_date": { - "label": "Revision date", - "description": "** nog te vertalen **" - } + "description": "No description" + }, + "schema_metadata": { + "label": "Schema", + "description": "** nog te vertalen **" + }, + "schema": { + "label": "Schema", + "description": "** nog te vertalen **" + }, + "uuid": { + "label": "UUID", + "description": "** nog te vertalen **" + }, + "url": { + "label": "Source registration URL", + "description": "** nog te vertalen **" + }, + "contact_email": { + "label": "Email for point of contact", + "description": "** nog te vertalen **" + }, + "area": { + "label": "Geographical area", + "description": "** nog te vertalen **" + }, + "lang": { + "label": "Language", + "description": "** nog te vertalen **" + }, + "revision_date": { + "label": "Revision date", + "description": "** nog te vertalen **" } + }, + "weekDays": { + "1": "Sunday", + "2": "Monday", + "3": "Tuesday", + "4": "Wednesday", + "5": "Thursday", + "6": "Friday", + "7": "Saturday" + }, + "months": { + "1": "January", + "2": "February", + "3": "March", + "4": "April", + "5": "May", + "6": "June", + "7": "July", + "8": "August", + "9": "September", + "10": "October", + "11": "November", + "12": "December" } } diff --git a/frontend/locales/nl.json b/frontend/locales/nl.json index 13b0d63a..15a8e1bc 100644 --- a/frontend/locales/nl.json +++ b/frontend/locales/nl.json @@ -3,7 +3,7 @@ "home-highlight-algorithm-text": "Of bekijk gelijk één van de volgende algoritmes", "explanation": "Uitleg", "you-are-here": "U bent hier", - "Ontbreekt": "Niet ingevuld", + "ontbreekt": "Niet ingevuld", "locale-en": "Engels", "locale-nl": "Nederlands", "selectLanguage": "Kies taal", @@ -57,177 +57,234 @@ "contact": "Contact", "vragen": "Vaak gestelde vragen", "privacyverklaring": "Privacy", - "toegankelijkheid": "Toegankelijkheid" + "toegankelijkheid": "Toegankelijkheid", + "db": "Dashboard" }, "pagination": { "goTo": "Ga naar pagina {n}" }, + "dashboard": { + "informationType": "Type informatie", + "select": "Selecteer", + "searchFor": "Zoeken op", + "dashboardHeading": "Inzicht in de registerdata", + "countPerTypeTitle": "Algoritmes in het register per informatietype", + "dateToday": "Datum", + "completenessTitle": "Mate van compleetheid", + "totalCountText": "Aantal algoritmes", + "tableHeader": "Aantal geregistreerde algoritmen met", + "fullyComplete": "Alle verplichte en optionele specificaties aanwezig", + "mandatoryComplete": "Alleen verplichte specificaties volledig aanwezig", + "mandatoryMissing": "Sommige verplichte specificaties nog niet aanwezig", + "value": "Waarde", + "numberOfMatches": "Aantal", + "loadingText": "Aan het laden" + }, "algorithmProperties": { + "toegevoegd_op": { + "label": "Toegevoegd op" + }, + "slug": { + "label": "" + }, "algemeneInformatie": { "label": "Algemene informatie", - "name": { - "label": "Naam", - "description": "De naam die gebruikt wordt om dit algoritme aan te duiden. bijv https://nu.nl" - }, - "organization": { - "label": "Organisatie", - "description": "De volledige naam van de organisatie verantwoordelijk voor de inzet van het algoritme." - }, - "department": { - "label": "Afdeling", - "description": "De volledige naam van de afdeling of divisie verantwoordelijk voor de inzet van het algoritme." - }, - "description_short": { - "label": "Korte omschrijving", - "description": "Een korte omschrijving van maximaal 150 karakters waarin hoog over omschreven wordt wat de rol is van het algoritme." - }, - "type": { - "label": "Type algoritme", - "description": "Is het algoritme regelgebaseerd of zelflerend?" - }, - "category": { - "label": "Thema", - "description": "Het domein waarin het algoritme wordt ingezet." - }, - "website": { - "label": "Publiekspagina", - "description": "URL van de publieke algoritmeregister pagina over de inzet van het algoritme van de organisatie zelf" - }, - "status": { - "label": "Status", - "description": "Is het algoritme in ontwikkeling, in gebruik, of buiten gebruik?" - } + "description": "Geen beschrijving" + }, + "name": { + "label": "Naam", + "description": "De naam die gebruikt wordt om dit algoritme aan te duiden." + }, + "organization": { + "label": "Organisatie", + "description": "De volledige naam van de organisatie verantwoordelijk voor de inzet van het algoritme." + }, + "department": { + "label": "Afdeling", + "description": "De volledige naam van de afdeling of divisie verantwoordelijk voor de inzet van het algoritme." + }, + "description_short": { + "label": "Korte omschrijving", + "description": "Een korte omschrijving van maximaal 150 karakters waarin hoog over omschreven wordt wat de rol is van het algoritme." + }, + "type": { + "label": "Type algoritme", + "description": "Is het algoritme regelgebaseerd of zelflerend?" + }, + "category": { + "label": "Thema", + "description": "Het domein waarin het algoritme wordt ingezet." + }, + "website": { + "label": "Publiekspagina", + "description": "URL van de publieke algoritmeregister pagina over de inzet van het algoritme van de organisatie zelf" + }, + "status": { + "label": "Status", + "description": "Is het algoritme in ontwikkeling, in gebruik, of buiten gebruik?" }, "inzet": { "label": "Inzet", - "goal": { - "label": "Doel", - "description": "Het doel waarvoor het algoritme ontwikkeld is en/of hoe de inzet ervan bijdraagt aan het behalen van die doelen." - }, - "impact": { - "label": "Impact", - "description": "(1) De impact op burgers van de effecten van het algoritme, (2) onder welke omstandigheden dit gebeurt, en (3) wat de verwachte consequenties daarvan zijn voor het individu en/of de samenleving." - }, - "proportionality": { - "label": "Proportionaliteit", - "description": "Een afweging van de voor- en nadelen van de inzet van het algoritme en waarom dit redelijk gerechtvaardigd is." - }, - "decision_making_process": { - "label": "Proces", - "description": "De naam van het bedrijfs- of besluitvormingsproces in de organisatie waarbinnen het algoritme wordt ingezet." - }, - "documentation": { - "label": "Projectpagina", - "description": "URL van de projectpagina van de organisatie zelf over dit specifieke gebruik van het algoritme." - } + "description": "Geen beschrijving" + }, + "goal": { + "label": "Doel", + "description": "Het doel waarvoor het algoritme ontwikkeld is en/of hoe de inzet ervan bijdraagt aan het behalen van die doelen." + }, + "impact": { + "label": "Impact", + "description": "(1) De impact op burgers van de effecten van het algoritme, (2) onder welke omstandigheden dit gebeurt, en (3) wat de verwachte consequenties daarvan zijn voor het individu en/of de samenleving." + }, + "proportionality": { + "label": "Proportionaliteit", + "description": "Een afweging van de voor- en nadelen van de inzet van het algoritme en waarom dit redelijk gerechtvaardigd is." + }, + "decision_making_process": { + "label": "Proces", + "description": "De naam van het bedrijfs- of besluitvormingsproces in de organisatie waarbinnen het algoritme wordt ingezet." + }, + "documentation": { + "label": "Projectpagina", + "description": "URL van de projectpagina van de organisatie zelf over dit specifieke gebruik van het algoritme." }, "toepassing": { "label": "Toepassing", - "description": { - "label": "Omschrijving", - "description": "Een uitgebreide uitleg tussen de 500 en 10000 karakters van hoe het algoritme werkt." - }, - "application_url": { - "label": "URL van de applicatie", - "description": "URL van de applicatiepagina van de ontwikkelaar/aanbieder van het algoritme." - }, - "publiccode": { - "label": "** Onbekend (publiccode) **", - "description": "** Onbekend **" - }, - "mprd": { - "label": "Koppelingen met basisregistraties", - "description": "Is het algoritme direct gekoppeld aan een basisregistratie?" - }, - "source_data": { - "label": "Brondata", - "description": "Een overzicht van de databronnen die gebruikt worden door of bij het maken of trainen van het algoritme." - }, - "methods_and_models": { - "label": "** Onbekend (methods and models) **", - "description": "** Onbekend **" - } + "description": "Geen beschrijving" + }, + "description": { + "label": "Omschrijving", + "description": "Een uitgebreide uitleg tussen de 500 en 10000 karakters van hoe het algoritme werkt." + }, + "application_url": { + "label": "URL van de applicatie", + "description": "URL van de applicatiepagina van de ontwikkelaar/aanbieder van het algoritme." + }, + "publiccode": { + "label": "URL van code base", + "description": "URL van de code base van het algoritme." + }, + "mprd": { + "label": "Koppelingen met basisregistraties", + "description": "Is het algoritme direct gekoppeld aan een basisregistratie?" + }, + "source_data": { + "label": "Brondata", + "description": "Een overzicht van de databronnen die gebruikt worden door of bij het maken of trainen van het algoritme." + }, + "methods_and_models": { + "label": "Methoden en modellen", + "description": "Standaard methoden of modellen die het algoritme gebruikt." }, "toezicht": { "label": "Toezicht", - "monitoring": { - "label": "Monitoring", - "description": "Een overzicht van hoe de inzet van het algoritme wordt gemonitord." - }, - "human_intervention": { - "label": "Menselijke tussenkomst", - "description": "Een omschrijving van hoe uitkomsten van het algoritme door een mens gecontroleerd en bijgesteld (kunnen) worden." - }, - "risks": { - "label": "Risico's", - "description": "Een overzicht van de voorziene risico's." - }, - "performance_standard": { - "label": "Prestatienormen", - "description": "Een omschrijving van de verwachte prestaties van het algoritme en hoe die worden gemeten." - } + "description": "Geen beschrijving" + }, + "monitoring": { + "label": "Monitoring", + "description": "Een overzicht van hoe de inzet van het algoritme wordt gemonitord." + }, + "human_intervention": { + "label": "Menselijke tussenkomst", + "description": "Een omschrijving van hoe uitkomsten van het algoritme door een mens gecontroleerd en bijgesteld (kunnen) worden." + }, + "risks": { + "label": "Risico's", + "description": "Een overzicht van de voorziene risico's." + }, + "performance_standard": { + "label": "Prestatienormen", + "description": "Een omschrijving van de verwachte prestaties van het algoritme en hoe die worden gemeten." }, "juridisch": { "label": "Juridisch", - "competent_authority": { - "label": "Bevoegde autoriteit", - "description": "Volledige naam van de bevoegde autoriteit verantwoordelijk voor de inzet van het algoritme." - }, - "lawful_basis": { - "label": "Wettelijke grondslag", - "description": "Een omschrijving van de wettelijke grondslag voor de inzet van het algoritme, of URL van het formele besluit." - }, - "iama": { - "label": "Impact Assessment Mensenrechten en Algoritmes", - "description": "Is er een impact assessment mensenrechten en algoritmes (IAMA) uitgevoerd?" - }, - "iama_description": { - "label": "Omschrijving van de IAMA", - "description": "Een overzicht van de belangrijkste zaken die uit de impact assessment mensenrechten en algoritmes (IAMA) naar voren kwamen." - }, - "dpia": { - "label": "Data protection impact assessment", - "description": "Is er een data protection impact assessment (DPIA) uitgevoerd?" - }, - "dpia_description": { - "label": "Omschrijving van de DPIA", - "description": "Een overzicht van de belangrijkste zaken die uit de data protection impact assessment (DPIA) naar voren kwamen." - }, - "objection_procedure": { - "label": "Bezwaarprocedure", - "description": "Een omschrijving van hoe burgers en bedrijven bezwaar kunnen maken tegen de uitkomsten van het algoritme." - } + "description": "Geen beschrijving" + }, + "competent_authority": { + "label": "Bevoegde autoriteit", + "description": "Volledige naam van de bevoegde autoriteit verantwoordelijk voor de inzet van het algoritme." + }, + "lawful_basis": { + "label": "Wettelijke grondslag", + "description": "Een omschrijving van de wettelijke grondslag voor de inzet van het algoritme, of URL van het formele besluit." + }, + "iama": { + "label": "Impact Assessment Mensenrechten en Algoritmes", + "description": "Is er een impact assessment mensenrechten en algoritmes (IAMA) uitgevoerd?" + }, + "iama_description": { + "label": "Omschrijving van de IAMA", + "description": "Een overzicht van de belangrijkste zaken die uit de impact assessment mensenrechten en algoritmes (IAMA) naar voren kwamen." + }, + "dpia": { + "label": "Data protection impact assessment", + "description": "Is er een data protection impact assessment (DPIA) uitgevoerd?" + }, + "dpia_description": { + "label": "Omschrijving van de DPIA", + "description": "Een overzicht van de belangrijkste zaken die uit de data protection impact assessment (DPIA) naar voren kwamen." + }, + "objection_procedure": { + "label": "Bezwaarprocedure", + "description": "Een omschrijving van hoe burgers en bedrijven bezwaar kunnen maken tegen de uitkomsten van het algoritme." }, "metadata_algorithm": { "label": "Metadata", - "schema_metadata": { - "label": "Schema", - "description": "Het schema gebruikt voor deze registratie in machine leesbaar format." - }, - "uuid": { - "label": "UUID", - "description": "De unieke identificatie (UUID) voor deze registratie." - }, - "url": { - "label": "URL van de bronregistratie", - "description": "URL van deze registratie of van het algoritmeregister waarin deze registratie is vastgelegd in machine leesbaar format." - }, - "contact_email": { - "label": "E-mailadres van de contactpersoon", - "description": "e-mailadres van de organisatie of de contactpersoon voor deze registratie." - }, - "area": { - "label": "Geografisch gebied", - "description": "Het geografische gebied waarin het algoritme wordt ingezet." - }, - "lang": { - "label": "Taal", - "description": "De taal waarin deze registratie is ingevoerd." - }, - "revision_date": { - "label": "Herzieningsdatum", - "description": "De datum waarvoor deze registratie moet worden herzien." - } + "description": "Geen beschrijving" + }, + "schema_metadata": { + "label": "Schema", + "description": "Het schema gebruikt voor deze registratie in machine leesbaar format." + }, + "schema": { + "label": "Schema", + "description": "Het schema gebruikt voor deze registratie in machine leesbaar format." + }, + "uuid": { + "label": "UUID", + "description": "De unieke identificatie (UUID) voor deze registratie." + }, + "url": { + "label": "URL van de bronregistratie", + "description": "URL van deze registratie of van het algoritmeregister waarin deze registratie is vastgelegd in machine leesbaar format." + }, + "contact_email": { + "label": "E-mailadres van de contactpersoon", + "description": "e-mailadres van de organisatie of de contactpersoon voor deze registratie." + }, + "area": { + "label": "Geografisch gebied", + "description": "Het geografische gebied waarin het algoritme wordt ingezet." + }, + "lang": { + "label": "Taal", + "description": "De taal waarin deze registratie is ingevoerd." + }, + "revision_date": { + "label": "Herzieningsdatum", + "description": "De datum waarvoor deze registratie moet worden herzien." } + }, + "weekDays": { + "1": "zondag", + "2": "maandag", + "3": "dinsdag", + "4": "woensdag", + "5": "donderdag", + "6": "vrijdag", + "7": "zaterdag" + }, + "months": { + "1": "januari", + "2": "februari", + "3": "maart", + "4": "april", + "5": "mei", + "6": "juni", + "7": "juli", + "8": "augustus", + "9": "september", + "10": "oktober", + "11": "november", + "12": "december" } -} \ No newline at end of file +} diff --git a/frontend/pages/algoritme/[slug].vue b/frontend/pages/algoritme/[slug].vue index dd1999b5..cd3a0ee0 100644 --- a/frontend/pages/algoritme/[slug].vue +++ b/frontend/pages/algoritme/[slug].vue @@ -79,9 +79,7 @@ {{ - `${explanation}: ${ - property.attributeKeyDescription || t('Ontbreekt') - }` + `${property.attributeKeyDescription || t('ontbreekt')}` }} @@ -275,11 +273,9 @@ const structuredProperties = computed(() => { attributeKey: key, attributeValue: parsedValue, attributeKeyDescription: t( - `algorithmProperties.${attributeGroupKey}.${key}.description` - ), - attributeKeyLabel: t( - `algorithmProperties.${attributeGroupKey}.${key}.label` + `algorithmProperties.${key}.description` ), + attributeKeyLabel: t(`algorithmProperties.${key}.label`), } }) // only show field that are either required or not empty. An empty string is considered empty. diff --git a/frontend/pages/dashboard.vue b/frontend/pages/dashboard.vue new file mode 100644 index 00000000..2ac05a32 --- /dev/null +++ b/frontend/pages/dashboard.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/frontend/services/algoritme.ts b/frontend/services/algoritme.ts index 8ba3b2c5..b1bf8c30 100644 --- a/frontend/services/algoritme.ts +++ b/frontend/services/algoritme.ts @@ -15,4 +15,34 @@ const getNameIdOrg = () => baseURL: useRuntimeConfig().public.apiBaseUrl, }) -export default { getAll, getOne, getNameIdOrg } +const getCount = (column: string) => + useFetch(`/db-count/${column}`, { + baseURL: useRuntimeConfig().public.apiBaseUrl, + }) + +const getTotalCount = () => + useFetch(`/db-count/`, { + baseURL: useRuntimeConfig().public.apiBaseUrl, + }) + +const getColumns = () => + // useFetch<[{ table_name: string; column_name: string; is_nullable: string }]>( + useFetch(`/columns/`, { + baseURL: useRuntimeConfig().public.apiBaseUrl, + }) + +const getCountWithFilledColumns = (columns: string[] | string) => + useFetch(`/completeness/`, { + baseURL: useRuntimeConfig().public.apiBaseUrl, + query: { columns }, + }) + +export default { + getAll, + getOne, + getNameIdOrg, + getCount, + getTotalCount, + getColumns, + getCountWithFilledColumns, +}