xmas-app 0.10.0__tar.gz → 0.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xmas_app-0.10.0 → xmas_app-0.11.0}/PKG-INFO +1 -1
- {xmas_app-0.10.0 → xmas_app-0.11.0}/pyproject.toml +1 -1
- xmas_app-0.11.0/xmas_app/deps/version_guard.py +26 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/main.py +32 -13
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/settings.py +1 -1
- {xmas_app-0.10.0 → xmas_app-0.11.0}/LICENSE +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/README.md +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/db.py +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/db_uow.py +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/form.py +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/models/crud.py +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/pygeoapi/config.yaml +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/pygeoapi/openapi.yaml +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/pygeoapi/provider.py +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/schema.py +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/services/crud.py +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/split_service.py +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/util/__init__.py +0 -0
- {xmas_app-0.10.0 → xmas_app-0.11.0}/xmas_app/util/codelist.py +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from fastapi import Header, HTTPException
|
|
2
|
+
from semver import format_version, parse
|
|
3
|
+
|
|
4
|
+
from xmas_app.settings import settings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def enforce_plugin_version(
|
|
8
|
+
user_agent: str = Header(...),
|
|
9
|
+
):
|
|
10
|
+
if not user_agent.startswith(settings.qgis_plugin_name):
|
|
11
|
+
return
|
|
12
|
+
try:
|
|
13
|
+
_, plugin_version = user_agent.split("/")
|
|
14
|
+
client_v = parse(plugin_version)
|
|
15
|
+
except Exception:
|
|
16
|
+
raise HTTPException(
|
|
17
|
+
status_code=400,
|
|
18
|
+
detail=f"Invalid {settings.qgis_plugin_name} user-agent header: '{user_agent}'",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if client_v < settings.qgis_plugin_min_version:
|
|
22
|
+
# 426 Upgrade Required is appropriate
|
|
23
|
+
raise HTTPException(
|
|
24
|
+
status_code=426,
|
|
25
|
+
detail=f"Plugin version {settings.qgis_plugin_min_version}+ required, got {format_version(**client_v)}",
|
|
26
|
+
)
|
|
@@ -3,6 +3,7 @@ import importlib
|
|
|
3
3
|
import inspect
|
|
4
4
|
import io
|
|
5
5
|
import logging
|
|
6
|
+
import os
|
|
6
7
|
import re
|
|
7
8
|
from contextlib import asynccontextmanager
|
|
8
9
|
from functools import partial
|
|
@@ -18,10 +19,23 @@ from pydantic import ValidationError
|
|
|
18
19
|
from starlette.applications import Starlette
|
|
19
20
|
from xplan_tools.util import get_geometry_type_from_wkt
|
|
20
21
|
|
|
22
|
+
from xmas_app.deps.version_guard import enforce_plugin_version
|
|
21
23
|
from xmas_app.schema import ErrorDetail, ErrorResponse, SplitPayload, SplitSuccess
|
|
22
24
|
from xmas_app.split_service import PlanSplitService, SplitValidationError
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
|
|
27
|
+
def _resolve_log_dir() -> Path:
|
|
28
|
+
# If launched from QGIS plugin
|
|
29
|
+
if os.getenv("XMAS_APP_FROM_PLUGIN") == "1":
|
|
30
|
+
plugin_dir = os.getenv("XMAS_APP_PLUGIN_DIR")
|
|
31
|
+
if plugin_dir:
|
|
32
|
+
return Path(plugin_dir) / "webapp_logs"
|
|
33
|
+
|
|
34
|
+
# default: local repo logs (dev)
|
|
35
|
+
return Path(__file__).parent / "logs"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
log_dir = _resolve_log_dir()
|
|
25
39
|
log_dir.mkdir(exist_ok=True)
|
|
26
40
|
log_file = log_dir / "xmas_app.log"
|
|
27
41
|
|
|
@@ -37,7 +51,7 @@ if not logger.handlers:
|
|
|
37
51
|
logger.addHandler(fh)
|
|
38
52
|
logger.debug(f"Writing logs to {log_file}")
|
|
39
53
|
|
|
40
|
-
from fastapi import APIRouter, HTTPException, Request, status
|
|
54
|
+
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
|
41
55
|
from nicegui import app, run, ui
|
|
42
56
|
from nicegui.events import ClickEventArguments, ValueChangeEventArguments
|
|
43
57
|
from xplan_tools.interface import repo_factory
|
|
@@ -114,10 +128,8 @@ async def get_model_for_select(
|
|
|
114
128
|
)
|
|
115
129
|
|
|
116
130
|
|
|
117
|
-
@ui.page("/")
|
|
118
|
-
def index(
|
|
119
|
-
request: Request,
|
|
120
|
-
):
|
|
131
|
+
@ui.page("/", dependencies=[Depends(enforce_plugin_version)])
|
|
132
|
+
def index():
|
|
121
133
|
"""
|
|
122
134
|
Render the main index page of the XPlan-GUI application.
|
|
123
135
|
Args:
|
|
@@ -138,7 +150,11 @@ def index(
|
|
|
138
150
|
)
|
|
139
151
|
|
|
140
152
|
|
|
141
|
-
@ui.page(
|
|
153
|
+
@ui.page(
|
|
154
|
+
"/plan-tree/{id}",
|
|
155
|
+
reconnect_timeout=5,
|
|
156
|
+
dependencies=[Depends(enforce_plugin_version)],
|
|
157
|
+
)
|
|
142
158
|
async def plan_tree(
|
|
143
159
|
request: Request,
|
|
144
160
|
id: str,
|
|
@@ -385,7 +401,7 @@ async def plan_tree(
|
|
|
385
401
|
tree_filter.bind_value_to(tree, "filter")
|
|
386
402
|
|
|
387
403
|
|
|
388
|
-
@ui.page("/plans")
|
|
404
|
+
@ui.page("/plans", dependencies=[Depends(enforce_plugin_version)])
|
|
389
405
|
async def plans(
|
|
390
406
|
request: Request,
|
|
391
407
|
appschema: str = settings.appschema,
|
|
@@ -708,7 +724,7 @@ async def plans(
|
|
|
708
724
|
new_plan_select.set_value(new_plan_select.options[0])
|
|
709
725
|
|
|
710
726
|
|
|
711
|
-
@ui.page("/feature/{id}")
|
|
727
|
+
@ui.page("/feature/{id}", dependencies=[Depends(enforce_plugin_version)])
|
|
712
728
|
async def feature(
|
|
713
729
|
request: Request,
|
|
714
730
|
id: str,
|
|
@@ -939,7 +955,7 @@ async def feature(
|
|
|
939
955
|
await add_form(feature_type, feature)
|
|
940
956
|
|
|
941
957
|
|
|
942
|
-
@ui.page("/feature/{id}/associations")
|
|
958
|
+
@ui.page("/feature/{id}/associations", dependencies=[Depends(enforce_plugin_version)])
|
|
943
959
|
def get_associations(
|
|
944
960
|
request: Request,
|
|
945
961
|
id: str,
|
|
@@ -1121,20 +1137,22 @@ def validate_featuretype(
|
|
|
1121
1137
|
return "OK"
|
|
1122
1138
|
|
|
1123
1139
|
|
|
1124
|
-
@app.post(
|
|
1140
|
+
@app.post(
|
|
1141
|
+
"/insert-features", status_code=201, dependencies=[Depends(enforce_plugin_version)]
|
|
1142
|
+
)
|
|
1125
1143
|
async def insert_features(payload: InsertPayload):
|
|
1126
1144
|
"""Insert a number of features."""
|
|
1127
1145
|
await crud.create(payload)
|
|
1128
1146
|
|
|
1129
1147
|
|
|
1130
|
-
@app.post("/update-features")
|
|
1148
|
+
@app.post("/update-features", dependencies=[Depends(enforce_plugin_version)])
|
|
1131
1149
|
async def update_features(payload: UpdatePayload):
|
|
1132
1150
|
"""Update a number of features."""
|
|
1133
1151
|
await crud.update(payload)
|
|
1134
1152
|
|
|
1135
1153
|
|
|
1136
1154
|
@app.post(
|
|
1137
|
-
"/
|
|
1155
|
+
"/split-tool",
|
|
1138
1156
|
response_model=SplitSuccess,
|
|
1139
1157
|
status_code=status.HTTP_201_CREATED, # semantically 'created'
|
|
1140
1158
|
responses={
|
|
@@ -1142,6 +1160,7 @@ async def update_features(payload: UpdatePayload):
|
|
|
1142
1160
|
422: {"model": ErrorResponse, "description": "Validation Error"},
|
|
1143
1161
|
500: {"model": ErrorResponse, "description": "Server Error"},
|
|
1144
1162
|
},
|
|
1163
|
+
dependencies=[Depends(enforce_plugin_version)],
|
|
1145
1164
|
)
|
|
1146
1165
|
async def receive_split_plans(payload: SplitPayload) -> SplitSuccess:
|
|
1147
1166
|
logger.info("Split plans endpoint reached.")
|
|
@@ -55,7 +55,7 @@ class Settings(BaseSettings):
|
|
|
55
55
|
"https://registry.gdi-de.org/codelist/de.xleitstelle.xplanung"
|
|
56
56
|
)
|
|
57
57
|
qgis_plugin_name: str = "XMAS-Plugin"
|
|
58
|
-
qgis_plugin_min_version: _VersionPydanticAnnotation = Version(major=0, minor=
|
|
58
|
+
qgis_plugin_min_version: _VersionPydanticAnnotation = Version(major=0, minor=10)
|
|
59
59
|
|
|
60
60
|
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
|
|
61
61
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|