fix: resolve all deprecation warnings and remove dead test scripts
All checks were successful
Deploy Plattform-Core (Int) / test (push) Successful in 1m4s
Deploy Plattform-Core (Int) / deploy (push) Successful in 10s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ValueOn AG 2026-06-09 23:52:50 +02:00
parent dce41a01ac
commit 30db7a310c
6 changed files with 76 additions and 671 deletions

View file

@ -57,37 +57,17 @@ class GeoTag(str, Enum):
class GeoPunkt(BaseModel): class GeoPunkt(BaseModel):
"""Represents a 3D point with reference.""" """Represents a 3D point with reference."""
koordinatensystem: str = Field( koordinatensystem: str = Field(
description="Coordinate system (e.g. 'LV95', 'EPSG:2056')", description="Coordinate system (e.g. 'LV95', 'EPSG:2056')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
frontend_type="text",
frontend_readonly=False,
frontend_required=True,
)
x: float = Field( x: float = Field(
description="East value (E) [m], typically 2'480'000 - 2'840'000", description="East value (E) [m], typically 2'480'000 - 2'840'000", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": True})
frontend_type="number",
frontend_readonly=False,
frontend_required=True,
)
y: float = Field( y: float = Field(
description="North value (N) [m], typically 1'070'000 - 1'300'000", description="North value (N) [m], typically 1'070'000 - 1'300'000", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": True})
frontend_type="number",
frontend_readonly=False,
frontend_required=True,
)
z: Optional[float] = Field( z: Optional[float] = Field(
None, None,
description="Height above sea level [m]", description="Height above sea level [m]", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
frontend_type="number",
frontend_readonly=False,
frontend_required=False,
)
referenz: Optional[GeoTag] = Field( referenz: Optional[GeoTag] = Field(
None, None,
description="Point categorization", description="Point categorization", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
frontend_type="select",
frontend_readonly=False,
frontend_required=False,
)
class GeoPolylinie(BaseModel): class GeoPolylinie(BaseModel):
@ -97,18 +77,10 @@ class GeoPolylinie(BaseModel):
description="Primary key", description="Primary key",
) )
closed: bool = Field( closed: bool = Field(
description="Is the GeoPolylinie closed (polygon)?", description="Is the GeoPolylinie closed (polygon)?", json_schema_extra={"frontend_type": "boolean", "frontend_readonly": False, "frontend_required": True})
frontend_type="boolean",
frontend_readonly=False,
frontend_required=True,
)
punkte: List[GeoPunkt] = Field( punkte: List[GeoPunkt] = Field(
default_factory=list, default_factory=list,
description="List of GeoPunkte forming the GeoPolylinie", description="List of GeoPunkte forming the GeoPolylinie", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": True})
frontend_type="json",
frontend_readonly=False,
frontend_required=True,
)
@i18nModel("Dokument") @i18nModel("Dokument")
@ -117,73 +89,33 @@ class Dokument(PowerOnModel):
id: str = Field( id: str = Field(
default_factory=lambda: str(uuid.uuid4()), default_factory=lambda: str(uuid.uuid4()),
description="Primary key", description="Primary key",
frontend_type="text", json_schema_extra={"label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_readonly=True,
frontend_required=False,
label="ID",
)
mandateId: str = Field( mandateId: str = Field(
description="ID of the mandate this document belongs to", description="ID of the mandate this document belongs to",
frontend_type="text", json_schema_extra={"label": "Mandats-ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_readonly=True,
frontend_required=False,
label="Mandats-ID",
)
featureInstanceId: str = Field( featureInstanceId: str = Field(
description="ID of the feature instance this document belongs to", description="ID of the feature instance this document belongs to",
frontend_type="text", json_schema_extra={"label": "Feature-Instanz-ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_readonly=True,
frontend_required=False,
label="Feature-Instanz-ID",
)
label: str = Field( label: str = Field(
description="Document label", description="Document label",
frontend_type="text", json_schema_extra={"label": "Bezeichnung", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
frontend_readonly=False,
frontend_required=True,
label="Bezeichnung",
)
versionsbezeichnung: Optional[str] = Field( versionsbezeichnung: Optional[str] = Field(
None, None,
description="Version number or designation (e.g. 'v1.0', 'Rev. A')", description="Version number or designation (e.g. 'v1.0', 'Rev. A')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
dokumentTyp: Optional[DokumentTyp] = Field( dokumentTyp: Optional[DokumentTyp] = Field(
None, None,
description="Document type", description="Document type", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
frontend_type="select",
frontend_readonly=False,
frontend_required=False,
)
dokumentReferenz: str = Field( dokumentReferenz: str = Field(
description="File path or URL", description="File path or URL", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
frontend_type="text",
frontend_readonly=False,
frontend_required=True,
)
quelle: Optional[str] = Field( quelle: Optional[str] = Field(
None, None,
description="Source of the document", description="Source of the document", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
mimeType: Optional[str] = Field( mimeType: Optional[str] = Field(
None, None,
description="MIME type of the document (e.g. 'application/pdf', 'image/png')", description="MIME type of the document (e.g. 'application/pdf', 'image/png')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
kategorienTags: List[str] = Field( kategorienTags: List[str] = Field(
default_factory=list, default_factory=list,
description="Document categorization tags", description="Document categorization tags", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
class Kontext(PowerOnModel): class Kontext(PowerOnModel):
@ -193,78 +125,38 @@ class Kontext(PowerOnModel):
description="Primary key", description="Primary key",
) )
thema: str = Field( thema: str = Field(
description="Theme designation", description="Theme designation", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
frontend_type="text",
frontend_readonly=False,
frontend_required=True,
)
inhalt: str = Field( inhalt: str = Field(
description="Detailed information (text)", description="Detailed information (text)", json_schema_extra={"frontend_type": "textarea", "frontend_readonly": False, "frontend_required": True})
frontend_type="textarea",
frontend_readonly=False,
frontend_required=True,
)
class Land(PowerOnModel): class Land(PowerOnModel):
"""National level administrative entity.""" """National level administrative entity."""
id: str = Field( id: str = Field(
default_factory=lambda: str(uuid.uuid4()), default_factory=lambda: str(uuid.uuid4()),
description="Primary key", description="Primary key", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_type="text",
frontend_readonly=True,
frontend_required=False,
)
mandateId: str = Field( mandateId: str = Field(
description="ID of the mandate", description="ID of the mandate", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_type="text",
frontend_readonly=True,
frontend_required=False,
)
featureInstanceId: str = Field( featureInstanceId: str = Field(
description="ID of the feature instance", description="ID of the feature instance", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_type="text",
frontend_readonly=True,
frontend_required=False,
)
label: str = Field( label: str = Field(
description="Country name (e.g. 'Schweiz')", description="Country name (e.g. 'Schweiz')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
frontend_type="text",
frontend_readonly=False,
frontend_required=True,
)
abk: Optional[str] = Field( abk: Optional[str] = Field(
None, None,
description="Abbreviation (e.g. 'CH')", description="Abbreviation (e.g. 'CH')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
dokumente: List[Dokument] = Field( dokumente: List[Dokument] = Field(
default_factory=list, default_factory=list,
description="National laws/documents", description="National laws/documents", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
kontextInformationen: List[Kontext] = Field( kontextInformationen: List[Kontext] = Field(
default_factory=list, default_factory=list,
description="National context information", description="National context information", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
class Kanton(PowerOnModel): class Kanton(PowerOnModel):
"""Cantonal level administrative entity.""" """Cantonal level administrative entity."""
id: str = Field( id: str = Field(
default_factory=lambda: str(uuid.uuid4()), default_factory=lambda: str(uuid.uuid4()),
description="Primary key", description="Primary key", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_type="text",
frontend_readonly=True,
frontend_required=False,
)
mandateId: str = Field( mandateId: str = Field(
description="ID of the mandate", description="ID of the mandate",
json_schema_extra={ json_schema_extra={
@ -282,11 +174,7 @@ class Kanton(PowerOnModel):
}, },
) )
label: str = Field( label: str = Field(
description="Canton name (e.g. 'Zürich')", description="Canton name (e.g. 'Zürich')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
frontend_type="text",
frontend_readonly=False,
frontend_required=True,
)
id_land: Optional[str] = Field( id_land: Optional[str] = Field(
None, None,
description="Land ID (Foreign Key) - eindeutiger Link zum Land, in welchem Land der Kanton liegt", 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( abk: Optional[str] = Field(
None, None,
description="Abbreviation (e.g. 'ZH')", description="Abbreviation (e.g. 'ZH')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
dokumente: List[Dokument] = Field( dokumente: List[Dokument] = Field(
default_factory=list, default_factory=list,
description="Cantonal documents", description="Cantonal documents", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
kontextInformationen: List[Kontext] = Field( kontextInformationen: List[Kontext] = Field(
default_factory=list, default_factory=list,
description="Canton-specific context information", description="Canton-specific context information", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
class Gemeinde(PowerOnModel): class Gemeinde(PowerOnModel):
"""Municipal level administrative entity.""" """Municipal level administrative entity."""
id: str = Field( id: str = Field(
default_factory=lambda: str(uuid.uuid4()), default_factory=lambda: str(uuid.uuid4()),
description="Primary key", description="Primary key", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_type="text",
frontend_readonly=True,
frontend_required=False,
)
mandateId: str = Field( mandateId: str = Field(
description="ID of the mandate", description="ID of the mandate", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_type="text",
frontend_readonly=True,
frontend_required=False,
)
featureInstanceId: str = Field( featureInstanceId: str = Field(
description="ID of the feature instance", description="ID of the feature instance", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_type="text",
frontend_readonly=True,
frontend_required=False,
)
label: str = Field( label: str = Field(
description="Municipality name (e.g. 'Zürich')", description="Municipality name (e.g. 'Zürich')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
frontend_type="text",
frontend_readonly=False,
frontend_required=True,
)
id_kanton: Optional[str] = Field( id_kanton: Optional[str] = Field(
None, None,
description="Kanton ID (Foreign Key) - eindeutiger Link zum Kanton, in welchem Kanton die Gemeinde liegt", 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( plz: Optional[str] = Field(
None, 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.", 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})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
dokumente: List[Dokument] = Field( dokumente: List[Dokument] = Field(
default_factory=list, default_factory=list,
description="Municipal documents", description="Municipal documents", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
kontextInformationen: List[Kontext] = Field( kontextInformationen: List[Kontext] = Field(
default_factory=list, default_factory=list,
description="Municipality-specific context information", description="Municipality-specific context information", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
# ===== Main Models (use ForwardRef for circular references) ===== # ===== Main Models (use ForwardRef for circular references) =====
@ -392,11 +240,7 @@ class Parzelle(PowerOnModel):
id: str = Field( id: str = Field(
default_factory=lambda: str(uuid.uuid4()), default_factory=lambda: str(uuid.uuid4()),
description="Primary key", description="Primary key",
frontend_type="text", json_schema_extra={"label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_readonly=True,
frontend_required=False,
label="ID",
)
mandateId: str = Field( mandateId: str = Field(
description="ID of the mandate", description="ID of the mandate",
json_schema_extra={ json_schema_extra={
@ -421,55 +265,27 @@ class Parzelle(PowerOnModel):
# Grunddaten # Grunddaten
label: str = Field( label: str = Field(
description="Plot designation", description="Plot designation",
frontend_type="text", json_schema_extra={"label": "Bezeichnung", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
frontend_readonly=False,
frontend_required=True,
label="Bezeichnung",
)
parzellenAliasTags: List[str] = Field( parzellenAliasTags: List[str] = Field(
default_factory=list, default_factory=list,
description="Additional plot names or field names", description="Additional plot names or field names", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
eigentuemerschaft: Optional[str] = Field( eigentuemerschaft: Optional[str] = Field(
None, None,
description="Owner of the plot", description="Owner of the plot", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
strasseNr: Optional[str] = Field( strasseNr: Optional[str] = Field(
None, None,
description="Street and house number", description="Street and house number", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
plz: Optional[str] = Field( plz: Optional[str] = Field(
None, None,
description="Postal code of the plot", description="Postal code of the plot", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
# Geografischer Kontext # Geografischer Kontext
perimeter: Optional[GeoPolylinie] = Field( perimeter: Optional[GeoPolylinie] = Field(
None, None,
description="Plot boundary as closed GeoPolylinie", description="Plot boundary as closed GeoPolylinie", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
baulinie: Optional[GeoPolylinie] = Field( baulinie: Optional[GeoPolylinie] = Field(
None, None,
description="Building line of the plot", description="Building line of the plot", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
kontextGemeinde: Optional[str] = Field( kontextGemeinde: Optional[str] = Field(
None, None,
@ -485,145 +301,69 @@ class Parzelle(PowerOnModel):
# Bebauungsparameter # Bebauungsparameter
bauzone: Optional[str] = Field( bauzone: Optional[str] = Field(
None, None,
description="Building zone designation (e.g. W3, WG2, etc.)", description="Building zone designation (e.g. W3, WG2, etc.)", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
az: Optional[float] = Field( az: Optional[float] = Field(
None, None,
description="Ausnützungsziffer", description="Ausnützungsziffer", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
frontend_type="number",
frontend_readonly=False,
frontend_required=False,
)
bz: Optional[float] = Field( bz: Optional[float] = Field(
None, None,
description="Bebauungsziffer", description="Bebauungsziffer", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
frontend_type="number",
frontend_readonly=False,
frontend_required=False,
)
vollgeschossZahl: Optional[int] = Field( vollgeschossZahl: Optional[int] = Field(
None, None,
description="Number of allowed full floors", description="Number of allowed full floors", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
frontend_type="number",
frontend_readonly=False,
frontend_required=False,
)
anrechenbarDachgeschoss: Optional[float] = Field( anrechenbarDachgeschoss: Optional[float] = Field(
None, None,
description="Accountable portion of attic (0.0 - 1.0)", description="Accountable portion of attic (0.0 - 1.0)", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
frontend_type="number",
frontend_readonly=False,
frontend_required=False,
)
anrechenbarUntergeschoss: Optional[float] = Field( anrechenbarUntergeschoss: Optional[float] = Field(
None, None,
description="Accountable portion of basement (0.0 - 1.0)", description="Accountable portion of basement (0.0 - 1.0)", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
frontend_type="number",
frontend_readonly=False,
frontend_required=False,
)
gebaeudehoeheMax: Optional[float] = Field( gebaeudehoeheMax: Optional[float] = Field(
None, None,
description="Maximum building height in meters", description="Maximum building height in meters", json_schema_extra={"frontend_type": "number", "frontend_readonly": False, "frontend_required": False})
frontend_type="number",
frontend_readonly=False,
frontend_required=False,
)
# Abstandsregelungen # Abstandsregelungen
regelnGrenzabstand: List[str] = Field( regelnGrenzabstand: List[str] = Field(
default_factory=list, default_factory=list,
description="Regulations for boundary distance", description="Regulations for boundary distance", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
regelnMehrlaengenzuschlag: List[str] = Field( regelnMehrlaengenzuschlag: List[str] = Field(
default_factory=list, default_factory=list,
description="Regulations for additional length surcharge", description="Regulations for additional length surcharge", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
regelnMehrhoehenzuschlag: List[str] = Field( regelnMehrhoehenzuschlag: List[str] = Field(
default_factory=list, default_factory=list,
description="Regulations for additional height surcharge", description="Regulations for additional height surcharge", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
# Eigenschaften (Ja/Nein) # Eigenschaften (Ja/Nein)
parzelleBebaut: Optional[JaNein] = Field( parzelleBebaut: Optional[JaNein] = Field(
None, None,
description="Is the plot built?", description="Is the plot built?", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
frontend_type="select",
frontend_readonly=False,
frontend_required=False,
)
parzelleErschlossen: Optional[JaNein] = Field( parzelleErschlossen: Optional[JaNein] = Field(
None, None,
description="Is the plot developed?", description="Is the plot developed?", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
frontend_type="select",
frontend_readonly=False,
frontend_required=False,
)
parzelleHanglage: Optional[JaNein] = Field( parzelleHanglage: Optional[JaNein] = Field(
None, None,
description="Is the plot on a slope?", description="Is the plot on a slope?", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
frontend_type="select",
frontend_readonly=False,
frontend_required=False,
)
# Schutzzonen # Schutzzonen
laermschutzzone: Optional[str] = Field( laermschutzzone: Optional[str] = Field(
None, None,
description="Noise protection zone (e.g. 'II')", description="Noise protection zone (e.g. 'II')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
hochwasserschutzzone: Optional[str] = Field( hochwasserschutzzone: Optional[str] = Field(
None, None,
description="Flood protection zone (e.g. 'tief')", description="Flood protection zone (e.g. 'tief')", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
grundwasserschutzzone: Optional[str] = Field( grundwasserschutzzone: Optional[str] = Field(
None, None,
description="Groundwater protection zone", description="Groundwater protection zone", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False})
frontend_type="text",
frontend_readonly=False,
frontend_required=False,
)
# Beziehungen (stored as JSONB in database) # Beziehungen (stored as JSONB in database)
parzellenNachbarschaft: List[Dict[str, Any]] = Field( parzellenNachbarschaft: List[Dict[str, Any]] = Field(
default_factory=list, default_factory=list,
description="Neighboring plots (stored as list of Parzelle IDs or full objects)", description="Neighboring plots (stored as list of Parzelle IDs or full objects)", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
dokumente: List[Dokument] = Field( dokumente: List[Dokument] = Field(
default_factory=list, default_factory=list,
description="Plot-specific documents", description="Plot-specific documents", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
kontextInformationen: List[Kontext] = Field( kontextInformationen: List[Kontext] = Field(
default_factory=list, default_factory=list,
description="Plot-specific context information", description="Plot-specific context information", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
@i18nModel("Projekt") @i18nModel("Projekt")
@ -632,11 +372,7 @@ class Projekt(PowerOnModel):
id: str = Field( id: str = Field(
default_factory=lambda: str(uuid.uuid4()), default_factory=lambda: str(uuid.uuid4()),
description="Primary key", description="Primary key",
frontend_type="text", json_schema_extra={"label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False})
frontend_readonly=True,
frontend_required=False,
label="ID",
)
mandateId: str = Field( mandateId: str = Field(
description="ID of the mandate", description="ID of the mandate",
json_schema_extra={ json_schema_extra={
@ -659,54 +395,26 @@ class Projekt(PowerOnModel):
) )
label: str = Field( label: str = Field(
description="Project designation", description="Project designation",
frontend_type="text", json_schema_extra={"label": "Bezeichnung", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True})
frontend_readonly=False,
frontend_required=True,
label="Bezeichnung",
)
statusProzess: Optional[StatusProzess] = Field( statusProzess: Optional[StatusProzess] = Field(
None, None,
description="Project status", description="Project status",
frontend_type="select", json_schema_extra={"label": "Prozessstatus", "frontend_type": "select", "frontend_readonly": False, "frontend_required": False})
frontend_readonly=False,
frontend_required=False,
label="Prozessstatus",
)
perimeter: Optional[GeoPolylinie] = Field( perimeter: Optional[GeoPolylinie] = Field(
None, None,
description="Envelope of all plots in the project", description="Envelope of all plots in the project", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
baulinie: Optional[GeoPolylinie] = Field( baulinie: Optional[GeoPolylinie] = Field(
None, None,
description="Building line of the project", description="Building line of the project", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
parzellen: List[Parzelle] = Field( parzellen: List[Parzelle] = Field(
default_factory=list, default_factory=list,
description="All plots of the project", description="All plots of the project", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
dokumente: List[Dokument] = Field( dokumente: List[Dokument] = Field(
default_factory=list, default_factory=list,
description="Project-specific documents", description="Project-specific documents", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
kontextInformationen: List[Kontext] = Field( kontextInformationen: List[Kontext] = Field(
default_factory=list, default_factory=list,
description="Project-specific context information", description="Project-specific context information", json_schema_extra={"frontend_type": "json", "frontend_readonly": False, "frontend_required": False})
frontend_type="json",
frontend_readonly=False,
frontend_required=False,
)
# Resolve forward references # Resolve forward references

View file

@ -457,10 +457,10 @@ async def getStats(
instanceId: str, instanceId: str,
dateFrom: Optional[str] = Query(default=None, description="ISO date YYYY-MM-DD"), dateFrom: Optional[str] = Query(default=None, description="ISO date YYYY-MM-DD"),
dateTo: 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), trackerIds: Optional[List[int]] = Query(default=None),
categoryIds: Optional[List[int]] = Query(default=None, description="Filter by Redmine issue categories"), 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), context: RequestContext = Depends(getRequestContext),
) -> RedmineStatsDto: ) -> RedmineStatsDto:
mandateId = _validateInstanceAccess(instanceId, context) mandateId = _validateInstanceAccess(instanceId, context)

View file

@ -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}")

View file

@ -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)")

View file

@ -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()

View file

@ -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) _BARE_LABEL_PATTERN = re.compile(r'^\s*"label"\s*:\s*"[^"]+"', re.MULTILINE)
# mainRealEstate.py contains "label": "AA1704" inside a multi-line f-string _ALLOWED_FILES_WITH_BARE_LABELS: set[str] = set()
# 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",
}
def _findFeatureMainFiles() -> list[Path]: def _findFeatureMainFiles() -> list[Path]: