Metadata-Version: 2.4
Name: xmas-app
Version: 1.0.0.dev1
Summary: The XLeitstelle model-driven application schema app.
License: EUPL-1.2-or-later
License-File: LICENSE
Author: Tobias Kraft
Author-email: tobias.kraft@gv.hamburg.de
Requires-Python: >=3.12,<3.14
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Pydantic :: 2
Classifier: License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Requires-Dist: aiohttp (>=3.13.5)
Requires-Dist: httpx2 (>=2.4.0,<3)
Requires-Dist: nicegui (>=3.12,<4.0)
Requires-Dist: pydantic-extra-types[semver]
Requires-Dist: pydantic-settings (>=2.0,<3.0)
Requires-Dist: structlog
Requires-Dist: tenacity (>=9.1.4,<10)
Requires-Dist: xplan-tools (==1.24.1)
Project-URL: Homepage, https://gitlab.opencode.de/xleitstelle/xmas-app
Project-URL: Issues, https://gitlab.opencode.de/xleitstelle/xmas-app/-/issues
Description-Content-Type: text/markdown

# XMAS-App

[![pipeline status](https://gitlab.opencode.de/xleitstelle/xmas-app/badges/main/pipeline.svg)](https://gitlab.opencode.de/xleitstelle/xmas-app/-/commits/main)
[![Latest Release](https://gitlab.opencode.de/xleitstelle/xmas-app/-/badges/release.svg)](https://gitlab.opencode.de/xleitstelle/xmas-app/-/releases)

The **X**Leistelle **m**odel-driven **a**pplication **s**chema app is a Python web application to edit and create data according to geo-spatial standards of the [XLeistelle](https://xleitstelle.de), e.g. XPlanung, XTrasse. It is based on [NiceGUI](https://nicegui.io/) for a graphical user interface
and [XPlan-Tools](https://gitlab.opencode.de/xleitstelle/xplanung/xplan-tools) for the data model and respective functionality.

While it could be advanced to a standalone application, its current focus is [integration in QGIS](https://gitlab.opencode.de/xleitstelle/xmas-plugin) to provide attribute forms etc.

## Features

* Render attribute forms for features.
* Create and edit features in combination with the corresponding QGIS Plugin.
* Import GML documents into a database.
* Export GML, JSON-FG or GPKG files from a database.
* Delete plans from a database.
* Display and edit relations of plan objects in a tree view.

## Installation

### Container Image
Container images are available in the [registry](https://gitlab.opencode.de/xleitstelle/xmas-app/container_registry). They are built and signed by the pipeline and tagged per release version and per integration branch (`dev-amd64`); there is no `latest` tag. See [Running via Docker Compose](#running-via-docker-compose) for configuration.

### Python >= v3.11

[GDAL](https://gdal.org) and its Python bindings are required, so you need to make sure the GDAL system library and Python package versions match.
QGIS installations come with GDAL and a Python environment that can readily be used to install the app.

Install with `pip`, e.g. via OSGeo4W Shell:
```shell
pip install xmas-app
```

### Pixi
This project uses [Pixi](https://pixi.sh) for package management. To install this repo with a self-contained environment, run

```shell
git clone https://gitlab.opencode.de/xleitstelle/xmas-app.git
cd xmas-app
pixi install
```

## Running

### Preconditions
A Postgres DB with PostGIS extension and adequate permissions for the used role. If required tables were not previously created with `xplan-tools`, they will be generated on initialization.

### Environment Configuration

Copy the example configuration and adjust values as needed:

```shell
cp .env.example .env
```

`.env.example` documents all available variables with inline comments, including database connection, app settings, and authentication. See that file as the canonical reference.

**Important:**

- Do not use the example values in production
- All credentials and tokens must be set explicitly
- The application will fail to start if required variables are missing

### Running via Docker Compose

```shell
docker compose -f compose.yaml up -d
```

This spins up a complete XMAS-App stack including a PostGIS backend and Keycloak. Configure the stack via `.env` — see `.env.example` for the container-mode database settings (`PGHOST=postgres`, `PGPORT=5432`).

#### Optional: testing `ROOT_PATH` sub-path deployments

To verify running the app under a URL sub-path (so several instances can share one domain), start the `reverse-proxy` service via its compose profile:

```shell
ROOT_PATH=/test docker compose -f compose.yaml --profile proxy up -d
```

This adds an nginx container (see `nginx/default.conf.template`) that strips `ROOT_PATH` before forwarding to `xmas-app`, mirroring a real front-facing proxy. Browse `http://localhost:${PROXY_PORT:-8000}/test/` — not `xmas-app`'s own port directly, since `ROOT_PATH` only affects URL generation, not routing; see [`xmas_app/core/settings.py`](xmas_app/core/settings.py) for the setting itself.

### Running Locally (host machine)

First activate the Pixi shell:

```shell
pixi shell
```

Then run:

```shell
xmas-app
```

Use the local-mode database settings in `.env` (`PGHOST=127.0.0.1`, `PGPORT=15432`).

### Optional: batch import test data

Test data can be downloaded from https://gitlab.opencode.de/xleitstelle/xplanung/testdaten, e.g. a ZIP archive with [BP_Plans in v 6.0](https://gitlab.opencode.de/xleitstelle/xplanung/testdaten/-/archive/main/testdaten-main.zip?path=valide/6_0/bp).

To import all `.gml` files at once, extract the archive and use:

**Linux / macOS / WSL:**
```bash
for f in <path>/bp/*.gml; do
  xplan-tools convert "$f" postgresql://postgres:postgres@localhost:55432/postgres
done
```

**Windows:**
```cmd
for %f in (*.gml) do xplan-tools convert "%f" postgresql://postgres:postgres@127.0.0.1:55432/postgres
```

## Authentication

The application supports two authentication modes, controlled by the `AUTH_MODE` environment variable.

### Static token

Intended for development and testing only. Not suitable for production.

Set `AUTH_MODE=static` in `.env`. See `.env.example` for the token format.

Requests must include:
```
Authorization: Bearer <token>
```

### Keycloak JWT (recommended for staging and production)

Validates RS256-signed access tokens issued by a Keycloak realm via the JWKS endpoint.

Set `AUTH_MODE=keycloak` in `.env`. See `.env.example` for the required Keycloak variables.

> **Note:** `KEYCLOAK_ISSUER` must match the `iss` claim in the token exactly — including scheme, host, and port. `KEYCLOAK_JWKS_URL` is the address of the Keycloak service as seen from inside the Docker network, which may differ from the issuer URL seen by clients.

The local Keycloak instance is started automatically via `compose.yaml`. A development realm (`realm-xmas-dev.json`) is imported on first start, including a test user and the required audience mapper.

For Keycloak setup, Identity Provider federation (e.g. Microsoft Entra ID / SSO), and onboarding documentation, see [`keycloak/README.md`](keycloak/README.md).

## Running Tests Locally

Integration tests use `testcontainers` to start a temporary PostGIS database.
Make sure Docker is running before executing the tests.

The test database is seeded from a git submodule `./testdaten/` (https://gitlab.opencode.de/xleitstelle/xplanung/testdaten).

Initialize the submodule first:

```shell
git submodule update --init --recursive
```

Then run:
```shell
pixi run pytest tests
```

## Troubleshooting

### PyJWKClientConnectionError
The plugin can't reach the app, and the app's container log shows `PyJWKClientConnectionError`.
Verify that the env variables are being passed correctly and that `KEYCLOAK_JWKS_URL` has the right value.
Make sure proxy settings aren't blocking Keycloak: add `keycloak` to the `NO_PROXY` list if not yet set in the compose file.

## License

The code in this repository is licensed under the [EUPL-1.2-or-later](https://joinup.ec.europa.eu/collection/eupl)

&copy; [XLeitstelle](https://xleitstelle.de), 2025

