dapla-toolbelt-metadata

Sist endret

January 31, 2025

dapla-toolbelt-metadata er en Python-pakke for å jobbe med metadatasystemene på Dapla. Pakken gir brukeren et Python-grensesnitt for å jobbe mot Datadoc og Vardef.

Forberedelser

dapla-toolbelt-metadata kan installeres i tjenester på Dapla Lab. Siden det er en Python-pakke så må den installeres i en tjeneste der Python er installert. Deretter gjør du følgende:

  1. Åpne en tjeneste på Dapla Lab med Python installert. Før du åpner tjenesten må du velge å representere team og tilgangsgruppe som har tilgang til dataene som skal dokumenteres.
  2. Installer pakken i et ssb-project på følgende måte:
Terminal
poetry add dapla-toolbelt-metadata

Etter det er du klar for bruke funksjonaliteten i dapla-toolbelt-metadata i en notebook.

Dapla Lab og dapla-toolbelt-metadata

I Dapla Lab velger man hvilket team og tilgangsgruppe man skal representere før man åpner en tjeneste. Hvis man f.eks. skal dokumentere et datasett i datatilstanden inndata for et team som heter dapla-felles, så må man logge seg inn i en tjeneste som dapla-felles-developers, hvis ikke har man ikke tilgang til datasettet.

I tillegg så benytter Vardef bl.a. informasjon om hvilket team og tilgangsgruppe du logget deg inn som, for definere hvilket team som blir eier av en nyopprettet variabeldefinisjon. På samme måte bruker Vardef denne informasjonen til å avgjøre om en bruker har tilgang til å endre en definisjon. F.eks. vil en bruker som logger seg inn som dapla-felles-developers og oppretter en ny definisjon, så vil dapla-felles stå som eier av definisjonen og det vil kun være medlemmer av dette teamet som kan gjøre endringer i definisjonen.

Datadoc

Datadoc er SSBs system for dokumentasjon av datasett. Første gang man skal dokumentere et datasett i Datadoc så er det anbefalt å bruke det grafiske grensesnittet i Datadoc-editor. I løpende produksjon er det dermed anbefalt å benytte en programmatisk tilnærming gjennom Datadoc-klassen i Python-pakken dapla-toolbelt-metadata.

Dokumentere ny periode

Hvis man har dokumentert datasett for periode t med Datadoc-editor, så kan man programmatisk dokumentere periode t+1 ved å benytte Datadoc-klassen i dapla-toolbelt-metadata. Det forutsetter at det kun nye data som har kommet til og at det er den eneste endringen i dataene. Da kan man dokumentere den nye perioden på følgende måte:

Notebook
from dapla_metadata.datasets import Datadoc

meta = Datadoc(
1    dataset_path="gs://ssb-dapla-felles-data-produkt-prod/datadoc/sykefratot/klargjorte_data/person_testdata_p2022_v1.parquet",
2    metadata_document_path="gs://ssb-dapla-felles-data-produkt-prod/datadoc/sykefratot/klargjorte_data/person_testdata_p2021_v1__DOC.json",
)
3meta.write_metadata_document()
1
dataset_path angir det nye datasettet som skal dokumenteres.
2
metadata_document_path angir sti til tidligere periodes metadata.
3
write_metadata_document er kommandoen som produserer de nye metadataene og skriver de til filen gs://ssb-dapla-felles-data-produkt-prod/datadoc/sykefratot/klargjorte_data/person_testdata_p2022_v1__DOC.json.

Det veldig viktig at man ikke bruker denne metoden hvis det er endringer i hvilke kolonner som finnes i datasettet eller andre større endringer. Metoden over antar at den eneste informasjonen som har endret seg er den som kan leses ut av filstien. Ved større endringer i selve dataene bør man heller gjøre en manuell gjennomgang av metadataene med Datadoc-editor

Vardef (WIP👷‍♂️)

Vardef er SSBs system for dokumentasjon av variabler. Vardef består av et sentralt datalager som man kan interagere med via et API. Statistikere og forskere i SSB kan benytte seg av API-et gjennom Vardef-klassen i Python-pakken dapla-toolbelt-metadata, som er et mer brukervennlig grensesnitt tilpasset Dapla Lab. Pakken lar brukeren bl.a. gjøre følgende:

  • Liste ut alle definisjoner.
  • Migrere definisjoner fra Vardok1.
  • Opprette ny definisjon
  • Endre eksisterende definisjon

Eksemplene under forutsetter at du har gjort følgende importer i en notebook på Dapla Lab:

Notebook
from dapla_metadata.variable_definitions import Vardef, models

Liste ut variabeldefinisjoner

For å lese inn alle variabeldefinisjoner fra Vardef kan man bruke list_variable_definitions()-funksjonen:

Notebook
variable_definitions = Vardef.list_variable_definitions()
variable_definitions

list_variable_definitions() returnerer en liste med dictionaries med alle variabeldefinisjoner i Vardef, inkludert Utkast, Publisert internt og Publisert eksternt. Under er det noen kode-eksempler på hvordan man kan kan filtrere eller endre på denne listen med Python for de som måtte ønske det.

Filtrere enkeltfelt for alle definisjoner

Bruk følgende kode for å liste ut ID, navn, kortnavn, definisjon, eierteam og status for alle definisjoner i Vardef:

Notebook
for variable in variable_definitions:
    print(
        f"Id: {variable.id}\n"
        f"Navn: {variable.name}\n"
        f"Kortnavn: {variable.short_name}\n"
        f"Definisjon: {variable.definition}\n"
        f"Eier: {variable.owner}\n"
        f"Status: {variable.variable_status}\n"
    )
Notebook
Id: 5eC5YUVR
Navn: {'nb': 'test navn', 'nn': 'test namn', 'en': 'test name'}
Kortnavn: test_shortname2
Definisjon: {'nb': 'Skriv en definisjonstekst på norsk bokmål.', 'nn': 'Skriv ein definisjonstekst på nynorsk.', 'en': 'Write a definition text in english.'}
Eier: {'team': 'dapla-felles', 'groups': ['dapla-felles-developers']}
Status: VariableStatus.PUBLISHED_INTERNAL

Id: fDun32UF
Navn: {'nb': 'test navn', 'nn': 'test namn', 'en': 'test name'}
Kortnavn: test_shortname_obr
Definisjon: {'nb': 'Skriv en definisjonstekst på norsk bokmål.', 'nn': 'Skriv ein definisjonstekst på nynorsk.', 'en': 'Write a definition text in english.'}
Eier: {'team': 'dapla-felles', 'groups': ['dapla-felles-developers']}
Status: VariableStatus.DRAFT

Id: Xww0Swou
Navn: {'nb': 'Inntekt etter skatt', 'nn': None, 'en': 'After-tax income'}
Kortnavn: wies
Definisjon: {'nb': 'Yrkesinntekter, kapitalinntekter, skattepliktige og skattefrie overføringer, i løpet av kalenderåret. Utlignet skatt og negative overføringer er trukket i fra.', 'nn': None, 'en': 'After-tax income is calculated as the sum of wages and salaries, income from self-employment, property income and transfers received minus total assessed taxes and negative transfers.'}
Eier: {'team': 'dapla-felles', 'groups': ['dapla-felles-developers']}
Status: VariableStatus.DRAFT

Id: ouMrO8E6
Navn: {'nb': 'test navn', 'nn': 'test namn', 'en': 'test name'}
Kortnavn: test_shortname
Definisjon: {'nb': 'Skriv en definisjonstekst på norsk bokmål.', 'nn': 'Skriv ein definisjonstekst på nynorsk.', 'en': 'Write a definition text in english.'}
Eier: {'team': 'dapla-felles', 'groups': ['dapla-felles-developers']}
Status: VariableStatus.DRAFT

Filtrere på ID

Hvis man vet ID-en til definisjon i Vardef så kan man hente ut variabeldefinisjonen med følgende kode:

Notebook
single_definition = Vardef.get_variable_definition("fDun32UF")
print(single_definition)

I eksempelet printes all feltene i definisjonen med ID fDun32UF.

Notebook
{
  "id": "fDun32UF",
  "patch_id": 1,
  "name": {
    "nb": "test navn",
    "nn": "test namn",
    "en": "test name"
  },
  "short_name": "test_shortname_obr",
  "definition": {
    "nb": "Skriv en definisjonstekst på norsk bokmål.",
    "nn": "Skriv ein definisjonstekst på nynorsk.",
    "en": "Write a definition text in english."
  },
  "classification_reference": "91",
  "unit_types": [
    "01"
  ],
  "subject_fields": [
    "al"
  ],
  "contains_special_categories_of_personal_data": false,
  "variable_status": "DRAFT",
  "measurement_type": null,
  "valid_from": "2024-12-18",
  "valid_until": null,
  "external_reference_uri": null,
  "comment": null,
  "related_variable_definition_uris": null,
  "owner": {
    "team": "dapla-felles",
    "groups": [
      "dapla-felles-developers"
    ]
  },
  "contact": null,
  "created_at": "2025-02-03T13:15:44.366000",
  "created_by": "obr@ssb.no",
  "last_updated_at": "2025-02-03T13:15:44.366000",
  "last_updated_by": "obr@ssb.no"
}

Filtrere på gyldighetsperiode

Siden alle definisjoner i Vardef er versjonert og har en gyldighetsperiode, så kan man filtrere hvilke definisjoner som var gyldig på et gitt tidspunkt:

Notebook
from datetime import date

desired_validity = date(2030,2,24)
filtered_variable_definitions = Vardef.list_variable_definitions(desired_validity)

I koden over hentes alle som definert som gyldig per 24. februar 2030, inkludert definisjoner som ikke har en sluttdato. Vi importerer også datetime-pakken for å sikre at datoformatet blir riktig.

Vi kan igjen printe spesifikk informasjon om felter vi er interessert i:

Notebook
print(f"Valid at {desired_validity}:\n")

for variable in filtered_variable_definitions:
    print(
        f"Id: {variable.id}\n"
        f"Name: {variable.name}\n"
        f"Short name: {variable.short_name}\n"
        f"Definition: {variable.definition}\n"
        f"Owner: {variable.owner}\n"
        f"Valid: {variable.valid_from} to {variable.valid_until}\n"
    )
Notebook
Valid at 2030-02-24:

Id: fDun32UF
Name: {'nb': 'test navn', 'nn': 'test namn', 'en': 'test name'}
Short name: test_shortname_obr
Definition: {'nb': 'Skriv en definisjonstekst på norsk bokmål.', 'nn': 'Skriv ein definisjonstekst på nynorsk.', 'en': 'Write a definition text in english.'}
Owner: {'team': 'dapla-felles', 'groups': ['dapla-felles-developers']}
Valid: 2024-12-18 to None

Id: Xww0Swou
Name: {'nb': 'Inntekt etter skatt', 'nn': None, 'en': 'After-tax income'}
Short name: wies
Definition: {'nb': 'Yrkesinntekter, kapitalinntekter, skattepliktige og skattefrie overføringer, i løpet av kalenderåret. Utlignet skatt og negative overføringer er trukket i fra.', 'nn': None, 'en': 'After-tax income is calculated as the sum of wages and salaries, income from self-employment, property income and transfers received minus total assessed taxes and negative transfers.'}
Owner: {'team': 'dapla-felles', 'groups': ['dapla-felles-developers']}
Valid: 1993-01-01 to None

Filtrere på eierteam

Man kan filtrere ut alle definisjoner som eies av speisifikt team. I eksempelet under listes det ut hvilke definisjoner som eies av team dapla-felles:

Notebook
owner_team = "dapla-felles"
print(f"\nFiltrer etter eierteam {owner_team}: \n")
my_team_variables = [variable for variable in variable_definitions if variable.owner["team"] == owner_team]

for variable in my_team_variables:
    print(f"Id: {variable.id}\nName: {variable.name['nb']}\nShort name: {variable.short_name}\nDefinition: {variable.definition['nb']}\nOwner: {variable.owner['team']}\n")
Notebook
Filtrer etter eierteam dapla-felles: 

Id: fDun32UF
Name: test navn
Short name: test_shortname_obr
Definition: Skriv en definisjonstekst på norsk bokmål.
Owner: dapla-felles

Id: Xww0Swou
Name: Inntekt etter skatt
Short name: wies
Definition: Yrkesinntekter, kapitalinntekter, skattepliktige og skattefrie overføringer, i løpet av kalenderåret. Utlignet skatt og negative overføringer er trukket i fra.
Owner: dapla-felles

Filtrere etter status

Man kan filtrere etter status for en definisjon. Her filtrerer vi ut alle definisjoner med status Draft:

Notebook
print("\nFiltrer etter status `DRAFT`: ")
draft_variables = [variable for variable in variable_definitions if variable.variable_status == models.VariableStatus.DRAFT]
print(draft_variables)

På samme måte kan vi filtrere ut definisjoner som er publisert internt:

Notebook
print("\nFiltrer etter status `PUBLISHED INTERNAL`: ")
published_intern_variables = [variable for variable in variable_definitions if variable.variable_status == models.VariableStatus.PUBLISHED_INTERNAL]

print(published_intern_variables)

Filtrere etter kortnavn

Man kan hente ut en definisjon basert på kortnavn på følgende måte:

Notebook
variable_by_short_name = next(variable for variable in variable_definitions if variable.short_name == "wies")

print(variable_by_short_name)

Enkeltfelter i en definisjon

Hvis man kun ønsker å hente ut verdien til et enkeltfelt i en variabeldefinisjon kan man gjøre det på følgende måte:

Notebook
wies_vardef = next(variable for variable in variable_definitions if variable.short_name == "wies")
wies_definition_nb = wies_vardef.definition["nb"]
wies_definition_nb
Notebook
'Yrkesinntekter, kapitalinntekter, skattepliktige og skattefrie overføringer, i løpet av kalenderåret. Utlignet skatt og negative overføringer er trukket i fra.'

I koden over hentes beskrivelsen av variabelen med kortnavn wies på norsk bokmål. Spørring returnerer en string-objekt.

Opprette ny definisjon

Når man skal opprette en ny definisjon i Vardef, så må man forholde seg til hvordan eierskapet til definisjonen blir definert. Nye definisjoner kan kun opprettes fra en tjeneste på Dapla Lab med Python installert, og teamet man velger å representere når man logger seg inn i tjenesten, blir automatisk satt som eier av definisjonen i Vardef. Dette kan endres senere, men det letteste er at noen fra teamet som skal eie definisjonen gjør opprettelsen fra Dapla Lab.

Stegene for å opprette en ny variabeldefinisjon er som følger:

  1. Opprette et utkast (draft) av definisjonen med all obligatorisk informasjon.
  2. Publisere variabelen internt eller eksternt.

Publisering av en definisjon internt at det kun vil være SSB-ansatte som får tilgang til den. Å publisere eksternt betyr at den er tilgjengelige for andre også. Før man publiserer et utkast er det god praksis at informasjonen er kvalitetssikret av flere personen, spesielt siden en publisert definisjon aldri kan slettes. Endringer i en definisjon genererer kun en ny versjon eller mindre endring.

Opprette et utkast

Første steg for å publisere en ny variabeldefinisjon til Vardef er å opprette et utkast eller draft. Et utkast må inneholde all obligatorisk informasjon for en variabeldefinisjon. Et viktig poeng her er at utkastet også lagres datalageret til Vardef, og det sjekkes derfor ved opprettelse om all obligatorisk informasjon er fylt ut. Men når en variabel har status utkast i Vardef så er det tillatt å endre alle felter uten at en ny versjon opprettes.

Man kan opprette en ny definisjon ved å bruke Draft-funksjonen models-klassen:

Notebook
utkast = models.Draft(
    name = {
        "nb": "test navn",
        "nn": "test namn",
        "en": "test name",
    },
    short_name= "test_shortname",
    definition= {
        "nb": "Skriv en definisjonstekst på norsk bokmål.",
        "nn": "Skriv ein definisjonstekst på nynorsk.",
        "en": "Write a definition text in english.",
    },
    classification_reference="91",
    unit_types=[
        "01",
    ],
    subject_fields=[
        "al",
    ],
    contains_special_categories_of_personal_data=False,
    measurement_type=None,
    valid_from=date(2024,12,18),
    external_reference_uri=None,
    comment=None,
    related_variable_definition_uris=None,
    contact=None,
)

my_draft = Vardef.create_draft(utkast)

I koden over så har vi fylt inn all obligatorisk informasjon for de nye variabeldefinisjon i utkast-variabelen. Deretter har vi publisert den til datalageret til Vardef med create_draft-funksjonen. I tillegg ble den nye variabelen lagret i en variabel kalt my_draft for å lettere kunne endre på variabelen senere.

Vi kan innholdet i variabelen som vi har lagret i datalageret til Vardef på følgende måte:

Notebook
print(my_draft)

Vi kan også aksessere enkeltfelter via dette objektet. F.eks. kan vi hente ut det maskingenererte feltet created_by for å validere brukerens navn er registrert:

Notebook
print(my_draft.created_by)

Publisere et utkast

Etter at informasjonen i et utkast er kvalitetssikret og ferdig, kan den publiseres internt (kun SSB-ansatte) eller eksternt (åpent for alle). Det er verdt å merke seg at en definisjon som er publisert eksternt ikke kan endres til å kun være publisert internt.

Når man publiserer en definisjon er det anbefalt å bruke VariableStatus-klassen for å minimere sannsynligheten for skrivefeil i dette viktige steget.

Notebook
update_status = models.UpdateDraft(
    variable_status=models.VariableStatus.PUBLISHED_INTERNAL,
)

my_draft.update_draft(update_status)

Lister man ut alle variabeldefinisjoner vil man nå se at denne variabelen har fått
"variable_status": "PUBLISHED_INTERNAL".

Endre eksisterende definisjon

Variabeldefinisjoner i Vardef har enten status som publisert internt, publisert eksternt eller utkast. En definisjon med status utkast kan endres av eierteamet på en enkel måte. Men endringer i variablerdefinisjoner som er publisert internt eller eksternt omfattes av prinsippet om uforanderlighet i Vardef. En definisjon som har blitt publisert slettes aldri, og endringer kan kun skje ved å opprette nye versjoner. Det finnes to typer av versjoner i Vardef:

  1. Mindre endringer (patches)
    Mindre endringer som ikke betyr noen innholdsmessige endring i beskrivelsen av variabelen. F.eks. retting av skrivefeil, legge til en oversettelse eller legge til subject_field.
  2. Større endringer
    Endringer som innebærer innholdsmessige endringer i variabelen. F.eks. en ny skatteregel krever at beskrivelsen for en skattevariabel må oppdateres.

Slette et utkast

Man kan slette et utkast på følgende måte:

Notebook
my_vardok_draft.delete_draft()

Dette kan kun gjøres på definisjoner med status utkast/draft.

Mindre endringer

Mindre endringer, kalt patches i Vardef, er mindre endringer som ikke krever en ny gyldighetsperiode for variabelen. Under er det noen eksempler på slike endringer og hvordan de gjøres:

Endre eierteam
Notebook
from dapla_metadata.variable_definitions.generated.vardef_client.models.owner import (
    Owner,
)
from dapla_metadata.variable_definitions.generated.vardef_client.models.patch import (
    Patch,
)

new_owner = Patch(
    owner=Owner(
    team="dapla-felles",
    groups=[
        "dapla-felles-developers",
        "play-enhjoern-a-developers",
    ],
),
)

my_draft.create_patch(new_owner)

my_draft = Vardef.get_variable_definition(my_draft.id)

print(my_draft.owner)

Større endringer

For å opprette en ny gyldighetsperiode må inndataene inneholde oppdatert beskrivelsestekst for alle gjeldende språk og en ny gyldig fra dato. En ny gyldighetsperiode bør kun opprettes når den grunnleggende definisjonen av variabelen har endret seg.

Notebook
valid_validity_period = models.ValidityPeriod(
    definition={
        "nb": "ny definisjon2",
        "nn": "ny definisjon2",
        "en": "new definition2",
    },
    valid_from=date(2040,4,5),
)
my_draft.create_validity_period(valid_validity_period)

Migrere definisjon fra Vardok

Eksternt publiserte variabeldefinisjoner fra Vardok kan migreres til Vardef med funksjonen migrate_from_vardok()-funksjonen i dapla-toolbelt-metadata. For å migrere en variabeldefinisjon må man referere til variabelens ID i Vardok. Siden alle definisjoner er eid av en seksjon så har alle seksjoner en oversikt over hvilke definisjoner de er ansvarlig for, med tilhørende ID, og skal kun migrere disse definisjonene.

migrate_from_vardok()-funksjonen migrerer alle feltene fra Vardok som skal videreføres til Vardef, oppdaterer felter automatisk der det er mulig, og publiserer den som et Utkast i Vardef. Deretter må hvert fagansvarlig gå gjennom all informasjon, gjøre nødvendige endringer og kvalitetssikre informasjonen, før den kan publiseres. Husk at eierteamet for definisjon blir bestemt av hvilket team man representerer i Dapla Lab når migrate_from_vardok()-funksjonen kjøres.

I koden under vises et eksempel der definisjonen for Spesifisert registreringstype migreres fra Vardok til Vardef. ID-en til variabelen er 90 og derfor er det denne det refereres til i funksjonen:

Notebook
my_vardok_draft = Vardef.migrate_from_vardok("90")
print(my_vardok_draft)

Alle definisjoner i Vardef må ha et gyldig kortnavn,. Hvis en definisjon som migreres fra Vardok ikke har et kortnavn så vil det genereres et midlertidig kortnavn ved migrering. Dette kortnavnet må endres før definisjonen publiseres internt eller eksternt. Automatisk genererte kortnavn starter alltid med generert. Man kan liste ut alle definisjoner i Vardef med et generert kortnavn for et gitt Dapla-team, med følgende kode:

Notebook
my_vardok_missing_short_name = Vardef.migrate_from_vardok("123")

my_owner_team = "dapla-felles"

my_team_variables_generert_kortnavn = [
    {variable.short_name, variable.id}
    for variable in Vardef.list_variable_definitions()
    if variable.owner["team"] == my_owner_team and variable.short_name.startswith("generert")
]

print(my_team_variables_generert_kortnavn)
Definisjoner i Vardok kan kun migreres en gang

En definisjon i Vardok kan kun migreres en gang. Hvis noen flere prøver å migrere en variabel vil man få en feilmelding i dapla-toolbelt-pseudo. Hvis den migrerte definisjonen har status Utkast, så er det mulig for eierteamet å slette utkastet, slik at et annet team kan migrere definisjonen. Eierteamet kan også overføre eierskapet til en definisjon i Vardef til et annet team.

Fotnoter

  1. Vardok er SSBs tidligere system for variabeldefinisjoner og som erstattes av Vardef.↩︎