Skip to content

Commit

Permalink
Merge branch '31-issue' into 'main'
Browse files Browse the repository at this point in the history
Resolve "Dashboard pagina, design van Roos"

Closes MinBZK#31, MinBZK#7, #44, MinBZK#36, MinBZK#39, MinBZK#22, MinBZK#29, MinBZK#14, MinBZK#27, MinBZK#28, MinBZK#1, MinBZK#8, MinBZK#5, and MinBZK#3

See merge request ictu/devops/algoritmeregister/algoritmeregister!27
  • Loading branch information
larsmustersICTU committed Dec 13, 2022
2 parents 9442934 + f6b049b commit 5a5fe82
Show file tree
Hide file tree
Showing 15 changed files with 928 additions and 313 deletions.
65 changes: 65 additions & 0 deletions backend/app/config/resource.py
Original file line number Diff line number Diff line change
@@ -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
6 changes: 4 additions & 2 deletions backend/app/main.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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=["*"]
Expand Down
79 changes: 79 additions & 0 deletions backend/app/routers/aggregations.py
Original file line number Diff line number Diff line change
@@ -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)
4 changes: 4 additions & 0 deletions frontend/assets/styles/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
cursor: pointer;
}

.block-info {
max-width: 600px;
}

.noselect {
user-select: none;
-moz-user-select: none;
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/SearchResultCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<!-- <p> -->
<dl class="dl columns--data">
<div v-for="sT in summaryTiles">
<dt>{{ t(`algorithmProperties.algemeneInformatie.${sT}.label`) }}</dt>
<dt>{{ t(`algorithmProperties.${sT}.label`) }}</dt>
<dd class="no-bottom-margin">
{{ algoritme[sT as keyof typeof algoritme] }}
</dd>
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/algoritme/AlgoritmeFilters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<h2>
{{
$t(
`algorithmProperties.algemeneInformatie.${aggregationType.aggregationAttribute}.label`
`algorithmProperties.${aggregationType.aggregationAttribute}.label`
)
}}
</h2>
Expand Down
100 changes: 100 additions & 0 deletions frontend/components/dashboard/Completeness.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<template>
<div class="block-cards__card">
<div class="block-info">
<div class="rows">
<h3>
{{ title }}
</h3>
<div class="row">
<div class="loading-text" v-if="loading">{{ loadingText }}...</div>
<table class="table" v-show="!loading">
<thead>
<tr>
<th class="u-columnwidth-50p">{{ tableHeader }}:</th>
<th class="u-columnwidth-10p"></th>
</tr>
</thead>
<tbody>
<tr v-for="agg in aggregates">
<td>{{ agg.label }}</td>
<td>{{ agg.value }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import algoritmeService from '@/services/algoritme'
const { t } = useI18n()
const title = computed(() => t('dashboard.completenessTitle'))
const tableHeader = computed(() => t('dashboard.tableHeader'))
const loadingText = computed(() => t('dashboard.loadingText'))
const props = defineProps<{
nAlgorithms: number
}>()
const getFullyComplete = async () => {
const result = await algoritmeService.getCountWithFilledColumns('*')
return result.data.value
}
const getMandatoryComplete = async () => {
const notNullableColumns = data.value.filter(
(column: any) => column.is_nullable == 'NO'
)
const columns = notNullableColumns.map(
(c: any) => `${c.table_name}.${c.column_name}`
)
const result = await algoritmeService.getCountWithFilledColumns(columns)
loading = false
return result.data.value
}
const getMandatoryMissing = async () => {
return (
props.nAlgorithms -
(await getFullyComplete()) -
(await getMandatoryComplete())
)
}
const aggregates = ref<{ label: string; value: string }[]>([])
const setAggregates = async () => {
aggregates.value = [
{
label: t('dashboard.fullyComplete'),
value: await getFullyComplete(),
},
{
label: t('dashboard.mandatoryComplete'),
value: await getMandatoryComplete(),
},
{
label: t('dashboard.mandatoryMissing'),
value: await getMandatoryMissing(),
},
]
}
var loading = true
const { data } = await algoritmeService.getColumns().then((value) => {
setAggregates()
return value
})
</script>

<style lang="scss">
.word-break {
word-break: break-word;
}
.loading-text {
text-align: center;
}
</style>
Loading

0 comments on commit 5a5fe82

Please sign in to comment.