Compare commits

...

12 commits

Author SHA1 Message Date
8d34c450e3 Cleanup version computation, add gisaf.__version__
All checks were successful
/ test (push) Successful in 31s
/ build (push) Successful in 2m22s
2025-03-16 19:52:28 +01:00
a3a2efe471 Use dynamic versioning
All checks were successful
/ test (push) Successful in 31s
/ build (push) Successful in 32s
2025-03-16 19:02:10 +01:00
d54bb178dd Update README.md
All checks were successful
/ test (push) Successful in 32s
2024-12-30 17:02:17 +00:00
93b9903e0d Update README.md
All checks were successful
/ test (push) Successful in 30s
2024-12-27 23:47:03 +00:00
e53ed92a56 Update doc
All checks were successful
/ test (push) Successful in 30s
/ build (push) Successful in 7s
2024-12-27 19:24:26 +01:00
c4dba88ed6 Update doc
All checks were successful
/ test (push) Successful in 31s
/ build (push) Successful in 7s
2024-12-27 19:11:15 +01:00
6035f93868 Deployment: move doc 2024-12-27 17:53:52 +01:00
cf98b3b160 Deployment: fix typo 2024-12-27 17:53:03 +01:00
151bceb8c5 Deployment: add documenation for Kubernetes
All checks were successful
/ test (push) Successful in 31s
/ build (push) Successful in 8s
2024-12-27 06:09:14 +01:00
3a84d920c1 Deployment: add Helm chart
All checks were successful
/ test (push) Successful in 30s
2024-12-27 02:07:36 +01:00
12d57f0de9 Deployment (Kubernetes): bump version
All checks were successful
/ test (push) Successful in 30s
/ build (push) Successful in 7s
2024-12-25 19:58:07 +01:00
bd7b4724d6 Deployment (Kubernetes): use dedicated namespace, use config file 2024-12-25 19:57:04 +01:00
19 changed files with 553 additions and 110 deletions

View file

@ -1,5 +1,4 @@
.venv
.git
dist
.pytest_cache
.mypy_cache

View file

@ -30,6 +30,8 @@ jobs:
echo '${{ toJSON(env) }}'
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install app with 'uv pip install'
run: uv pip install --python=$UV_PROJECT_ENVIRONMENT --no-deps .
@ -48,40 +50,35 @@ jobs:
with:
fetch-depth: 0
- name: Get the version from git
id: version
run: echo "version=$(git describe --dirty --tags)" >> $GITHUB_OUTPUT
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v4
with:
version: "0.6.6"
- name: Check if the container should be built
id: builder
env:
RUN: ${{ toJSON(inputs.build || !contains(steps.version.outputs.version, '-g')) }}
run: |
echo "run=$RUN" >> $GITHUB_OUTPUT
echo "Run build: $RUN"
- name: Install
run: uv sync
- name: Info - version and test if the git version is clean (then python package and image container should be built)
env:
VERSION: ${{ steps.version.outputs.version }}
RUN: ${{ steps.builder.outputs.run }}
FORCE: ${{ toJSON(inputs.build) }}
run: |
echo "Version $VERSION, force (manual input): $FORCE, run the build: $RUN"
- name: Set the version in pyproject.toml (workaround for uv not supporting dynamic version)
if: fromJSON(steps.builder.outputs.run)
env:
VERSION: ${{ steps.version.outputs.version }}
run: sed "s/0.0.0/${VERSION}/" -i pyproject.toml
- name: Get version
run: echo "VERSION=$(.venv/bin/dunamai from any --style semver)" >> $GITHUB_ENV
- name: Version
run: echo $VERSION
- name: Get distance from tag
run: echo "DISTANCE=$(.venv/bin/dunamai from any --format '{distance}')" >> $GITHUB_ENV
- name: Distance
run: echo $DISTANCE
- name: Workaround for bug of podman-login
if: fromJSON(steps.builder.outputs.run)
if: env.DISTANCE == '0'
run: |
mkdir -p $HOME/.docker
echo "{ \"auths\": {} }" > $HOME/.docker/config.json
- name: Log in to the container registry (with another workaround)
if: fromJSON(steps.builder.outputs.run)
if: env.DISTANCE == '0'
uses: actions/podman-login@v1
with:
registry: ${{ vars.REGISTRY }}
@ -90,37 +87,31 @@ jobs:
auth_file_path: /tmp/auth.json
- name: Build the container image
if: fromJSON(steps.builder.outputs.run)
if: env.DISTANCE == '0'
uses: actions/buildah-build@v1
with:
image: gisaf-backend
oci: true
labels: gisaf-backend
tags: latest ${{ steps.version.outputs.version }}
tags: "latest ${{ env.VERSION }}"
containerfiles: |
./Containerfile
build-args: |
APP_VERSION=${{ steps.version.outputs.version }}
- name: Push the image to the registry
if: fromJSON(steps.builder.outputs.run)
if: env.DISTANCE == '0'
uses: actions/push-to-registry@v2
with:
registry: "docker://${{ vars.REGISTRY }}/${{ vars.ORGANISATION }}"
image: gisaf-backend
tags: latest ${{ steps.version.outputs.version }}
tags: "latest ${{ env.VERSION }}"
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "0.5.9"
- name: Build python package
if: fromJSON(steps.builder.outputs.run)
- name: Build wheel
if: env.DISTANCE == '0'
run: uv build --wheel
- name: Publish Python package (home)
if: fromJSON(steps.builder.outputs.run)
if: env.DISTANCE == '0'
env:
LOCAL_PYPI_TOKEN: ${{ secrets.LOCAL_PYPI_TOKEN }}
run: uv publish --publish-url https://code.philo.ydns.eu/api/packages/philorg/pypi --token $LOCAL_PYPI_TOKEN
continue-on-error: true

View file

@ -1,19 +1,17 @@
# Build: podman build -t code.philo.ydns.eu/philorg/gisaf-backend -f Containerfile
FROM code.philo.ydns.eu/philorg/gisaf-backend-deps
FROM docker.io/library/python:latest
ENV PYTHONPATH $UV_PROJECT_ENVIRONMENT/lib/python3.12/site-packages
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
ENV GISAF__BACKEND__PORT 8898
ENV GISAF__BACKEND__BIND__ADDR 0.0.0.0
ARG APP_VERSION=0.0.0
COPY . /src
COPY . /app
RUN uv pip install \
--python=$UV_PROJECT_ENVIRONMENT \
--no-deps \
/src
# Sync the project into a new environment, using the frozen lockfile
WORKDIR /app
RUN uv pip install --system .
RUN echo $APP_VERSION > /app/version.txt
CMD uvicorn gisaf.application:app --port ${GISAF__BACKEND__PORT} --host ${GISAF__BACKEND__BIND__ADDR}

View file

@ -1,23 +1,40 @@
# Gisaf
*Gisaf* is a web based GIS (Geographical Information System) initially developed
for the CSR Goematics unit in Auroville, India.
for the CSR Geomatics unit in Auroville, India.
Its main audiences are local administrations which need a platform
to develop and maintain a geographical, topological and environmental data,
allow teams of surveyors to upload data and update the GIS in near real time,
and collect data from a wide range of sources.
Although it just works out of the box, it is primarily intended to be customized
and extended with the data, uses, needs.
## Features
- Layers defined with Python plugins or ANSI-standard categories
- Open source industry standard interfaces (PostGIS database, OGCAPI)
- Support of different geographical projections
- Integrated administration interface
- Export of data from standard formats (Geopackage, Shapefile)
- Import and update of data managed in a well defined workflow with "baskets"
- Acquisition of different kinds of surveyors' equipments, accuracies
- Robust mechanisms of identification of the data sources, survey times
- Geographical feature statuses (eg. Existing, Deprecated, Future)
- Role-based access to data
- Support temporal and other data for different features
- Easy integration with IoT sensors, eg. using the MQTT protocol
- Detailed information for all features
- Tagging of features
- Customizable, user triggered actions
- Background map
- Customizable background maps
- Customizable dashboards
- Customizable base maps (domain specific set of layers)
- Python and Jupyter integration: easy to interact with, add features, components,
explore data and conduct scientific research
- Plugin architecture: add functions with Python packages
- Free and open source
## Software stack
@ -31,3 +48,12 @@ for the CSR Goematics unit in Auroville, India.
## Installation / deployment
See the documentation in the deployment directory.
## Configuration
Documentation is being rewritten. Please contact us.
## Demo
A very basic demo without any data, basically to give a feel of the user interface,
is available [here](https://gisaf.philo.ydns.eu).

105
deployment/README.md Normal file
View file

@ -0,0 +1,105 @@
# Gisaf deployment / installation
This documentation covers the specifics of Gisaf deployment, but
it does no cover application configuration details,
the set up of a public facing web server handling dns domain / https security.
*Gisaf* is shipped as containers for easy and effective installation,
and packages to deploy them for different environments.
There are 4 containers:
- Frontend (Angular build: static files, served by an nginx process,
eventually forwarding requests to the backend)
- Backend (Python server)
- Postgis database server
- Redis server
Commands below assume that they are run from their respective directories
(`deployment/systemd`, `deployment/kubernetes`, ...).
## Systemd
With the help of *podman*, systemd can handle starting/restarting services, etc,
even with a non-privileged user.
The 4 containers are in the same *pod*, as it is meant primarily for a lightweight
production environment, but this can be easily adapted.
The `deployment/systemd` directory contains files used for running *Gisaf* with Systemd.
Podman is used to facilitate the creation of the relevant services.
### Deployment
```sh
cp -r * $HOME/.config/containers/systemd/
systemctl --user daemon-reload
systemctl --user start gisaf-pod.service
```
Environment variables for the Gisaf configuration can be added
in the `gisaf-backend.container` file.
Note that starting on system boot requires the user to be enabled accordingly with:
```sh
sudo loginctl enable-linger <username>
```
## Plain Kubernetes (no Helm)
The `kubernetes` directory contains files for deployment on Kubernetes.
The standard installation uses a namespace named `gisaf`.
The Kubernetes configuration is split in 2 files: `gisaf.yaml` and `config.yaml`
Deployment:
```sh
kubectl create namespace gisaf
kubectl apply -f config.yaml
kubectl apply -f gisaf.yaml
```
Update after modification on the server (frontend and backend):
```sh
kubectl --namespace gisaf rollout restart deployment gisaf-server-deployment
```
## Helm
The `helm` chart is in the directory named `helm`.
### Deploy on Kubernetes
Deploying with Helm on Kubernetes makes it straightforward to
run on cloud services.
```sh
kubectl create namespace gisaf
helm install gisaf helm
```
#### Update
```sh
helm upgrade gisaf helm
```
### Publish the Helm chart
First, build the Helm package:
```sh
helm package helm
```
Then upload it:
```sh
helm --user phil:<password> -X POST \
--upload-file gisaf-0.1.0.tgz \
https://code.philo.ydns.eu/api/packages/philorg/helm/api/charts
```

1
deployment/kubernetes/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
gisaf-*.tgz

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: gisaf-config
namespace: gisaf
data:
GISAF__MAP__LNG: "6.178"
GISAF__MAP__LAT: "45.8818"
GISAF__MAP__ZOOM: "14"
GISAF__MAP__PITCH: "40"

View file

@ -2,6 +2,7 @@ apiVersion: v1
kind: Service
metadata:
name: gisaf-database
namespace: gisaf
labels:
app: gisaf-database
spec:
@ -18,6 +19,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: gisaf-database-deployment
namespace: gisaf
labels:
app: gisaf-database
spec:
@ -27,6 +29,7 @@ spec:
app: gisaf-database
template:
metadata:
namespace: gisaf
labels:
app: gisaf-database
spec:
@ -71,6 +74,7 @@ apiVersion: v1
kind: Service
metadata:
name: gisaf-server
namespace: gisaf
labels:
app: gisaf-server
spec:
@ -87,6 +91,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: gisaf-server-deployment
namespace: gisaf
labels:
app: gisaf-server
spec:
@ -96,6 +101,7 @@ spec:
app: gisaf-server
template:
metadata:
namespace: gisaf
labels:
app: gisaf-server
spec:
@ -106,7 +112,7 @@ spec:
- "gisaf-backend"
initContainers:
- name: gisaf-backend-initdb
image: code.philo.ydns.eu/philorg/gisaf-backend:0.5.0-alpha.8
image: code.philo.ydns.eu/philorg/gisaf-backend:0.5.0-alpha.10
imagePullPolicy: Always
command: ["gisaf", "create-db"]
env:
@ -114,13 +120,16 @@ spec:
value: gisaf-database
containers:
- name: gisaf-backend
image: code.philo.ydns.eu/philorg/gisaf-backend:0.5.0-alpha.8
image: code.philo.ydns.eu/philorg/gisaf-backend:0.5.0-alpha.10
imagePullPolicy: Always
env:
- name: GISAF__GISAF_LIVE__REDIS
value: redis://gisaf-database
- name: GISAF__DB__HOST
value: gisaf-database
envFrom:
- configMapRef:
name: gisaf-config
- name: gisaf-frontend
image: code.philo.ydns.eu/philorg/gisaf-frontend:0.5.0-alpha.13
imagePullPolicy: Always
@ -137,11 +146,12 @@ apiVersion: v1
kind: PersistentVolume
metadata:
name: gisaf-pgdata-pv
namespace: gisaf
labels:
type: local
app: gisaf-postgres
spec:
storageClassName: manual
storageClassName: local-path
capacity:
storage: 2Gi
accessModes:
@ -154,11 +164,12 @@ apiVersion: v1
kind: PersistentVolume
metadata:
name: gisaf-redis-pv
namespace: gisaf
labels:
type: local
app: gisaf-redis
spec:
storageClassName: manual
storageClassName: local-path
capacity:
storage: 2Gi
accessModes:
@ -171,8 +182,9 @@ apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gisaf-pgdata-pvc
namespace: gisaf
spec:
storageClassName: manual
storageClassName: local-path
accessModes:
- ReadWriteMany
resources:
@ -184,8 +196,9 @@ apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gisaf-redis-pvc
namespace: gisaf
spec:
storageClassName: manual
storageClassName: local-path
accessModes:
- ReadWriteMany
resources:
@ -197,7 +210,7 @@ apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gisaf
namespace: default
namespace: gisaf
#annotations:
# kubernetes.io/ingress.class: traefik
# #traefik.ingress.kubernetes.io/router.middlewares: default-strip-prefix@kubernetescrd

View file

@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View file

@ -0,0 +1,6 @@
apiVersion: v2
name: gisaf
description: Gisaf Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: "0.15.0-alpha"

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-config
namespace: gisaf
data:
GISAF__MAP__LNG: "{{ .Values.map.lng }}"
GISAF__MAP__LAT: "{{ .Values.map.lat }}"
GISAF__MAP__ZOOM: "{{ .Values.map.zoom }}"
GISAF__MAP__PITCH: "{{ .Values.map.pitch }}"

View file

@ -0,0 +1,135 @@
---
apiVersion: v1
kind: Service
metadata:
name: gisaf-database
namespace: gisaf
labels:
app: gisaf-database
spec:
ports:
- name: psql
port: 5432
- name: redis
port: 6379
selector:
app: gisaf-database
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gisaf-database-deployment
namespace: gisaf
labels:
app: gisaf-database
spec:
replicas: 1
selector:
matchLabels:
app: gisaf-database
template:
metadata:
namespace: gisaf
labels:
app: gisaf-database
spec:
hostAliases:
- ip: "127.0.0.1"
hostnames:
- "gisaf-redis"
- "gisaf-database"
containers:
- name: gisaf-database
image: code.philo.ydns.eu/philorg/gisaf-database:latest
imagePullPolicy: Always
args:
- postgres
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: gisaf-pgdata
ports:
- containerPort: 5432
name: psql
- image: docker.io/library/redis:alpine
imagePullPolicy: Always
name: gisaf-redis
args:
- redis-server
volumeMounts:
- mountPath: /data
name: gisaf-redis
ports:
- containerPort: 6379
name: redis
volumes:
- name: gisaf-pgdata
persistentVolumeClaim:
claimName: gisaf-pgdata-pvc
- name: gisaf-redis
persistentVolumeClaim:
claimName: gisaf-redis-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gisaf-pgdata-pvc
namespace: gisaf
spec:
storageClassName: local-path
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gisaf-redis-pvc
namespace: gisaf
spec:
storageClassName: local-path
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: gisaf-pgdata-pv
namespace: gisaf
labels:
type: local
app: gisaf-postgres
spec:
storageClassName: local-path
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
hostPath:
path: /data/gisaf/postgresql
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: gisaf-redis-pv
namespace: gisaf
labels:
type: local
app: gisaf-redis
spec:
storageClassName: local-path
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
hostPath:
path: /data/gisaf/redis

View file

@ -0,0 +1,28 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gisaf
namespace: gisaf
#annotations:
# kubernetes.io/ingress.class: traefik
# #traefik.ingress.kubernetes.io/router.middlewares: default-strip-prefix@kubernetescrd
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gisaf-server
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: gisaf-server
port:
number: 8081

View file

@ -0,0 +1,69 @@
---
apiVersion: v1
kind: Service
metadata:
name: gisaf-server
namespace: gisaf
labels:
app: gisaf-server
spec:
ports:
- port: 80
targetPort: 80
selector:
app: gisaf-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gisaf-server-deployment
namespace: gisaf
labels:
app: gisaf-server
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: gisaf-server
template:
metadata:
namespace: gisaf
labels:
app: gisaf-server
spec:
hostAliases:
- ip: "127.0.0.1"
hostnames:
- "gisaf-frontend"
- "gisaf-backend"
initContainers:
- name: gisaf-backend-initdb
image: "code.philo.ydns.eu/philorg/gisaf-backend:{{ .Values.version.backend }}"
imagePullPolicy: Always
command: ["gisaf", "create-db"]
env:
- name: GISAF__DB__HOST
value: gisaf-database
containers:
- name: gisaf-backend
image: "code.philo.ydns.eu/philorg/gisaf-backend:{{ .Values.version.backend }}"
imagePullPolicy: Always
env:
- name: GISAF__GISAF_LIVE__REDIS
value: redis://gisaf-database
- name: GISAF__DB__HOST
value: gisaf-database
envFrom:
- configMapRef:
name: gisaf-config
- name: gisaf-frontend
image: "code.philo.ydns.eu/philorg/gisaf-frontend:{{ .Values.version.frontend }}"
imagePullPolicy: Always
args:
- nginx
- -g
- daemon off;
ports:
- containerPort: 80

View file

@ -0,0 +1,13 @@
# Replicaset count for the server (2 pods: frontend and backend)
replicaCount: 2
version:
backend: 0.5.0-alpha.10
frontend: 0.5.0-alpha.13
# Map config
map:
lng: "6.178"
lat: "45.8818"
zoom: "14"
pitch: "40"

View file

@ -1,7 +1,7 @@
[project]
name = "gisaf-backend"
version = "0.0.0"
description = "Gisaf backend"
dynamic = ["version"]
authors = [{ name = "phil", email = "phil.dev@philome.mooo.com" }]
dependencies = [
"aiopath>=0.7.1",
@ -40,20 +40,9 @@ readme = "README.md"
[project.scripts]
gisaf = "gisaf.cli:cli"
[project.optional-dependencies]
contextily = ["contextily>=1.4.0"]
mqtt = ["aiomqtt>=1.2.1"]
all = ["gisaf-backend[contextily]", "gisaf-backend[mqtt]"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/gisaf"]
[tool.uv]
dev-dependencies = [
[dependency-groups]
dev = [
"dunamai>=1.23.0",
"ipdb>=0.13.13",
"pandas-stubs>=2.1.4.231218",
"pretty-errors>=1.2.25",
@ -64,3 +53,27 @@ dev-dependencies = [
"types-passlib>=1.7.7.20240311",
"pytest>=8.3.4",
]
[project.optional-dependencies]
contextily = ["contextily>=1.4.0"]
mqtt = ["aiomqtt>=1.2.1"]
all = ["gisaf-backend[contextily]", "gisaf-backend[mqtt]"]
[build-system]
requires = ["hatchling", "uv-dynamic-versioning"]
build-backend = "hatchling.build"
[tool.hatch.version]
source = "uv-dynamic-versioning"
[tool.hatch.build.targets.wheel]
packages = ["src/gisaf"]
[tool.uv-dynamic-versioning]
style = "semver"
[tool.uv]
package = true
[tool.black]
line-length = 98

View file

@ -0,0 +1,11 @@
import importlib.metadata
try:
from dunamai import Version, Style
__version__ = Version.from_git().serialize(style=Style.SemVer, dirty=True)
except ImportError:
# __name__ could be used if the package name is the same
# as the directory - not the case here
# __version__ = importlib.metadata.version(__name__)
__version__ = importlib.metadata.version("gisaf-backend")

View file

@ -14,25 +14,7 @@ from pydantic_settings import (
YamlConfigSettingsSource,
)
def get_version():
version = importlib_version("gisaf-backend")
if version == "0.0.0":
from subprocess import run
try:
v_git_describe = run(
["git", "describe", "--always", "--tags", "--dirty"],
capture_output=True,
text=True,
cwd=Path(__file__).parent.parent,
)
v_git_describe.check_returncode()
version = v_git_describe.stdout.strip()
except Exception:
logger.warn("Version not set in Python package and cannot run git describe")
version = "?"
return version
from gisaf import __version__
logger = logging.getLogger(__name__)
@ -48,12 +30,8 @@ config_files = [
class DashboardHome(BaseModel):
title: str = "Gisaf - home/dashboards"
content_file: Path = (
Path(Path.cwd().root) / "etc" / "gisaf" / "dashboard_home_content.html"
)
footer_file: Path = (
Path(Path.cwd().root) / "etc" / "gisaf" / "dashboard_home_footer.html"
)
content_file: Path = Path(Path.cwd().root) / "etc" / "gisaf" / "dashboard_home_content.html"
footer_file: Path = Path(Path.cwd().root) / "etc" / "gisaf" / "dashboard_home_footer.html"
class GisafConfig(BaseModel):
@ -141,7 +119,9 @@ class DB(BaseModel):
echo: bool = False
def get_sqla_url(self):
return f"postgresql+asyncpg://{self.user}:{self.password}@{self.host}:{self.port}/{self.db}"
return (
f"postgresql+asyncpg://{self.user}:{self.password}@{self.host}:{self.port}/{self.db}"
)
def get_pg_url(self):
return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.db}"
@ -223,9 +203,7 @@ class OGCAPI(BaseModel):
class TileServer(BaseModel):
baseDir: Path = Path(BaseDirectory.xdg_data_home) / "gisaf" / "mbtiles_files_dir"
useRequestUrl: bool = False
spriteBaseDir: Path = (
Path(BaseDirectory.xdg_data_home) / "gisaf" / "mbtiles_sprites_dir"
)
spriteBaseDir: Path = Path(BaseDirectory.xdg_data_home) / "gisaf" / "mbtiles_sprites_dir"
spriteUrl: str = "/tiles/sprite/sprite"
spriteBaseUrl: str = "https://gisaf.example.org"
openMapTilesKey: str | None = None
@ -332,9 +310,7 @@ class Config(BaseSettings):
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
configs = [
YamlConfigSettingsSource(settings_cls, yaml_file=cf) for cf in config_files
]
configs = [YamlConfigSettingsSource(settings_cls, yaml_file=cf) for cf in config_files]
return (
env_settings,
init_settings,
@ -370,7 +346,7 @@ class Config(BaseSettings):
plot: Plot = Plot()
plugins: dict[str, dict[str, Any]] = {}
survey: Survey = Survey()
version: str = get_version()
version: str = __version__
weather_station: dict[str, dict[str, Any]] = {}
widgets: Widgets = Widgets()

26
uv.lock generated
View file

@ -1,4 +1,5 @@
version = 1
revision = 1
requires-python = ">=3.12"
[[package]]
@ -276,7 +277,7 @@ name = "click"
version = "8.1.7"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "platform_system == 'Windows'" },
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 }
wheels = [
@ -425,6 +426,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 },
]
[[package]]
name = "dunamai"
version = "1.23.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/4e/a5c8c337a1d9ac0384298ade02d322741fb5998041a5ea74d1cd2a4a1d47/dunamai-1.23.0.tar.gz", hash = "sha256:a163746de7ea5acb6dacdab3a6ad621ebc612ed1e528aaa8beedb8887fccd2c4", size = 44681 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/21/4c/963169386309fec4f96fd61210ac0a0666887d0fb0a50205395674d20b71/dunamai-1.23.0-py3-none-any.whl", hash = "sha256:a0906d876e92441793c6a423e16a4802752e723e9c9a5aabdc5535df02dbe041", size = 26342 },
]
[[package]]
name = "ecdsa"
version = "0.19.0"
@ -538,7 +551,6 @@ wheels = [
[[package]]
name = "gisaf-backend"
version = "0.0.0"
source = { editable = "." }
dependencies = [
{ name = "aiopath" },
@ -587,6 +599,7 @@ mqtt = [
[package.dev-dependencies]
dev = [
{ name = "asyncpg-stubs" },
{ name = "dunamai" },
{ name = "ipdb" },
{ name = "pandas-stubs" },
{ name = "pretty-errors" },
@ -599,17 +612,17 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "aiomqtt", marker = "extra == 'all'", specifier = ">=1.2.1" },
{ name = "aiomqtt", marker = "extra == 'mqtt'", specifier = ">=1.2.1" },
{ name = "aiopath", specifier = ">=0.7.1" },
{ name = "aiosqlite", specifier = ">=0.19.0" },
{ name = "apscheduler", specifier = ">=3.10.4" },
{ name = "asyncpg", specifier = ">=0.28.0" },
{ name = "contextily", marker = "extra == 'all'", specifier = ">=1.4.0" },
{ name = "contextily", marker = "extra == 'contextily'", specifier = ">=1.4.0" },
{ name = "fastapi", specifier = ">=0.111" },
{ name = "geoalchemy2", specifier = ">=0.14.2" },
{ name = "geopandas", specifier = ">=1.0.1" },
{ name = "gisaf-backend", extras = ["contextily"], marker = "extra == 'all'" },
{ name = "gisaf-backend", extras = ["mqtt"], marker = "extra == 'all'" },
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "itsdangerous", specifier = ">=2.1.2" },
{ name = "matplotlib", specifier = ">=3.8.3" },
@ -633,10 +646,12 @@ requires-dist = [
{ name = "uvicorn", specifier = ">=0.23.2" },
{ name = "websockets", specifier = ">=12.0" },
]
provides-extras = ["all", "contextily", "mqtt"]
[package.metadata.requires-dev]
dev = [
{ name = "asyncpg-stubs", specifier = ">=0.29.1" },
{ name = "dunamai", specifier = ">=1.23.0" },
{ name = "ipdb", specifier = ">=0.13.13" },
{ name = "pandas-stubs", specifier = ">=2.1.4.231218" },
{ name = "pretty-errors", specifier = ">=1.2.25" },
@ -1207,6 +1222,7 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 },
{ url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 },
{ url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 },
{ url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224 },
]
[[package]]
@ -1786,7 +1802,7 @@ name = "tzlocal"
version = "5.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "tzdata", marker = "platform_system == 'Windows'" },
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/04/d3/c19d65ae67636fe63953b20c2e4a8ced4497ea232c43ff8d01db16de8dc0/tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e", size = 30201 }
wheels = [