From 30db7a310cab0b3649375e2520c4cbe22e0efdad Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 9 Jun 2026 23:52:50 +0200
Subject: [PATCH] fix: resolve all deprecation warnings and remove dead test
scripts
Co-authored-by: Cursor
---
.../realEstate/datamodelFeatureRealEstate.py | 438 +++---------------
.../features/redmine/routeFeatureRedmine.py | 4 +-
tests/functional/test_kpi_full.py | 97 ----
tests/functional/test_kpi_incomplete.py | 134 ------
tests/functional/test_kpi_path.py | 68 ---
.../test_featureCatalogLabels_i18n.py | 6 +-
6 files changed, 76 insertions(+), 671 deletions(-)
delete mode 100644 tests/functional/test_kpi_full.py
delete mode 100644 tests/functional/test_kpi_incomplete.py
delete mode 100644 tests/functional/test_kpi_path.py
diff --git a/modules/features/realEstate/datamodelFeatureRealEstate.py b/modules/features/realEstate/datamodelFeatureRealEstate.py
index 346e998b..cf2c6c67 100644
--- a/modules/features/realEstate/datamodelFeatureRealEstate.py
+++ b/modules/features/realEstate/datamodelFeatureRealEstate.py
@@ -57,37 +57,17 @@ class GeoTag(str, Enum):
class GeoPunkt(BaseModel):
"""Represents a 3D point with reference."""
koordinatensystem: str = Field(
- description="Coordinate system (e.g. 'LV95', 'EPSG:2056')",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="Coordinate system (e.g. 'LV95', 'EPSG:2056')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
x: float = Field(
- description="East value (E) [m], typically 2'480'000 - 2'840'000",
- frontend_type="number",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="East value (E) [m], typically 2'480'000 - 2'840'000", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": True})
y: float = Field(
- description="North value (N) [m], typically 1'070'000 - 1'300'000",
- frontend_type="number",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="North value (N) [m], typically 1'070'000 - 1'300'000", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": True})
z: Optional[float] = Field(
None,
- description="Height above sea level [m]",
- frontend_type="number",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Height above sea level [m]", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
referenz: Optional[GeoTag] = Field(
None,
- description="Point categorization",
- frontend_type="select",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Point categorization", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
class GeoPolylinie(BaseModel):
@@ -97,18 +77,10 @@ class GeoPolylinie(BaseModel):
description="Primary key",
)
closed: bool = Field(
- description="Is the GeoPolylinie closed (polygon)?",
- frontend_type="boolean",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="Is the GeoPolylinie closed (polygon)?", json_schema_extra={"frontend_type": "boolean", "frontend_readonly": False, "frontend_required": True})
punkte: List[GeoPunkt] = Field(
default_factory=list,
- description="List of GeoPunkte forming the GeoPolylinie",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="List of GeoPunkte forming the GeoPolylinie", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": True})
@i18nModel("Dokument")
@@ -117,73 +89,33 @@ class Dokument(PowerOnModel):
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
description="Primary key",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- label="ID",
- )
+ json_schema_extra={"label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
mandateId: str = Field(
description="ID of the mandate this document belongs to",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- label="Mandats-ID",
- )
+ json_schema_extra={"label": "Mandats-ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
featureInstanceId: str = Field(
description="ID of the feature instance this document belongs to",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- label="Feature-Instanz-ID",
- )
+ json_schema_extra={"label": "Feature-Instanz-ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
label: str = Field(
description="Document label",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=True,
- label="Bezeichnung",
- )
+ json_schema_extra={"label": "Bezeichnung", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
versionsbezeichnung: Optional[str] = Field(
None,
- description="Version number or designation (e.g. 'v1.0', 'Rev. A')",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Version number or designation (e.g. 'v1.0', 'Rev. A')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
dokumentTyp: Optional[DokumentTyp] = Field(
None,
- description="Document type",
- frontend_type="select",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Document type", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
dokumentReferenz: str = Field(
- description="File path or URL",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="File path or URL", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
quelle: Optional[str] = Field(
None,
- description="Source of the document",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Source of the document", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
mimeType: Optional[str] = Field(
None,
- description="MIME type of the document (e.g. 'application/pdf', 'image/png')",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="MIME type of the document (e.g. 'application/pdf', 'image/png')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
kategorienTags: List[str] = Field(
default_factory=list,
- description="Document categorization tags",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Document categorization tags", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
class Kontext(PowerOnModel):
@@ -193,78 +125,38 @@ class Kontext(PowerOnModel):
description="Primary key",
)
thema: str = Field(
- description="Theme designation",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="Theme designation", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
inhalt: str = Field(
- description="Detailed information (text)",
- frontend_type="textarea",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="Detailed information (text)", json_schema_extra={"frontend_type": "textarea", "frontend_readonly": False, "frontend_required": True})
class Land(PowerOnModel):
"""National level administrative entity."""
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
- description="Primary key",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- )
+ description="Primary key", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
mandateId: str = Field(
- description="ID of the mandate",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- )
+ description="ID of the mandate", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
featureInstanceId: str = Field(
- description="ID of the feature instance",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- )
+ description="ID of the feature instance", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
label: str = Field(
- description="Country name (e.g. 'Schweiz')",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="Country name (e.g. 'Schweiz')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
abk: Optional[str] = Field(
None,
- description="Abbreviation (e.g. 'CH')",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Abbreviation (e.g. 'CH')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
dokumente: List[Dokument] = Field(
default_factory=list,
- description="National laws/documents",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="National laws/documents", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
kontextInformationen: List[Kontext] = Field(
default_factory=list,
- description="National context information",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="National context information", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
class Kanton(PowerOnModel):
"""Cantonal level administrative entity."""
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
- description="Primary key",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- )
+ description="Primary key", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
mandateId: str = Field(
description="ID of the mandate",
json_schema_extra={
@@ -282,11 +174,7 @@ class Kanton(PowerOnModel):
},
)
label: str = Field(
- description="Canton name (e.g. 'Zürich')",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="Canton name (e.g. 'Zürich')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
id_land: Optional[str] = Field(
None,
description="Land ID (Foreign Key) - eindeutiger Link zum Land, in welchem Land der Kanton liegt",
@@ -299,54 +187,26 @@ class Kanton(PowerOnModel):
)
abk: Optional[str] = Field(
None,
- description="Abbreviation (e.g. 'ZH')",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Abbreviation (e.g. 'ZH')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
dokumente: List[Dokument] = Field(
default_factory=list,
- description="Cantonal documents",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Cantonal documents", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
kontextInformationen: List[Kontext] = Field(
default_factory=list,
- description="Canton-specific context information",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Canton-specific context information", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
class Gemeinde(PowerOnModel):
"""Municipal level administrative entity."""
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
- description="Primary key",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- )
+ description="Primary key", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
mandateId: str = Field(
- description="ID of the mandate",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- )
+ description="ID of the mandate", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
featureInstanceId: str = Field(
- description="ID of the feature instance",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- )
+ description="ID of the feature instance", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
label: str = Field(
- description="Municipality name (e.g. 'Zürich')",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=True,
- )
+ description="Municipality name (e.g. 'Zürich')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
id_kanton: Optional[str] = Field(
None,
description="Kanton ID (Foreign Key) - eindeutiger Link zum Kanton, in welchem Kanton die Gemeinde liegt",
@@ -359,25 +219,13 @@ class Gemeinde(PowerOnModel):
)
plz: Optional[str] = Field(
None,
- description="Postal code (for municipalities with multiple PLZ, this can be a main PLZ). Bei Gemeinden mit mehreren Postleitzahlen wird die konkrete PLZ der Parzelle im Attribut `plz` der Parzelle erfasst.",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Postal code (for municipalities with multiple PLZ, this can be a main PLZ). Bei Gemeinden mit mehreren Postleitzahlen wird die konkrete PLZ der Parzelle im Attribut `plz` der Parzelle erfasst.", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
dokumente: List[Dokument] = Field(
default_factory=list,
- description="Municipal documents",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Municipal documents", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
kontextInformationen: List[Kontext] = Field(
default_factory=list,
- description="Municipality-specific context information",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Municipality-specific context information", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
# ===== Main Models (use ForwardRef for circular references) =====
@@ -392,11 +240,7 @@ class Parzelle(PowerOnModel):
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
description="Primary key",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- label="ID",
- )
+ json_schema_extra={"label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
mandateId: str = Field(
description="ID of the mandate",
json_schema_extra={
@@ -421,55 +265,27 @@ class Parzelle(PowerOnModel):
# Grunddaten
label: str = Field(
description="Plot designation",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=True,
- label="Bezeichnung",
- )
+ json_schema_extra={"label": "Bezeichnung", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
parzellenAliasTags: List[str] = Field(
default_factory=list,
- description="Additional plot names or field names",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Additional plot names or field names", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
eigentuemerschaft: Optional[str] = Field(
None,
- description="Owner of the plot",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Owner of the plot", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
strasseNr: Optional[str] = Field(
None,
- description="Street and house number",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Street and house number", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
plz: Optional[str] = Field(
None,
- description="Postal code of the plot",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Postal code of the plot", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
# Geografischer Kontext
perimeter: Optional[GeoPolylinie] = Field(
None,
- description="Plot boundary as closed GeoPolylinie",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Plot boundary as closed GeoPolylinie", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
baulinie: Optional[GeoPolylinie] = Field(
None,
- description="Building line of the plot",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Building line of the plot", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
kontextGemeinde: Optional[str] = Field(
None,
@@ -485,145 +301,69 @@ class Parzelle(PowerOnModel):
# Bebauungsparameter
bauzone: Optional[str] = Field(
None,
- description="Building zone designation (e.g. W3, WG2, etc.)",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Building zone designation (e.g. W3, WG2, etc.)", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
az: Optional[float] = Field(
None,
- description="Ausnützungsziffer",
- frontend_type="number",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Ausnützungsziffer", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
bz: Optional[float] = Field(
None,
- description="Bebauungsziffer",
- frontend_type="number",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Bebauungsziffer", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
vollgeschossZahl: Optional[int] = Field(
None,
- description="Number of allowed full floors",
- frontend_type="number",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Number of allowed full floors", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
anrechenbarDachgeschoss: Optional[float] = Field(
None,
- description="Accountable portion of attic (0.0 - 1.0)",
- frontend_type="number",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Accountable portion of attic (0.0 - 1.0)", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
anrechenbarUntergeschoss: Optional[float] = Field(
None,
- description="Accountable portion of basement (0.0 - 1.0)",
- frontend_type="number",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Accountable portion of basement (0.0 - 1.0)", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
gebaeudehoeheMax: Optional[float] = Field(
None,
- description="Maximum building height in meters",
- frontend_type="number",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Maximum building height in meters", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
# Abstandsregelungen
regelnGrenzabstand: List[str] = Field(
default_factory=list,
- description="Regulations for boundary distance",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Regulations for boundary distance", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
regelnMehrlaengenzuschlag: List[str] = Field(
default_factory=list,
- description="Regulations for additional length surcharge",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Regulations for additional length surcharge", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
regelnMehrhoehenzuschlag: List[str] = Field(
default_factory=list,
- description="Regulations for additional height surcharge",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Regulations for additional height surcharge", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
# Eigenschaften (Ja/Nein)
parzelleBebaut: Optional[JaNein] = Field(
None,
- description="Is the plot built?",
- frontend_type="select",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Is the plot built?", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
parzelleErschlossen: Optional[JaNein] = Field(
None,
- description="Is the plot developed?",
- frontend_type="select",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Is the plot developed?", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
parzelleHanglage: Optional[JaNein] = Field(
None,
- description="Is the plot on a slope?",
- frontend_type="select",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Is the plot on a slope?", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
# Schutzzonen
laermschutzzone: Optional[str] = Field(
None,
- description="Noise protection zone (e.g. 'II')",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Noise protection zone (e.g. 'II')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
hochwasserschutzzone: Optional[str] = Field(
None,
- description="Flood protection zone (e.g. 'tief')",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Flood protection zone (e.g. 'tief')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
grundwasserschutzzone: Optional[str] = Field(
None,
- description="Groundwater protection zone",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Groundwater protection zone", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
# Beziehungen (stored as JSONB in database)
parzellenNachbarschaft: List[Dict[str, Any]] = Field(
default_factory=list,
- description="Neighboring plots (stored as list of Parzelle IDs or full objects)",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Neighboring plots (stored as list of Parzelle IDs or full objects)", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
dokumente: List[Dokument] = Field(
default_factory=list,
- description="Plot-specific documents",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Plot-specific documents", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
kontextInformationen: List[Kontext] = Field(
default_factory=list,
- description="Plot-specific context information",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Plot-specific context information", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
@i18nModel("Projekt")
@@ -632,11 +372,7 @@ class Projekt(PowerOnModel):
id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
description="Primary key",
- frontend_type="text",
- frontend_readonly=True,
- frontend_required=False,
- label="ID",
- )
+ json_schema_extra={"label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
mandateId: str = Field(
description="ID of the mandate",
json_schema_extra={
@@ -659,54 +395,26 @@ class Projekt(PowerOnModel):
)
label: str = Field(
description="Project designation",
- frontend_type="text",
- frontend_readonly=False,
- frontend_required=True,
- label="Bezeichnung",
- )
+ json_schema_extra={"label": "Bezeichnung", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
statusProzess: Optional[StatusProzess] = Field(
None,
description="Project status",
- frontend_type="select",
- frontend_readonly=False,
- frontend_required=False,
- label="Prozessstatus",
- )
+ json_schema_extra={"label": "Prozessstatus", "frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
perimeter: Optional[GeoPolylinie] = Field(
None,
- description="Envelope of all plots in the project",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Envelope of all plots in the project", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
baulinie: Optional[GeoPolylinie] = Field(
None,
- description="Building line of the project",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Building line of the project", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
parzellen: List[Parzelle] = Field(
default_factory=list,
- description="All plots of the project",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="All plots of the project", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
dokumente: List[Dokument] = Field(
default_factory=list,
- description="Project-specific documents",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Project-specific documents", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
kontextInformationen: List[Kontext] = Field(
default_factory=list,
- description="Project-specific context information",
- frontend_type="json",
- frontend_readonly=False,
- frontend_required=False,
- )
+ description="Project-specific context information", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
# Resolve forward references
diff --git a/modules/features/redmine/routeFeatureRedmine.py b/modules/features/redmine/routeFeatureRedmine.py
index 86ac8d30..d3f5b771 100644
--- a/modules/features/redmine/routeFeatureRedmine.py
+++ b/modules/features/redmine/routeFeatureRedmine.py
@@ -457,10 +457,10 @@ async def getStats(
instanceId: str,
dateFrom: Optional[str] = Query(default=None, description="ISO date YYYY-MM-DD"),
dateTo: Optional[str] = Query(default=None, description="ISO date YYYY-MM-DD"),
- bucket: str = Query(default="week", regex="^(day|week|month)$"),
+ bucket: str = Query(default="week", pattern="^(day|week|month)$"),
trackerIds: Optional[List[int]] = Query(default=None),
categoryIds: Optional[List[int]] = Query(default=None, description="Filter by Redmine issue categories"),
- statusFilter: str = Query(default="*", regex="^(\\*|open|closed)$", description="Restrict to open/closed/all tickets"),
+ statusFilter: str = Query(default="*", pattern="^(\\*|open|closed)$", description="Restrict to open/closed/all tickets"),
context: RequestContext = Depends(getRequestContext),
) -> RedmineStatsDto:
mandateId = _validateInstanceAccess(instanceId, context)
diff --git a/tests/functional/test_kpi_full.py b/tests/functional/test_kpi_full.py
deleted file mode 100644
index aa8d8540..00000000
--- a/tests/functional/test_kpi_full.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright (c) 2026 PowerOn AG
-# All rights reserved.
-"""Test full KPI extraction and validation flow"""
-import json
-import sys
-import os
-import pytest
-
-# Add gateway directory to path
-_gateway_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
-if _gateway_path not in sys.path:
- sys.path.insert(0, _gateway_path)
-
-from modules.serviceCenter.services.serviceAi.subJsonResponseHandling import JsonResponseHandler
-from modules.datamodels.datamodelAi import JsonAccumulationState
-
-# Load actual JSON response
-json_file = os.path.join(
- os.path.dirname(__file__),
- "..", "..", "..", "local", "debug", "prompts",
- "20251130-211706-078-document_generation_response.txt"
-)
-
-if not os.path.exists(json_file):
- pytest.skip(f"Test data file not found: {json_file}", allow_module_level=True)
-
-with open(json_file, 'r', encoding='utf-8') as f:
- content = f.read()
-
-# Extract JSON
-from modules.shared.jsonUtils import extractJsonString
-extracted = extractJsonString(content)
-parsedJson = json.loads(extracted)
-
-# KPI definition from the response
-kpiDefinitions = [{
- "id": "prime_numbers_count",
- "description": "Number of prime numbers generated and organized in the table",
- "jsonPath": "documents[0].sections[0].elements[0].rows",
- "targetValue": 4000
-}]
-
-print("="*60)
-print("KPI EXTRACTION AND VALIDATION TEST")
-print("="*60)
-
-# Step 1: Initialize accumulation state with KPIs
-accumulationState = JsonAccumulationState(
- accumulatedJsonString="",
- isAccumulationMode=True,
- lastParsedResult=None,
- allSections=[],
- kpis=[{**kpi, "currentValue": 0} for kpi in kpiDefinitions]
-)
-
-print(f"\nStep 1: Initialized KPIs")
-for kpi in accumulationState.kpis:
- print(f" KPI {kpi['id']}: currentValue={kpi.get('currentValue', 'N/A')}, targetValue={kpi.get('targetValue', 'N/A')}")
-
-# Step 2: Extract KPI values from parsed JSON
-print(f"\nStep 2: Extracting KPI values from JSON...")
-updatedKpis = JsonResponseHandler.extractKpiValuesFromJson(
- parsedJson,
- accumulationState.kpis
-)
-
-print(f" Extracted {len(updatedKpis)} KPIs")
-for kpi in updatedKpis:
- print(f" KPI {kpi['id']}: currentValue={kpi.get('currentValue', 'N/A')}, targetValue={kpi.get('targetValue', 'N/A')}")
-
-# Step 3: Validate progression
-print(f"\nStep 3: Validating KPI progression...")
-shouldProceed, reason = JsonResponseHandler.validateKpiProgression(
- accumulationState,
- updatedKpis
-)
-
-print(f" Result: shouldProceed={shouldProceed}, reason={reason}")
-
-# Step 4: Check what's in accumulationState.kpis vs updatedKpis
-print(f"\nStep 4: Comparing state...")
-print(f" accumulationState.kpis[0].currentValue = {accumulationState.kpis[0].get('currentValue', 'N/A')}")
-print(f" updatedKpis[0].currentValue = {updatedKpis[0].get('currentValue', 'N/A')}")
-
-# Step 5: Check if we need to update accumulationState.kpis
-print(f"\nStep 5: Updating accumulationState.kpis...")
-accumulationState.kpis = updatedKpis
-print(f" Updated accumulationState.kpis[0].currentValue = {accumulationState.kpis[0].get('currentValue', 'N/A')}")
-
-# Step 6: Validate again (should show progress)
-print(f"\nStep 6: Validating again after update...")
-shouldProceed2, reason2 = JsonResponseHandler.validateKpiProgression(
- accumulationState,
- updatedKpis
-)
-print(f" Result: shouldProceed={shouldProceed2}, reason={reason2}")
-
diff --git a/tests/functional/test_kpi_incomplete.py b/tests/functional/test_kpi_incomplete.py
deleted file mode 100644
index b9125d9f..00000000
--- a/tests/functional/test_kpi_incomplete.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# Copyright (c) 2026 PowerOn AG
-# All rights reserved.
-"""Test KPI extraction with incomplete JSON"""
-import json
-import sys
-import os
-import pytest
-
-# Add gateway directory to path
-_gateway_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
-if _gateway_path not in sys.path:
- sys.path.insert(0, _gateway_path)
-
-from modules.serviceCenter.services.serviceAi.subJsonResponseHandling import JsonResponseHandler
-from modules.datamodels.datamodelAi import JsonAccumulationState
-from modules.shared.jsonUtils import extractJsonString, repairBrokenJson
-
-# Load actual incomplete JSON response
-json_file = os.path.join(
- os.path.dirname(__file__),
- "..", "..", "..", "local", "debug", "prompts",
- "20251130-211706-078-document_generation_response.txt"
-)
-
-if not os.path.exists(json_file):
- pytest.skip(f"Test data file not found: {json_file}", allow_module_level=True)
-
-with open(json_file, 'r', encoding='utf-8') as f:
- content = f.read()
-
-print("="*60)
-print("KPI EXTRACTION WITH INCOMPLETE JSON TEST")
-print("="*60)
-
-# Step 1: Try to extract and parse JSON
-print(f"\nStep 1: Extracting JSON string...")
-extracted = extractJsonString(content)
-print(f" Extracted length: {len(extracted)} chars")
-
-# Step 2: Try to parse
-print(f"\nStep 2: Attempting to parse...")
-parsedJson = None
-try:
- parsedJson = json.loads(extracted)
- print(f" ✅ JSON parsed successfully")
-except json.JSONDecodeError as e:
- print(f" ❌ JSON parsing failed: {e}")
- print(f" Attempting repair...")
- try:
- parsedJson = repairBrokenJson(extracted)
- if parsedJson:
- print(f" ✅ JSON repaired successfully")
- else:
- print(f" ❌ JSON repair failed")
- except Exception as e2:
- print(f" ❌ Repair error: {e2}")
-
-if not parsedJson:
- pytest.skip("Cannot proceed - JSON cannot be parsed or repaired", allow_module_level=True)
-
-# Step 3: Check if path exists
-print(f"\nStep 3: Checking if KPI path exists...")
-path = "documents[0].sections[0].elements[0].rows"
-try:
- value = JsonResponseHandler._extractValueByPath(parsedJson, path)
- print(f" ✅ Path exists: {type(value)}")
- if isinstance(value, list):
- print(f" ✅ Value is list with {len(value)} items")
- if len(value) > 0:
- print(f" ✅ First item: {value[0]}")
- else:
- print(f" ⚠️ Value is not a list: {value}")
-except Exception as e:
- print(f" ❌ Path extraction failed: {e}")
- import traceback
- traceback.print_exc()
- pytest.skip(f"Path extraction failed: {e}", allow_module_level=True)
-
-# Step 4: Test KPI extraction
-print(f"\nStep 4: Testing KPI extraction...")
-kpiDefinitions = [{
- "id": "prime_numbers_count",
- "description": "Number of prime numbers generated and organized in the table",
- "jsonPath": "documents[0].sections[0].elements[0].rows",
- "targetValue": 4000
-}]
-
-accumulationState = JsonAccumulationState(
- accumulatedJsonString="",
- isAccumulationMode=True,
- lastParsedResult=parsedJson,
- allSections=[],
- kpis=[{**kpi, "currentValue": 0} for kpi in kpiDefinitions]
-)
-
-print(f" Initial KPI currentValue: {accumulationState.kpis[0].get('currentValue', 'N/A')}")
-
-updatedKpis = JsonResponseHandler.extractKpiValuesFromJson(
- parsedJson,
- accumulationState.kpis
-)
-
-print(f" Updated KPI currentValue: {updatedKpis[0].get('currentValue', 'N/A')}")
-
-# Step 5: Test validation
-print(f"\nStep 5: Testing KPI validation...")
-shouldProceed, reason = JsonResponseHandler.validateKpiProgression(
- accumulationState,
- updatedKpis
-)
-
-print(f" Result: shouldProceed={shouldProceed}, reason={reason}")
-
-if not shouldProceed:
- print(f"\n❌ VALIDATION FAILED - This is the problem!")
- print(f" Let's debug why...")
-
- # Check what's being compared
- lastValues = {kpi.get("id"): kpi.get("currentValue", 0) for kpi in accumulationState.kpis}
- print(f" Last values from accumulationState: {lastValues}")
-
- for updatedKpi in updatedKpis:
- kpiId = updatedKpi.get("id")
- currentValue = updatedKpi.get("currentValue", 0)
- print(f" Updated KPI {kpiId}: currentValue={currentValue}")
-
- if kpiId in lastValues:
- lastValue = lastValues[kpiId]
- print(f" Comparing: {lastValue} vs {currentValue}")
- if currentValue > lastValue:
- print(f" ✅ Should detect progress!")
- else:
- print(f" ❌ No progress detected (currentValue <= lastValue)")
-
diff --git a/tests/functional/test_kpi_path.py b/tests/functional/test_kpi_path.py
deleted file mode 100644
index c0a32862..00000000
--- a/tests/functional/test_kpi_path.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright (c) 2026 PowerOn AG
-# All rights reserved.
-"""Test KPI path extraction"""
-import json
-import sys
-import os
-
-# Add gateway directory to path
-_gateway_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
-if _gateway_path not in sys.path:
- sys.path.insert(0, _gateway_path)
-
-from modules.serviceCenter.services.serviceAi.subJsonResponseHandling import JsonResponseHandler
-
-# Test JSON matching the actual response
-test_json = {
- "metadata": {
- "split_strategy": "single_document",
- "source_documents": [],
- "extraction_method": "ai_generation"
- },
- "documents": [
- {
- "id": "doc_1",
- "title": "Prime Numbers Table",
- "filename": "prime_numbers.json",
- "sections": [
- {
- "id": "section_prime_numbers_table",
- "content_type": "table",
- "elements": [
- {
- "headers": ["Column 1", "Column 2"],
- "rows": [
- [2, 3, 5, 7, 11],
- [13, 17, 19, 23, 29]
- ]
- }
- ]
- }
- ]
- }
- ]
-}
-
-# Test path from KPI definition
-path = "documents[0].sections[0].elements[0].rows"
-
-print(f"Testing path: {path}")
-print(f"JSON structure: documents[0].sections[0].elements[0].rows")
-print()
-
-try:
- value = JsonResponseHandler._extractValueByPath(test_json, path)
- print(f"✅ Extracted value: {type(value)}")
- print(f" Value: {value}")
-
- if isinstance(value, list):
- count = len(value)
- print(f" Count: {count}")
- else:
- print(f" Not a list!")
-
-except Exception as e:
- print(f"❌ Error: {e}")
- import traceback
- traceback.print_exc()
-
diff --git a/tests/validation/test_featureCatalogLabels_i18n.py b/tests/validation/test_featureCatalogLabels_i18n.py
index ffdf1c2b..f5b2b490 100644
--- a/tests/validation/test_featureCatalogLabels_i18n.py
+++ b/tests/validation/test_featureCatalogLabels_i18n.py
@@ -30,11 +30,7 @@ _FEATURES_DIR = Path(__file__).resolve().parents[2] / "modules" / "features"
_BARE_LABEL_PATTERN = re.compile(r'^\s*"label"\s*:\s*"[^"]+"', re.MULTILINE)
-# mainRealEstate.py contains "label": "AA1704" inside a multi-line f-string
-# that is used as a JSON example in an AI prompt -- not a real catalog entry.
-_ALLOWED_FILES_WITH_BARE_LABELS: set[str] = {
- "mainRealEstate.py",
-}
+_ALLOWED_FILES_WITH_BARE_LABELS: set[str] = set()
def _findFeatureMainFiles() -> list[Path]: