xmas-app 0.9.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.9.0 → xmas_app-0.11.0}/PKG-INFO +9 -7
- {xmas_app-0.9.0 → xmas_app-0.11.0}/README.md +6 -5
- {xmas_app-0.9.0 → xmas_app-0.11.0}/pyproject.toml +18 -2
- xmas_app-0.11.0/xmas_app/deps/version_guard.py +26 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/form.py +3 -1
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/main.py +53 -59
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/settings.py +4 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/LICENSE +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/db.py +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/db_uow.py +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/models/crud.py +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/pygeoapi/config.yaml +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/pygeoapi/openapi.yaml +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/pygeoapi/provider.py +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/schema.py +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/services/crud.py +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/split_service.py +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/util/__init__.py +0 -0
- {xmas_app-0.9.0 → xmas_app-0.11.0}/xmas_app/util/codelist.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xmas-app
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: The XLeitstelle model-driven application schema app.
|
|
5
5
|
License: EUPL-1.2-or-later
|
|
6
6
|
License-File: LICENSE
|
|
@@ -13,10 +13,11 @@ Classifier: License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1
|
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Requires-Dist: nicegui (>=2.20,<3.0)
|
|
16
|
+
Requires-Dist: pydantic-extra-types[semver]
|
|
16
17
|
Requires-Dist: pydantic-settings (>=2.0,<3.0)
|
|
17
18
|
Requires-Dist: xplan-tools (>=1.10.3)
|
|
18
19
|
Project-URL: Homepage, https://gitlab.opencode.de/xleitstelle/xmas-app
|
|
19
|
-
Project-URL: Issues, https://gitlab.opencode.de/xleitstelle/
|
|
20
|
+
Project-URL: Issues, https://gitlab.opencode.de/xleitstelle/xmas-app/-/issues
|
|
20
21
|
Description-Content-Type: text/markdown
|
|
21
22
|
|
|
22
23
|
# XMAS-App
|
|
@@ -106,7 +107,7 @@ pixi shell
|
|
|
106
107
|
then run
|
|
107
108
|
|
|
108
109
|
```shell
|
|
109
|
-
|
|
110
|
+
xmas-app
|
|
110
111
|
```
|
|
111
112
|
|
|
112
113
|
### Optional: batch import test data to the database using xplan-tools
|
|
@@ -125,10 +126,11 @@ done
|
|
|
125
126
|
(on Windows:):
|
|
126
127
|
```cmd
|
|
127
128
|
for %f in (*.gml) do xplan-tools convert "%f" postgresql://postgres:postgres@127.0.0.1:55432/postgres
|
|
128
|
-
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## License
|
|
129
132
|
|
|
130
|
-
|
|
133
|
+
The code in this repository is licensed under the [EUPL-1.2-or-later](https://joinup.ec.europa.eu/collection/eupl)
|
|
131
134
|
|
|
132
|
-
|
|
133
|
-
This does not affect production deployments, which can use secure defaults.
|
|
135
|
+
© [XLeitstelle](https://xleitstelle.de), 2025
|
|
134
136
|
|
|
@@ -85,7 +85,7 @@ pixi shell
|
|
|
85
85
|
then run
|
|
86
86
|
|
|
87
87
|
```shell
|
|
88
|
-
|
|
88
|
+
xmas-app
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
### Optional: batch import test data to the database using xplan-tools
|
|
@@ -104,9 +104,10 @@ done
|
|
|
104
104
|
(on Windows:):
|
|
105
105
|
```cmd
|
|
106
106
|
for %f in (*.gml) do xplan-tools convert "%f" postgresql://postgres:postgres@127.0.0.1:55432/postgres
|
|
107
|
-
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## License
|
|
108
110
|
|
|
109
|
-
|
|
111
|
+
The code in this repository is licensed under the [EUPL-1.2-or-later](https://joinup.ec.europa.eu/collection/eupl)
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
This does not affect production deployments, which can use secure defaults.
|
|
113
|
+
© [XLeitstelle](https://xleitstelle.de), 2025
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "xmas-app"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.11.0"
|
|
4
4
|
description = "The XLeitstelle model-driven application schema app."
|
|
5
5
|
authors = [{ name = "Tobias Kraft", email = "tobias.kraft@gv.hamburg.de" }]
|
|
6
6
|
classifiers = [
|
|
@@ -15,6 +15,7 @@ license = "EUPL-1.2-or-later"
|
|
|
15
15
|
license-files = ["LICENSE"]
|
|
16
16
|
dependencies = [
|
|
17
17
|
"pydantic-settings>=2.0,<3.0",
|
|
18
|
+
"pydantic-extra-types[semver]",
|
|
18
19
|
"xplan-tools>=1.10.3",
|
|
19
20
|
"nicegui>=2.20,<3.0",
|
|
20
21
|
# "uvicorn[standard]>=0.29,<0.31"
|
|
@@ -25,7 +26,7 @@ requires-python = ">=3.11,<3.14"
|
|
|
25
26
|
|
|
26
27
|
[project.urls]
|
|
27
28
|
Homepage = "https://gitlab.opencode.de/xleitstelle/xmas-app"
|
|
28
|
-
Issues = "https://gitlab.opencode.de/xleitstelle/
|
|
29
|
+
Issues = "https://gitlab.opencode.de/xleitstelle/xmas-app/-/issues"
|
|
29
30
|
|
|
30
31
|
[project.scripts]
|
|
31
32
|
xmas-app = "xmas_app.main:run_server"
|
|
@@ -51,6 +52,21 @@ libgdal-core = ">=3.9.1,<4"
|
|
|
51
52
|
python = { version = "3.11.*", build = "*_cpython*" }
|
|
52
53
|
openssl = "3.3.*"
|
|
53
54
|
|
|
55
|
+
[tool.pixi.tasks.release]
|
|
56
|
+
args = [
|
|
57
|
+
{ "arg" = "version", "default" = "minor" }
|
|
58
|
+
]
|
|
59
|
+
cmd = """
|
|
60
|
+
pixi workspace version {{version}}
|
|
61
|
+
&& pixi lock
|
|
62
|
+
&& git add pyproject.toml pixi.lock
|
|
63
|
+
&& git commit -m "bump to $(pixi workspace version get)"
|
|
64
|
+
&& git tag $(pixi workspace version get)
|
|
65
|
+
&& git push
|
|
66
|
+
&& git push --tags
|
|
67
|
+
&& pixi workspace version get
|
|
68
|
+
"""
|
|
69
|
+
|
|
54
70
|
[dependency-groups]
|
|
55
71
|
dev = ["pre-commit>=4", "ruff>=0.8"]
|
|
56
72
|
build = ["poetry>=2.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
|
+
)
|
|
@@ -710,7 +710,9 @@ class ModelForm:
|
|
|
710
710
|
ui.select(
|
|
711
711
|
options,
|
|
712
712
|
label=prop_info["typename"],
|
|
713
|
-
on_change=self._set_stylesheetId_and_schriftinhalt
|
|
713
|
+
on_change=self._set_stylesheetId_and_schriftinhalt
|
|
714
|
+
if self.editable
|
|
715
|
+
else None,
|
|
714
716
|
multiple=prop_info["list"],
|
|
715
717
|
clearable=prop_info["nullable"],
|
|
716
718
|
validation=None
|
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
import pydantic
|
|
4
|
-
import pydantic_core
|
|
5
|
-
from nicegui.observables import ObservableSet
|
|
6
|
-
from xplan_tools.util import get_geometry_type_from_wkt
|
|
7
|
-
|
|
8
|
-
from xmas_app.schema import ErrorDetail, ErrorResponse, SplitPayload, SplitSuccess
|
|
9
|
-
from xmas_app.split_service import PlanSplitService, SplitValidationError
|
|
10
|
-
|
|
11
|
-
os.environ["PGGSSENCMODE"] = "disable"
|
|
12
|
-
os.environ["PGSSLMODE"] = "disable"
|
|
13
|
-
|
|
14
1
|
import asyncio
|
|
15
2
|
import importlib
|
|
16
3
|
import inspect
|
|
17
4
|
import io
|
|
18
5
|
import logging
|
|
6
|
+
import os
|
|
19
7
|
import re
|
|
20
8
|
from contextlib import asynccontextmanager
|
|
21
9
|
from functools import partial
|
|
@@ -24,10 +12,30 @@ from tempfile import NamedTemporaryFile
|
|
|
24
12
|
from typing import Any, Literal
|
|
25
13
|
from uuid import uuid4
|
|
26
14
|
|
|
15
|
+
import pydantic
|
|
16
|
+
import pydantic_core
|
|
17
|
+
from nicegui.observables import ObservableSet
|
|
27
18
|
from pydantic import ValidationError
|
|
28
19
|
from starlette.applications import Starlette
|
|
20
|
+
from xplan_tools.util import get_geometry_type_from_wkt
|
|
29
21
|
|
|
30
|
-
|
|
22
|
+
from xmas_app.deps.version_guard import enforce_plugin_version
|
|
23
|
+
from xmas_app.schema import ErrorDetail, ErrorResponse, SplitPayload, SplitSuccess
|
|
24
|
+
from xmas_app.split_service import PlanSplitService, SplitValidationError
|
|
25
|
+
|
|
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()
|
|
31
39
|
log_dir.mkdir(exist_ok=True)
|
|
32
40
|
log_file = log_dir / "xmas_app.log"
|
|
33
41
|
|
|
@@ -43,20 +51,7 @@ if not logger.handlers:
|
|
|
43
51
|
logger.addHandler(fh)
|
|
44
52
|
logger.debug(f"Writing logs to {log_file}")
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
# ui.timer(
|
|
48
|
-
# 1.0,
|
|
49
|
-
# lambda: (
|
|
50
|
-
# print("bindings:", len(binding.bindings)),
|
|
51
|
-
# print("a. links:", len(binding.active_links)),
|
|
52
|
-
# print("b. props:", len(binding.bindable_properties)),
|
|
53
|
-
# print(
|
|
54
|
-
# "c. link elements:", "\n".join([str(link) for link in binding.active_links])
|
|
55
|
-
# ),
|
|
56
|
-
# ),
|
|
57
|
-
# )
|
|
58
|
-
|
|
59
|
-
from fastapi import APIRouter, HTTPException, Request, status
|
|
54
|
+
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
|
60
55
|
from nicegui import app, run, ui
|
|
61
56
|
from nicegui.events import ClickEventArguments, ValueChangeEventArguments
|
|
62
57
|
from xplan_tools.interface import repo_factory
|
|
@@ -133,10 +128,8 @@ async def get_model_for_select(
|
|
|
133
128
|
)
|
|
134
129
|
|
|
135
130
|
|
|
136
|
-
@ui.page("/")
|
|
137
|
-
def index(
|
|
138
|
-
request: Request,
|
|
139
|
-
):
|
|
131
|
+
@ui.page("/", dependencies=[Depends(enforce_plugin_version)])
|
|
132
|
+
def index():
|
|
140
133
|
"""
|
|
141
134
|
Render the main index page of the XPlan-GUI application.
|
|
142
135
|
Args:
|
|
@@ -157,7 +150,11 @@ def index(
|
|
|
157
150
|
)
|
|
158
151
|
|
|
159
152
|
|
|
160
|
-
@ui.page(
|
|
153
|
+
@ui.page(
|
|
154
|
+
"/plan-tree/{id}",
|
|
155
|
+
reconnect_timeout=5,
|
|
156
|
+
dependencies=[Depends(enforce_plugin_version)],
|
|
157
|
+
)
|
|
161
158
|
async def plan_tree(
|
|
162
159
|
request: Request,
|
|
163
160
|
id: str,
|
|
@@ -170,7 +167,7 @@ async def plan_tree(
|
|
|
170
167
|
tree.update()
|
|
171
168
|
|
|
172
169
|
app.storage.client["user_agent"] = request.headers.get("user-agent", None)
|
|
173
|
-
qgis = app.storage.client["user_agent"]
|
|
170
|
+
qgis = app.storage.client["user_agent"].startswith(settings.qgis_plugin_name)
|
|
174
171
|
if qgis:
|
|
175
172
|
ui.add_head_html('<script src="qrc:///qtwebchannel/qwebchannel.js"></script>')
|
|
176
173
|
|
|
@@ -244,23 +241,17 @@ async def plan_tree(
|
|
|
244
241
|
if feature.get_geom_wkt():
|
|
245
242
|
ui.button("Zum Feature springen").on(
|
|
246
243
|
"click",
|
|
247
|
-
lambda e,
|
|
248
|
-
e, str(origin), str(target)
|
|
249
|
-
),
|
|
244
|
+
lambda e, target=target: highlight_feature(e, str(target)),
|
|
250
245
|
)
|
|
251
246
|
|
|
252
247
|
ui.button("Feature selektieren").on(
|
|
253
248
|
"click",
|
|
254
|
-
lambda e,
|
|
255
|
-
e, str(origin), str(target)
|
|
256
|
-
),
|
|
249
|
+
lambda e, target=target: select_feature(e, str(target)),
|
|
257
250
|
)
|
|
258
251
|
|
|
259
252
|
ui.button("Attributformular öffnen").on(
|
|
260
253
|
"click",
|
|
261
|
-
lambda e,
|
|
262
|
-
e, str(origin), str(target)
|
|
263
|
-
),
|
|
254
|
+
lambda e, target=target: show_attribute_form(e, str(target)),
|
|
264
255
|
)
|
|
265
256
|
|
|
266
257
|
# ui.button(
|
|
@@ -308,12 +299,12 @@ async def plan_tree(
|
|
|
308
299
|
return
|
|
309
300
|
action_dialog.open()
|
|
310
301
|
|
|
311
|
-
async def highlight_feature(e: ClickEventArguments,
|
|
302
|
+
async def highlight_feature(e: ClickEventArguments, target: str):
|
|
312
303
|
logger.debug("highlight_feature called")
|
|
313
304
|
try:
|
|
314
305
|
logger.info("Sending highlight_feature to QWebChannel handler")
|
|
315
306
|
ui.run_javascript(f"""new QWebChannel(qt.webChannelTransport, function (channel) {{
|
|
316
|
-
channel.objects.handler.highlight_feature("{
|
|
307
|
+
channel.objects.handler.highlight_feature("{target}")
|
|
317
308
|
}});""")
|
|
318
309
|
except Exception as ex:
|
|
319
310
|
logger.error(
|
|
@@ -322,12 +313,12 @@ async def plan_tree(
|
|
|
322
313
|
finally:
|
|
323
314
|
e.sender.props(remove="loading")
|
|
324
315
|
|
|
325
|
-
async def show_attribute_form(e: ClickEventArguments,
|
|
316
|
+
async def show_attribute_form(e: ClickEventArguments, target: str):
|
|
326
317
|
logger.debug("show_attribute_form called")
|
|
327
318
|
try:
|
|
328
319
|
logger.info("Sending show_attribute_form to QWebChannel handler")
|
|
329
320
|
ui.run_javascript(f"""new QWebChannel(qt.webChannelTransport, function (channel) {{
|
|
330
|
-
channel.objects.handler.show_attribute_form("{
|
|
321
|
+
channel.objects.handler.show_attribute_form("{target}")
|
|
331
322
|
}});""")
|
|
332
323
|
except Exception as ex:
|
|
333
324
|
logger.error(
|
|
@@ -336,12 +327,12 @@ async def plan_tree(
|
|
|
336
327
|
finally:
|
|
337
328
|
e.sender.props(remove="loading")
|
|
338
329
|
|
|
339
|
-
async def select_feature(e: ClickEventArguments,
|
|
330
|
+
async def select_feature(e: ClickEventArguments, target: str):
|
|
340
331
|
logger.debug("highlight_feature called")
|
|
341
332
|
try:
|
|
342
333
|
logger.info("Sending select_feature to QWebChannel handler")
|
|
343
334
|
ui.run_javascript(f"""new QWebChannel(qt.webChannelTransport, function (channel) {{
|
|
344
|
-
channel.objects.handler.select_feature("{
|
|
335
|
+
channel.objects.handler.select_feature("{target}")
|
|
345
336
|
}});""")
|
|
346
337
|
except Exception as ex:
|
|
347
338
|
logger.error(f"Exception while select_feature called: {ex}", exc_info=True)
|
|
@@ -397,7 +388,7 @@ async def plan_tree(
|
|
|
397
388
|
nodes,
|
|
398
389
|
on_select=show_menu,
|
|
399
390
|
tick_strategy=None,
|
|
400
|
-
).props("accordion")
|
|
391
|
+
).props("accordion no-transition")
|
|
401
392
|
|
|
402
393
|
notification.spinner = False
|
|
403
394
|
notification.type = "positive"
|
|
@@ -410,7 +401,7 @@ async def plan_tree(
|
|
|
410
401
|
tree_filter.bind_value_to(tree, "filter")
|
|
411
402
|
|
|
412
403
|
|
|
413
|
-
@ui.page("/plans")
|
|
404
|
+
@ui.page("/plans", dependencies=[Depends(enforce_plugin_version)])
|
|
414
405
|
async def plans(
|
|
415
406
|
request: Request,
|
|
416
407
|
appschema: str = settings.appschema,
|
|
@@ -434,7 +425,7 @@ async def plans(
|
|
|
434
425
|
ui.dropdown_button.default_props("flat square no-caps")
|
|
435
426
|
ui.select.default_props("square filled dense")
|
|
436
427
|
app.storage.client["user_agent"] = request.headers.get("user-agent", None)
|
|
437
|
-
qgis = app.storage.client["user_agent"]
|
|
428
|
+
qgis = app.storage.client["user_agent"].startswith(settings.qgis_plugin_name)
|
|
438
429
|
if qgis:
|
|
439
430
|
ui.add_head_html('<script src="qrc:///qtwebchannel/qwebchannel.js"></script>')
|
|
440
431
|
|
|
@@ -733,7 +724,7 @@ async def plans(
|
|
|
733
724
|
new_plan_select.set_value(new_plan_select.options[0])
|
|
734
725
|
|
|
735
726
|
|
|
736
|
-
@ui.page("/feature/{id}")
|
|
727
|
+
@ui.page("/feature/{id}", dependencies=[Depends(enforce_plugin_version)])
|
|
737
728
|
async def feature(
|
|
738
729
|
request: Request,
|
|
739
730
|
id: str,
|
|
@@ -865,7 +856,7 @@ async def feature(
|
|
|
865
856
|
app.storage.client["parent_id"] = parentId
|
|
866
857
|
app.storage.client["user_agent"] = request.headers.get("user-agent", None)
|
|
867
858
|
|
|
868
|
-
qgis = app.storage.client["user_agent"]
|
|
859
|
+
qgis = app.storage.client["user_agent"].startswith(settings.qgis_plugin_name)
|
|
869
860
|
|
|
870
861
|
ui.colors(
|
|
871
862
|
# primary=f"{'#f0f0f0' if qgis else 'rgb(157 157 156)'}",
|
|
@@ -964,7 +955,7 @@ async def feature(
|
|
|
964
955
|
await add_form(feature_type, feature)
|
|
965
956
|
|
|
966
957
|
|
|
967
|
-
@ui.page("/feature/{id}/associations")
|
|
958
|
+
@ui.page("/feature/{id}/associations", dependencies=[Depends(enforce_plugin_version)])
|
|
968
959
|
def get_associations(
|
|
969
960
|
request: Request,
|
|
970
961
|
id: str,
|
|
@@ -1059,7 +1050,7 @@ def get_associations(
|
|
|
1059
1050
|
|
|
1060
1051
|
app.storage.client["user_agent"] = request.headers.get("user-agent", None)
|
|
1061
1052
|
|
|
1062
|
-
qgis = app.storage.client["user_agent"]
|
|
1053
|
+
qgis = app.storage.client["user_agent"].startswith(settings.qgis_plugin_name)
|
|
1063
1054
|
|
|
1064
1055
|
feature_data = {}
|
|
1065
1056
|
|
|
@@ -1146,20 +1137,22 @@ def validate_featuretype(
|
|
|
1146
1137
|
return "OK"
|
|
1147
1138
|
|
|
1148
1139
|
|
|
1149
|
-
@app.post(
|
|
1140
|
+
@app.post(
|
|
1141
|
+
"/insert-features", status_code=201, dependencies=[Depends(enforce_plugin_version)]
|
|
1142
|
+
)
|
|
1150
1143
|
async def insert_features(payload: InsertPayload):
|
|
1151
1144
|
"""Insert a number of features."""
|
|
1152
1145
|
await crud.create(payload)
|
|
1153
1146
|
|
|
1154
1147
|
|
|
1155
|
-
@app.post("/update-features")
|
|
1148
|
+
@app.post("/update-features", dependencies=[Depends(enforce_plugin_version)])
|
|
1156
1149
|
async def update_features(payload: UpdatePayload):
|
|
1157
1150
|
"""Update a number of features."""
|
|
1158
1151
|
await crud.update(payload)
|
|
1159
1152
|
|
|
1160
1153
|
|
|
1161
1154
|
@app.post(
|
|
1162
|
-
"/
|
|
1155
|
+
"/split-tool",
|
|
1163
1156
|
response_model=SplitSuccess,
|
|
1164
1157
|
status_code=status.HTTP_201_CREATED, # semantically 'created'
|
|
1165
1158
|
responses={
|
|
@@ -1167,6 +1160,7 @@ async def update_features(payload: UpdatePayload):
|
|
|
1167
1160
|
422: {"model": ErrorResponse, "description": "Validation Error"},
|
|
1168
1161
|
500: {"model": ErrorResponse, "description": "Server Error"},
|
|
1169
1162
|
},
|
|
1163
|
+
dependencies=[Depends(enforce_plugin_version)],
|
|
1170
1164
|
)
|
|
1171
1165
|
async def receive_split_plans(payload: SplitPayload) -> SplitSuccess:
|
|
1172
1166
|
logger.info("Split plans endpoint reached.")
|
|
@@ -2,7 +2,9 @@ import os
|
|
|
2
2
|
from typing import Any, Dict, List, Literal, Optional, TypedDict
|
|
3
3
|
|
|
4
4
|
from pydantic import HttpUrl
|
|
5
|
+
from pydantic_extra_types.semver import _VersionPydanticAnnotation
|
|
5
6
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
7
|
+
from semver import Version
|
|
6
8
|
from xplan_tools.interface.db import DBRepository
|
|
7
9
|
|
|
8
10
|
|
|
@@ -52,6 +54,8 @@ class Settings(BaseSettings):
|
|
|
52
54
|
codelist_repo: HttpUrl = HttpUrl(
|
|
53
55
|
"https://registry.gdi-de.org/codelist/de.xleitstelle.xplanung"
|
|
54
56
|
)
|
|
57
|
+
qgis_plugin_name: str = "XMAS-Plugin"
|
|
58
|
+
qgis_plugin_min_version: _VersionPydanticAnnotation = Version(major=0, minor=10)
|
|
55
59
|
|
|
56
60
|
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
|
|
57
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
|