From fecbe9db225c21260ae262578678c40bc77b6719 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Tue, 21 Jan 2025 13:35:32 +0100 Subject: [PATCH 1/5] Reparer GitHub Action test workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit En ændring i seneste version af Ubuntus "libaio1"-pakke ødelagde Oracle værktøjerne. Problemet er omgået ved at lave et symlink til erstatningen for libaio1, der heldigvis kan bruges som en erstatning. I samme ombæring er links til Oracles pakker ændret, så der altid hentes den nyeste version af disse. --- .github/workflows/tests.yml | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a13a8ba1..91d53ff5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,21 +36,28 @@ jobs: cd /opt/oracle # Download links findes her: https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html - wget -nv https://download.oracle.com/otn_software/linux/instantclient/2340000/instantclient-basic-linux.x64-23.4.0.24.05.zip - wget -nv https://download.oracle.com/otn_software/linux/instantclient/2340000/instantclient-sqlplus-linux.x64-23.4.0.24.05.zip + wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-basic-linuxx64.zip + wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-sqlplus-linuxx64.zip - unzip -o instantclient-basic-linux.x64-23.4.0.24.05.zip - unzip -o instantclient-sqlplus-linux.x64-23.4.0.24.05.zip + unzip -o instantclient-basic-linuxx64.zip + unzip -o instantclient-sqlplus-linuxx64.zip - ls -la /opt/oracle/instantclient_23_4 + # Oracle koder versionsnummeret ind i mappenavnet, fx "instantclient_23_6", selv når man henter "latest"-pakken + # Derfor omdøbes den som første skridt herunder. + mv instantclient_* instantclient + ls -la /opt/oracle/instantclient - sudo apt-get install libaio1 + sudo apt-get install libaio1t64 + # Ubuntu har med version 24.04 ændret navnet på libaio1 pakken og tilhørende .so-fil. + # Det har Oracle selvfølgelig ikke lige taget højde for, så vi hjælper lidt ved at pege + # et symlink i retning af den nye .so-fil. + sudo ln -s /usr/lib/x86_64-linux-gnu/libaio.so.1t64 /usr/lib/x86_64-linux-gnu/libaio.so.1 - sudo sh -c "echo /opt/oracle/instantclient_23_4 > /etc/ld.so.conf.d/oracle-instantclient.conf" + sudo sh -c "echo /opt/oracle/instantclient > /etc/ld.so.conf.d/oracle-instantclient.conf" sudo ldconfig - export LD_LIBRARY_PATH=/opt/oracle/instantclient_23_4:$LD_LIBRARY_PATH - export PATH=/opt/oracle/instantclient_23_4:$PATH + export LD_LIBRARY_PATH=/opt/oracle/instantclient:$LD_LIBRARY_PATH + export PATH=/opt/oracle/instantclient:$PATH # Gem miljøvariable til brug i de følgende trin echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> "$GITHUB_ENV" From f87ddb9a98570c221cb8493d5c26f360741ce5f8 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Tue, 14 Jan 2025 21:56:26 +0100 Subject: [PATCH 2/5] =?UTF-8?q?fire=20info=20sag:=20Forkort=20lange=20titl?= =?UTF-8?q?er=20p=C3=A5=20sagsevents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For at gøre listen af sagsevent mere overskuelig trunkeres strenge, der er længere end 40 tegn. --- fire/cli/info.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fire/cli/info.py b/fire/cli/info.py index a88a5323..b3d7e474 100644 --- a/fire/cli/info.py +++ b/fire/cli/info.py @@ -734,6 +734,10 @@ def sag(sagsid: str, **kwargs): for sagsevent in sag.sagsevents: try: beskrivelse = sagsevent.beskrivelse + max_beskrivelseslængde = 40 + if len(beskrivelse) > max_beskrivelseslængde: + beskrivelse = beskrivelse[0:max_beskrivelseslængde] + "..." + except IndexError: beskrivelse = "" eventtype = ( From 0e62f90654ecf0837416581de93934d1ec555fee Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Tue, 14 Jan 2025 22:23:57 +0100 Subject: [PATCH 3/5] =?UTF-8?q?fire=20info=20sag:=20Tilf=C3=B8j=20uuid=20i?= =?UTF-8?q?=20udskrift=20af=20sagsevents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tidsstemplet begrænset kun til datoen for at give bedre overblik. Af samme årsag skrives kun de første 8 tegn af sagseventid'et. --- fire/cli/info.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fire/cli/info.py b/fire/cli/info.py index b3d7e474..2af407d0 100644 --- a/fire/cli/info.py +++ b/fire/cli/info.py @@ -743,7 +743,9 @@ def sag(sagsid: str, **kwargs): eventtype = ( str(sagsevent.eventtype).replace("EventType.", "").replace("OE", "Ø") ) - fire.cli.print(f"[{sagsevent.registreringfra}] {eventtype}: {beskrivelse}") + tid = sagsevent.registreringfra.strftime("%Y-%m-%d") + sagseventid = sagsevent.id[0:8] + fire.cli.print(f"[{tid}|{sagseventid}] {eventtype}: {beskrivelse}") return From 985e9e892e2a27b76e433e321a92e73e49a85c92 Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Wed, 22 Jan 2025 10:10:27 +0100 Subject: [PATCH 4/5] =?UTF-8?q?Undg=C3=A5=20fejl=20n=C3=A5r=20sagsevent=20?= =?UTF-8?q?ikke=20har=20tilknyttet=20sagseventinfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kald til `Sagsevent.beskrivelse` resulterede i en `IndexError` hvis ikke, der er tilknyttet sagseventinfo til det adspurgte sagsevent. Løst ved at lave et tjek for om det findes. --- fire/api/model/sagstyper.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fire/api/model/sagstyper.py b/fire/api/model/sagstyper.py index 9f02ba87..6db53115 100644 --- a/fire/api/model/sagstyper.py +++ b/fire/api/model/sagstyper.py @@ -290,6 +290,8 @@ class Sagsevent(RegisteringFraObjekt): @property def beskrivelse(self) -> str: + if not self.sagseventinfos: + return "" if self.sagseventinfos[-1].beskrivelse is None: return "" return self.sagseventinfos[-1].beskrivelse From e61571f45d58664078e29b5e7f38e1f19404768d Mon Sep 17 00:00:00 2001 From: Kristian Evers Date: Tue, 14 Jan 2025 22:32:01 +0100 Subject: [PATCH 5/5] =?UTF-8?q?Tilf=C3=B8j=20`fire=20info=20sagsevent`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Viser alle dataobjekter, der er tilknyttet et specifikt sagsevent. --- docs/apps/info.rst | 4 + fire/api/firedb/hent.py | 13 +++ fire/cli/info.py | 205 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 216 insertions(+), 6 deletions(-) diff --git a/docs/apps/info.rst b/docs/apps/info.rst index 1b9b6fdd..8791d1dc 100644 --- a/docs/apps/info.rst +++ b/docs/apps/info.rst @@ -16,6 +16,10 @@ typer indhold i FIRE databasen. :prog: fire info sag :nested: full +.. click:: fire.cli.info:sagsevent + :prog: fire info sagsevent + :nested: full + .. click:: fire.cli.info:srid :prog: fire info srid :nested: full diff --git a/fire/api/firedb/hent.py b/fire/api/firedb/hent.py index aa77a389..88c315fa 100644 --- a/fire/api/firedb/hent.py +++ b/fire/api/firedb/hent.py @@ -13,6 +13,7 @@ from fire.api.firedb.base import FireDbBase from fire.api.model import ( Sag, + Sagsevent, Punkt, PunktSamling, PunktInformation, @@ -213,6 +214,18 @@ def hent_sag(self, sagsid: str) -> Sag: """ return self.session.query(Sag).filter(Sag.id.ilike(f"{sagsid}%")).one() + def hent_sagsevent(self, sagseventid: str) -> Sagsevent: + """ + Hent et sagsevent ud fra dens sagseventid. + + Sagseventid'er behøver ikke være fuldstændige, funktionen forsøger at matche + partielle sagseventid'er i samme stil som git håndterer commit hashes. I + tilfælde af at søgningen med et partielt sagseventid resulterer i flere + matches udsendes en sqlalchemy.orm.exc.MultipleResultsFound exception. + """ + return self.session.query(Sagsevent).filter(Sagsevent.id.ilike(f"{sagseventid}%")).one() + + def hent_alle_sager(self, aktive=True) -> List[Sag]: """ Henter alle sager fra databasen. diff --git a/fire/cli/info.py b/fire/cli/info.py index 2af407d0..cc0909ca 100644 --- a/fire/cli/info.py +++ b/fire/cli/info.py @@ -1,10 +1,13 @@ import datetime +import io import itertools +import re import textwrap from typing import List +import zipfile import click -from sqlalchemy.orm.exc import NoResultFound +from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound from sqlalchemy import not_, or_ from pyproj import CRS from pyproj.exceptions import CRSError @@ -12,6 +15,7 @@ import fire.cli from fire.ident import klargør_ident_til_søgning from fire.api.model import ( + EventType, Punkt, PunktInformation, PunktInformationType, @@ -21,6 +25,7 @@ Boolean, Srid, Tidsserie, + Grafik, ) @@ -271,7 +276,12 @@ def punktsamlingsrapport(punktsamlinger: list[PunktSamling], id: str = None): """ Hjælpefunktion for funktionerne punkt_fuld_rapport og punktsamling. """ - kolonnebredder = (34, 11, 13, 16,) + kolonnebredder = ( + 34, + 11, + 13, + 16, + ) kolonnenavne = ("Navn", "Jessenpunkt", "Antal punkter", "Antal tidsserier") header = " ".join([str(n).ljust(w) for n, w in zip(kolonnenavne, kolonnebredder)]) subheader = " ".join(["-" * w for w in kolonnebredder]) @@ -287,10 +297,18 @@ def punktsamlingsrapport(punktsamlinger: list[PunktSamling], id: str = None): if ps.jessenpunkt.id == id: farve = "green" - kolonner = [ps.navn, ps.jessenpunkt.jessennummer, len(ps.punkter), len(ps.tidsserier)] + kolonner = [ + ps.navn, + ps.jessenpunkt.jessennummer, + len(ps.punkter), + len(ps.tidsserier), + ] linje = " ".join( - [textwrap.shorten(str(c), width=w, placeholder="...").ljust(w) for c, w in zip(kolonner, kolonnebredder)] + [ + textwrap.shorten(str(c), width=w, placeholder="...").ljust(w) + for c, w in zip(kolonner, kolonnebredder) + ] ) fire.cli.print(linje, fg=farve) @@ -316,11 +334,16 @@ def tidsserietype(tstype): return "Højde" for ts in tidsserier: - navn_ombrudt = textwrap.wrap(str(ts.navn),kolonnebredder[0]) + navn_ombrudt = textwrap.wrap(str(ts.navn), kolonnebredder[0]) for navn_del in navn_ombrudt[:-1]: fire.cli.print(navn_del) - kolonner = [navn_ombrudt[-1], len(ts), tidsserietype(ts.tstype), ts.referenceramme] + kolonner = [ + navn_ombrudt[-1], + len(ts), + tidsserietype(ts.tstype), + ts.referenceramme, + ] linje = " ".join([str(c).ljust(w) for c, w in zip(kolonner, kolonnebredder)]) fire.cli.print(linje) @@ -757,6 +780,176 @@ def sag(sagsid: str, **kwargs): fire.cli.print(f"{sag.id[0:8]}: {sag.behandler:20}{beskrivelse}...") +@info.command() +@fire.cli.default_options() +@click.argument("sagseventid", required=True) +def sagsevent(sagseventid: str, **kwargs) -> None: + """ + Information om et sagsevent. + """ + + # Hver type FikspunktsregisterObjekt kan tilknyttes to slags Sagsevents, + # oprettelse og nedlukning. De skal præsenteres på omtrent samme måde. + # Nedenstående interne funktioner benyttes til dette. + def _koordinatoversigt(koordinater: list[Koordinat]) -> None: + for koordinat in koordinater: + fire.cli.print(f"{koordinat.punkt.ident:14} {koordinat_linje(koordinat)}") + fire.cli.print("\n") + + def _observationsoversigt(observationer: list[Observation]) -> None: + niv_obstyper = (1, 2) + observationstyper = [o.observationstypeid for o in observationer] + ikke_niv_observationer = all( + obstype not in niv_obstyper for obstype in observationstyper + ) + if ikke_niv_observationer: + fire.cli.print( + "Sagseventen indeholder observationer, der ikke er målt med nivellement." + ) + fire.cli.print("Disse observationer kan på nuværende tidspunkt ikke vises.") + else: + observationsrapport( + observationer_til=observationer, + observationer_fra=[], + options="niv", + opt_detaljeret=False, + ) + + def _punktoversigt(punkter: list[Punkt]) -> None: + for punkt in punkter: + fire.cli.print(f" PUNKT {punkt.ident} ({punkt.id})", bold=True) + fire.cli.print(f" Lokation {punkt.geometriobjekter[-1].geometri}") + fire.cli.print("\n") + + def _punktinfooversigt( + punktinformationer: list[PunktInformation], historik: bool + ) -> None: + # PunktInformationer grupperes efter Punkt + punkter = set(punktinfo.punkt for punktinfo in punktinformationer) + punktinfo_oversigt = {punkt: [] for punkt in punkter} + + for punktinfo in punktinformationer: + punktinfo_oversigt[punktinfo.punkt].append(punktinfo) + + for punkt, punktinformationer in punktinfo_oversigt.items(): + fire.cli.print(punkt.ident) + punktinforapport(punktinformationer, historik) + fire.cli.print("\n") + + def _grafikoversigt(grafikker: list[Grafik]) -> None: + fire.cli.print(f"{'Filnavn':30} Type") + fire.cli.print(f"{'-'*29:30} --------------") + + for grafik in grafikker: + fire.cli.print(f"{grafik.filnavn:30} {grafik.type}") + + def _header(tekst: str, bold=False) -> None: + max_bredde = 80 + spacer_bredde = (max_bredde - len(tekst) - 2) // 2 + 1 + fire.cli.print( + f"{'-'*spacer_bredde} {tekst} {'-'*spacer_bredde}"[0:80], bold=bold + ) + + try: + event = fire.cli.firedb.hent_sagsevent(sagseventid) + except NoResultFound: + fire.cli.print(f'Fejl! "{sagseventid}" ikke fundet!', fg="red", err=True) + raise SystemExit(1) # pylint: disable=raise-missing-from + except MultipleResultsFound: + fire.cli.print( + f'Fejl! Partielt UUID "{sagseventid}" ikke unikt!', fg="red", err=True + ) + raise SystemExit(1) # pylint: disable=raise-missing-from + + fire.cli.print("\n") + _header("SAGSEVENT", bold=True) + fire.cli.print(f" Sagseventid : {event.id}") + fire.cli.print(f" Sagseventtype : {event.eventtype.name}") + fire.cli.print(f" Oprettet : {event.registreringfra}") + fire.cli.print(f" Sagsid : {event.sag.id}") + if len(event.beskrivelse) > 45: + fire.cli.print(" Beskrivelse :\n") + fire.cli.print(event.beskrivelse) + else: + fire.cli.print(f" Beskrivelse : {event.beskrivelse}") + fire.cli.print("\n") + + if event.eventtype == EventType.KOMMENTAR: + materialer = [m.materiale for si in event.sagseventinfos for m in si.materialer] + htmler = [h.html for si in event.sagseventinfos for h in si.htmler] + + if materialer: + _header("Tilknyttet sagsmateriale") + for materiale in materialer: + blob = io.BytesIO(materiale) + with zipfile.ZipFile(blob, "r") as zipped_files: + zipped_files.printdir() + fire.cli.print("\n") + + if htmler: + _header("Tilknyttede HTML-filer") + for html in htmler: + match = re.search('(.*?)', html) + title = match.group(1) if match else 'Ingen HTML titel' + fire.cli.print(title) + + if event.koordinater: + _header("Tilføjede koordinater") + _koordinatoversigt(event.koordinater) + + if event.koordinater_slettede: + _header("Afregistrerede koordinater") + _koordinatoversigt(event.koordinater_slettede) + + if event.observationer: + _header("Tilføjede observationer") + _observationsoversigt(event.observationer) + + if event.observationer_slettede: + _header("Afregistrerede observationer") + _observationsoversigt(event.observationer_slettede) + + if event.punktinformationer: + _header("Tilføjede punktinformationer") + _punktinfooversigt(event.punktinformationer, historik=False) + + if event.punktinformationer_slettede: + _header("Afregistrerede punktinformationer") + _punktinfooversigt(event.punktinformationer_slettede, historik=True) + + if event.punkter: + _header("Tilføjet punkt") + _punktoversigt(event.punkter) + + if event.punkter_slettede: + _header("Afregistreret punkt") + _punktoversigt(event.punkter_slettede) + + if event.grafikker: + _header("Tilføjet grafik") + _grafikoversigt(event.grafikker) + + if event.grafikker_slettede: + _header("Afregistreret grafik") + _grafikoversigt(event.grafikker_slettede) + + if event.punktsamlinger: + _header("Tilføjede punktsamlinger") + punktsamlingsrapport(event.punktsamlinger) + + if event.punktsamlinger_slettede: + _header("Afregistrerede punktsamlinger") + punktsamlingsrapport(event.punktsamlinger_slettede) + + if event.tidsserier: + _header("Tilføjede tidsserier") + tidsserierapport(event.tidsserier) + + if event.tidsserier_slettede: + _header("Afregistrerede tidsserier") + tidsserierapport(event.tidsserier_slettede) + + @info.command() @fire.cli.default_options() @click.argument("punktsamling", required=False)