diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..4e30e921 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,51 @@ +name: 🚧 Website MkDocs +on: + pull_request: + branches: + - main + paths: + - 'docs/**' + - '.github/workflows/docs.yml' + push: + branches: + - main + paths: + - 'docs/**' + - '.github/workflows/docs.yml' + workflow_dispatch: # useful for testing tx pushes + workflow_call: # call from release + +defaults: + run: + working-directory: docs + +jobs: + website: + runs-on: ubuntu-latest + # Skip if this job was scheduled and the runner belongs to a fork (i.e. forks have no use for this action) + if: (github.event_name == 'schedule' && github.repository == 'opengisch/django-oapif') || (github.event_name != 'schedule') + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install Python requirements + run: | + pip install -r requirements.txt + pip install -r requirements-dev.txt + + - name: Build documentation + run: mkdocs build + + - uses: actions/upload-artifact@v4 + if: ${{ github.event_name == 'pull_request' }} + with: + name: docs + path: docs/site + if-no-files-found: error + + - name: Deploy to GitHub Pages + if: contains(fromJSON('["push", "workflow_dispatch"]'), github.event_name) + run: mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index 7b3b9fda..42c567b7 100644 --- a/.gitignore +++ b/.gitignore @@ -11,9 +11,10 @@ dist *.egg-info django_oapif/core/__version__.py +docs/site + tests/benchmark/output tests/output - tests/django_oapif_tests/tests/fixtures/polygon_2056.json.gz tests/django_oapif_tests/tests/fixtures/polygon_2056_local_geom.json.gz diff --git a/README.md b/README.md index 833cb4a3..e761ba1f 100644 --- a/README.md +++ b/README.md @@ -1,108 +1,6 @@ -# django-oapif +# Django-OAPIF -**WARNING** This is in under development. API will break. Do not use in production. +*Django-OAPIF* allows to easily expose your Django models through an OGC API Features endpoint. +It is based on Django REST Framework. -*django-oapif* allows to easily expose your Django models through an OAPI-Features endpoint. It is based on Django REST Framework. - -## Table of contents -- [Quickstart](#quickstart) -- [Use from QGIS](#use-from-qgis) -- [Install it as a Django app](#install-it-as-a-django-app) -- [Authentication & permissions](#custom-authentication--permissions) -- [Tests](#tests) -- [OGC conformance](#ogc-conformance) - -## Quickstart - -This lets you run the Compose application locally and demo it: - -```bash -# copy default conf -cp .env.example .env - -# start the stack -docker compose up --build -d - -# deploy static files and migrate database -docker compose exec django python manage.py collectstatic --no-input -docker compose exec django python manage.py migrate --no-input - -# A convenience start-up Django command is there -# to populate the database with testdata -docker compose exec django python manage.py populate_users -docker compose exec django python manage.py populate_data -``` -After waiting little you'll be able to access all collections at http://0.0.0.0:7180/oapif/collections. - -Three users are provided out of the box; they can be logged in with through basic authentication; all `123` for password: -- `demo_viewer` -- `demo_editor` -- `admin` - -As expected `admin` can access Django Admin at http://0.0.0.0:7180/admin. - -## Use from QGIS - -When up and running you can access the REST API from QGIS like this: - -- Go to `Layers` > `Add layer` > `Add WFS Layer...` -- Create a new connection - - URL: `https://0.0.0.0:7180/oapif/` - - Version: `OGC API - Features` -- Click OK and ignore choose to ignore the invalid certificate error and store the exception -- You should see the two layers in the list, select them and choose `add`. - -## Install it as a Django app - -This project is [hosted on PyPI](https://pypi.org/project/django-oapif/). You can install it as a Django app: - -```bash -# Install with your favorite package manager -pip3 install --user https://github.com/opengisch/django-oapif -# Edit your Django project's settings.py accordingly: -settings.py ------------ -INSTALLED_APPS = [ - ... - django_oapif -] -``` -## Custom authentication & permissions - -By default the viewsets under `tests` use the `DjangoModelPermissionsOrAnonReadOnly` permissions class. You can add model permissions when registering their corresponding viewsets, as `permission_classes` [^1]. Example: - -```python -models.py ---------- -from rest_framework import permissions -from django.contrib.gis.db import models -from django_oapif import register_oapif_viewset - - -@register_oapif_viewset( - custom_viewset_attrs={ - "permission_classes": (permissions.DjangoModelPermissionsOrAnonReadOnly,) - } -) -class MyModel(models.Model): - ... -``` - -[^1]: Refer to https://www.django-rest-framework.org/api-guide/permissions/#api-reference for permission classes. - -## Tests - -To run all tests, launch the Compose application as shown in the [Quickstart](#quickstart). Then run - - docker compose exec django python manage.py test - - -## OGC Conformance - -You can run the OGC API conformance test suite like this: - -``` -docker compose run conformance_test -``` - -Results will be stored to `tests/output/emailable-report.html +https://opengisch.github.io/django-oapif diff --git a/django_oapif/README.md b/django_oapif/README.md index a7b3d40c..3ce2ed0f 100644 --- a/django_oapif/README.md +++ b/django_oapif/README.md @@ -1,79 +1,4 @@ -# Django OAPIF +# Django-OAPIF -This Django app implements an OGC Services API Features (a.k.a. OAPIF) for Django. - -It provides a Django Rest Framework (DRF) specific router to which you can -register your Viewsets, and thus benefit from all DRF's features (permissions, -serialization, authentication, etc.). - -## Quickstart - -1. Download Django-OAPIF: `pip install https://github.com/opengisch/django-oapif.git` - -2. In `settings.py` make sure that rest_framework is installed: - -```python -INSTALLED_APPS = [ - ..., - "django_oapif", - "rest_framework", - "rest_framework_gis", - ..., -] - -``` - -Add this to your `urls.py` : - -urlpatterns += [ - ..., - path("oapif/", include(django_oapif.urls)), - ..., -] - -3. Register your models with the decorator: - -```python -# models.py - -from django.contrib.gis.db import models -from django_oapif import register - - -@register_oapif_viewset() -class TestingDecorator(models.Model): - name = models.CharField(max_length=10) - geom = models.PointField(srid=2056) -``` - -4. Configure global settings - -Optionally specify your endpoint's metadata in `settings.py`: - -```python -# settings.py -... - -OAPIF_TITLE = "My Endpoint" -OAPIF_DESCRIPTION = "Description" -``` - -Voilà ! Your OAPIF endpoint should be ready to use. - -## Permissions - -Permissions are defined by default using Django model permissions. - -## Advanced use cases - -If you need more control over the serialization or the viewset, refer to the decorator's code and to DRF's viewset documentation. - -## Roadmap / status - -This is probably still relatively far from a full OGC Services API implementation and currently only aims to support read-only view from QGIS. - -This app will at some point be factored out into a reusable Django library. - -## Releases - -Releases are made automatically from the CI whenever a tag in format `v*` is pushed. Please use semantic versionning for tagging releases. +*Django-OAPIF* allows to easily expose your Django models through an OGC API Features endpoint. +It is based on Django REST Framework. diff --git a/docs/content/demo.md b/docs/content/demo.md new file mode 100644 index 00000000..36e80988 --- /dev/null +++ b/docs/content/demo.md @@ -0,0 +1,46 @@ +--- +hide: + - navigation +--- + +# Try the demo + +## Setup + +This lets you run the compose application locally and demo it: + +```bash +# copy default conf +cp .env.example .env + +# start the stack +docker compose up --build -d + +# deploy static files and migrate database +docker compose exec django python manage.py collectstatic --no-input +docker compose exec django python manage.py migrate --no-input + +# A convenience start-up Django command is there +# to populate the database with testdata +docker compose exec django python manage.py populate_users +docker compose exec django python manage.py populate_data +``` +After waiting little you'll be able to access all collections at http://0.0.0.0:7180/oapif/collections. + +Three users are provided out of the box; they can be logged in with through basic authentication; all `123` for password: +- `demo_viewer` +- `demo_editor` +- `admin` + +As expected `admin` can access Django Admin at http://0.0.0.0:7180/admin. + +## Use from QGIS + +When up and running you can access the REST API from QGIS like this: + +- Go to `Layers` > `Add layer` > `Add WFS Layer...` +- Create a new connection + - URL: `https://0.0.0.0:7180/oapif/` + - Version: `OGC API - Features` +- Click OK and ignore choose to ignore the invalid certificate error and store the exception +- You should see the two layers in the list, select them and choose `add`. diff --git a/docs/content/index.md b/docs/content/index.md new file mode 100644 index 00000000..979c2495 --- /dev/null +++ b/docs/content/index.md @@ -0,0 +1,9 @@ +--- +hide: + - navigation +--- + +# Django-OAPIF + +*Django-OAPIF* allows to easily expose your Django models through an OGC API Features endpoint. +It is based on Django REST Framework. diff --git a/docs/content/permissions.md b/docs/content/permissions.md new file mode 100644 index 00000000..f12c7d15 --- /dev/null +++ b/docs/content/permissions.md @@ -0,0 +1,27 @@ +--- +hide: + - navigation +--- + +# Custom authentication & permissions + +By default the viewsets use `DjangoModelPermissionsOrAnonReadOnly` [permissions class from DRF](https://www.django-rest-framework.org/api-guide/permissions/#djangomodelpermissionsoranonreadonly). + +This can be altered in the DRF settings by adapting `DEFAULT_PERMISSION_CLASSES`. + +You can also add custom permissions when registering their corresponding viewsets, as [`permission_classes`](https://www.django-rest-framework.org/api-guide/permissions/#api-reference). +Example in `models.py`: + +```python +from rest_framework import permissions +from django.contrib.gis.db import models +from django_oapif import register_oapif_viewset + +@register_oapif_viewset( + custom_viewset_attrs={ + "permission_classes": (permissions.DjangoModelPermissionsOrAnonReadOnly,) + } +) +class MyModel(models.Model): + ... +``` diff --git a/docs/content/quick-start.md b/docs/content/quick-start.md new file mode 100644 index 00000000..91ff93e8 --- /dev/null +++ b/docs/content/quick-start.md @@ -0,0 +1,64 @@ +--- +hide: + - navigation +--- + +# Quickstart + +## Installation + +Install with your favorite package manager + +```bash +pip install --user https://github.com/opengisch/django-oapif +``` + +## Enable the app + +Edit settings.py + +``` +INSTALLED_APPS = [ + ... + "django_oapif", + "rest_framework", + "rest_framework_gis", +] +``` + +Add this to your `urls.py` : + +urlpatterns += [ + ..., + path("oapif/", include(django_oapif.urls)), + ..., +] + +## Register your models with the decorator: + +```python +# models.py + +from django.contrib.gis.db import models +from django_oapif import register + + +@register_oapif_viewset() +class TestingDecorator(models.Model): + name = models.CharField(max_length=10) + geom = models.PointField(srid=2056) +``` + +## Configure global settings + +Optionally specify your endpoint's metadata in `settings.py`: + +```python +# settings.py +... + +OAPIF_TITLE = "My Endpoint" +OAPIF_DESCRIPTION = "Description" +``` + +Voilà ! Your OAPIF endpoint should be ready to use. diff --git a/docs/content/tests.md b/docs/content/tests.md new file mode 100644 index 00000000..4c678351 --- /dev/null +++ b/docs/content/tests.md @@ -0,0 +1,28 @@ +--- +hide: + - navigation +--- + +## Run tests + + # unit tests + docker compose exec django python manage.py test + + # integration tests + docker compose --profile testing_integration up -d + docker compose run integration_tests + + +## OGC Conformance + +You can run the OGC API conformance test suite like this: + +``` +docker compose --profile testing_conformance up --build -d +docker compose exec django python manage.py migrate --no-input +docker compose exec django python manage.py populate_users +docker compose exec django python manage.py populate_data +docker compose run conformance_test +``` + +Results will be stored to `tests/output/emailable-report.html diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 00000000..8b25bcce --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,73 @@ +site_name: Django-OAPIF +site_url: +site_author: OPENGIS.ch + +docs_dir: content + +# Repository +repo_name: opengisch/django-oapif +repo_url: https://github.com/opengisch/django-oapif +edit_uri: https://github.com/opengisch/django-oapif/tree/main/docs/content/ + +# Copyright +copyright: Copyright © 2023-2024 OPENGIS.ch + +# Configuration +theme: + name: material + language: en + features: + - navigation.tabs + - navigation.tabs.sticky + - navigation.sections + - navigation.tracking + - search.suggest + - search.highlight + #logo: assets/images/oapif_logo.png + #favicon: assets/images/favicon.ico + palette: + # Palette toggle for automatic mode + - media: "(prefers-color-scheme)" + primary: grey + toggle: + icon: material/brightness-auto + name: Light mode + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: grey + toggle: + icon: material/brightness-7 + name: Dark mode + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: grey + toggle: + icon: material/brightness-4 + name: System default + +nav: + - Home: index.md + - Quick start: quick-start.md + - Demo: demo.md + - Permissions: permissions.md + - Test and conformance: tests.md + +extra: + # Remove "Made with Material for MkDocs" from footer + generator: false + +markdown_extensions: + - attr_list + - md_in_html + - fancyboxmd + - sane_lists + - admonition + - pymdownx.details + - pymdownx.tabbed: + alternate_style: true + - pymdownx.superfences + +plugins: + - search diff --git a/docs/requirements-dev.txt b/docs/requirements-dev.txt new file mode 100644 index 00000000..3f3e6876 --- /dev/null +++ b/docs/requirements-dev.txt @@ -0,0 +1,3 @@ +ruamel.yaml==0.18.6 +pre-commit==3.6.2 +python-frontmatter==1.1.0 diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..49a1551c --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +mkdocs-material==9.5.9 +mkdocs-video==1.5.0 +fancyboxmd==1.1.0