diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml deleted file mode 100644 index 63f256a..0000000 --- a/.forgejo/workflows/build.yaml +++ /dev/null @@ -1,117 +0,0 @@ -on: - push: - tags: - - "**" - workflow_dispatch: - inputs: - verbose: - description: "Verbose" - required: false - default: false - type: boolean - -jobs: - test: - runs-on: container - container: - image: code.philo.ydns.eu/philorg/gisaf-backend-ci - volumes: - - "uv_cache:/root/.cache/uv" - - "ca-cert:/etc/containers/certs.d" - services: - gisaf-database: - image: code.philo.ydns.eu/philorg/gisaf-database - redis: - image: docker.io/redis:alpine - steps: - - name: Echo env - if: ${{ inputs.verbose }} - run: | - 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 . - - - name: Initialize database - run: GISAF__DB__HOST=gisaf-database gisaf create-db - - - name: Run tests (API call) - run: GISAF__DB__HOST=gisaf-database GISAF__GISAF_LIVE__REDIS=redis://redis pytest -s tests/basic.py -W ignore::DeprecationWarning - - build: - runs-on: container - needs: test - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install the latest version of uv - uses: astral-sh/setup-uv@v4 - with: - version: "0.6.6" - - - name: Install - run: uv sync - - - - 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: 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: env.DISTANCE == '0' - uses: actions/podman-login@v1 - with: - registry: ${{ vars.REGISTRY }} - username: ${{ secrets.REGISTRY_USER }} - password: ${{ secrets.REGISTRY_PASSWORD }} - auth_file_path: /tmp/auth.json - - - name: Build the container image - if: env.DISTANCE == '0' - uses: actions/buildah-build@v1 - with: - image: gisaf-backend - oci: true - labels: gisaf-backend - tags: "latest ${{ env.VERSION }}" - containerfiles: | - ./Containerfile - - - name: Push the image to the registry - if: env.DISTANCE == '0' - uses: actions/push-to-registry@v2 - with: - registry: "docker://${{ vars.REGISTRY }}/${{ vars.ORGANISATION }}" - image: gisaf-backend - tags: "latest ${{ env.VERSION }}" - - - name: Build wheel - if: env.DISTANCE == '0' - run: uv build --wheel - - - name: Publish Python package (home) - 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 diff --git a/.forgejo/workflows/test.yaml b/.forgejo/workflows/test.yaml deleted file mode 100644 index c72d872..0000000 --- a/.forgejo/workflows/test.yaml +++ /dev/null @@ -1,39 +0,0 @@ -on: - push: - workflow_dispatch: - inputs: - verbose: - description: "Verbose" - required: false - default: false - type: boolean - -jobs: - test: - runs-on: container - container: - image: code.philo.ydns.eu/philorg/gisaf-backend-ci - volumes: - - "uv_cache:/root/.cache/uv" - - "ca-cert:/etc/containers/certs.d" - services: - gisaf-database: - image: code.philo.ydns.eu/philorg/gisaf-database - redis: - image: docker.io/redis:alpine - steps: - - name: Echo env - if: ${{ inputs.verbose }} - run: | - echo '${{ toJSON(env) }}' - - - uses: actions/checkout@v4 - - - name: Install app with 'uv pip install' - run: uv pip install --python=$UV_PROJECT_ENVIRONMENT --no-deps . - - - name: Initialize database - run: GISAF__DB__HOST=gisaf-database gisaf create-db - - - name: Run tests (API call) - run: GISAF__DB__HOST=gisaf-database GISAF__GISAF_LIVE__REDIS=redis://redis pytest -s tests/basic.py -W ignore::DeprecationWarning diff --git a/.python-version b/.python-version deleted file mode 100644 index e4fba21..0000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.12 diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml index 5d32a38..495cbec 100644 --- a/.woodpecker/test.yaml +++ b/.woodpecker/test.yaml @@ -20,11 +20,14 @@ steps: image: code.philo.ydns.eu/philorg/uv-geo environment: GISAF__DB__HOST: gisaf-database + GISAF__GISAF_LIVE__REDIS: redis://redis commands: # Initialize database - .venv/bin/gisaf create-db - .venv/bin/pytest -s tests/basic.py services: + redis: + image: docker.io/redis:alpine gisaf-database: - image: code.philo.ydns.eu/philorg/treetrail-database + image: code.philo.ydns.eu/philorg/gisaf-database diff --git a/README.md b/README.md index 133a663..4e1b1be 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ and extended with the data, uses, needs. - 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 + explore data and conduct scientific research - Plugin architecture: add functions with Python packages - Free and open source @@ -57,3 +57,5 @@ Documentation is being rewritten. Please contact us. A very basic demo without any data, basically to give a feel of the user interface, is available [here](https://gisaf.philo.ydns.eu). + +[![status-badge](https://code.philo.ydns.eu/woodpecker/api/badges/17/status.svg)](https://code.philo.ydns.eu/woodpecker/repos/17) diff --git a/src/gisaf/__init__.py b/src/gisaf/__init__.py index 710f782..0028fe7 100644 --- a/src/gisaf/__init__.py +++ b/src/gisaf/__init__.py @@ -4,7 +4,7 @@ try: from dunamai import Version, Style __version__ = Version.from_git().serialize(style=Style.SemVer, dirty=True) -except ImportError: +except (ImportError, RuntimeError): # __name__ could be used if the package name is the same # as the directory - not the case here # __version__ = importlib.metadata.version(__name__) diff --git a/src/gisaf/cli.py b/src/gisaf/cli.py index abb26cf..55a5752 100644 --- a/src/gisaf/cli.py +++ b/src/gisaf/cli.py @@ -1,6 +1,4 @@ #!/usr/bin/env python -from importlib.metadata import version as importlib_version -from sqlalchemy.engine import create from typing_extensions import Annotated import typer @@ -11,7 +9,6 @@ cli = typer.Typer(no_args_is_help=True, help="Gisaf GIS backend") @cli.command() def create_db(): """Populate the database with a functional empty structure""" - from gisaf.application import app from gisaf.database import create_db from asyncio import run @@ -36,15 +33,15 @@ def serve(host: str = "localhost", port: int = 8000): def version_callback(show_version: bool): if show_version: - print(importlib_version("gisaf-backend")) + from gisaf import __version__ + + typer.echo(__version__) raise typer.Exit() @cli.callback() def main( - version: Annotated[ - bool | None, typer.Option("--version", callback=version_callback) - ] = None + version: Annotated[bool | None, typer.Option("--version", callback=version_callback)] = None, ): pass diff --git a/src/gisaf/database.py b/src/gisaf/database.py index 0717c92..f1e8d63 100644 --- a/src/gisaf/database.py +++ b/src/gisaf/database.py @@ -166,9 +166,24 @@ async def create_db(drop=False): async with engine.begin() as conn: await create_schemas(conn) async with engine.begin() as conn: + # Import modules to populate the SQLModel metadata + from gisaf.models import admin + from gisaf.models import authentication + from gisaf.models import category + from gisaf.models import dashboard + from gisaf.models import geo_models_base + from gisaf.models import info + from gisaf.models import misc + from gisaf.models import project + from gisaf.models import raw_survey + from gisaf.models import reconcile + from gisaf.models import survey + from gisaf.models import tags + if drop: await conn.run_sync(SQLModel.metadata.drop_all) - await conn.run_sync(SQLModel.metadata.create_all) + resp = await conn.run_sync(SQLModel.metadata.create_all) + pass logger.debug(f"Connect to database with config: {conf.db}") while attempts > 0: @@ -186,9 +201,7 @@ async def create_db(drop=False): await populate_init_db() return else: - logger.warning( - f"Cannot connect to database after {CREATE_DB_TIMEOUT}, giving up." - ) + logger.warning(f"Cannot connect to database after {CREATE_DB_TIMEOUT}, giving up.") exit(1) @@ -196,6 +209,15 @@ async def is_fresh_install() -> bool: """Detect is the database is newly created, without data""" from gisaf.models.authentication import User + async with engine.begin() as conn: + has_table_query = """ + SELECT count(tablename) + FROM pg_catalog.pg_tables + WHERE schemaname = 'gisaf_admin' and tablename='user'""" + resp = await conn.execute(text(has_table_query)) + if resp.scalar() == 0: + return True + async with db_session() as session: nb_users = (await session.exec(select(func.count(col(User.username))))).one() return nb_users == 0 @@ -222,9 +244,7 @@ async def populate_init_db(): from gisaf.models.map_bases import BaseStyle logger.info("Populating initial database") - async with db_session() as session: - user = await create_user( session=session, username="admin",