Fixes and improvements

This commit is contained in:
Matthew Northcott 2023-02-08 14:24:48 +13:00
parent 436075f11c
commit c1796c22a6
12 changed files with 199 additions and 67 deletions

60
Makefile Normal file
View file

@ -0,0 +1,60 @@
#!/usr/bin/env make
SHELL = /bin/bash
UID := $(shell id -u)
GID := $(shell id -g)
export UID
export GID
frontend/node_modules:
docker run --rm -v ${PWD}/frontend:/app -w /app -u ${UID}:${GID} node:16-bullseye npm i
backend/right_tree/staticfiles:
docker run --rm -v ${PWD}/backend:/app -w /app -u ${UID}:${GID} right-tree python manage.py collectstatic --noinput
ingest:
docker-compose up -d backend postgres
docker-compose exec backend python manage.py loaddata \
/app/right_tree/api/data/fixtures/001_eco_regions.json \
/app/right_tree/api/data/fixtures/002_tolerance_levels.json \
/app/right_tree/api/data/fixtures/003_soil_variants.json \
/app/right_tree/api/data/fixtures/004_soil_order_mappings.json \
/app/right_tree/api/data/fixtures/005_habitats.json \
/app/right_tree/api/data/fixtures/006_zones.json \
/app/right_tree/api/data/fixtures/007_habitat_images.json
docker-compose exec backend python manage.py loadshapefiles
docker-compose exec backend python manage.py createplantfixtures
docker-compose exec backend python manage.py loaddata \
/app/right_tree/api/data/fixtures/plants.json
createsuperuser:
docker-compose up -d backend
docker-compose exec backend python manage.py createsuperuser
shell:
docker-compose up -d backend
docker-compose exec backend python manage.py shell
psql:
docker-compose up -d postgres
docker-compose exec postgres psql -U righttree -d righttree
build:
docker build --no-cache -t right-tree backend
start: frontend/node_modules backend/right_tree/staticfiles
docker-compose up -d
docker-compose logs -f
logs:
docker-compose logs -f
stop:
docker-compose down
clean: stop
git clean -dxf
reset: clean
docker-compose down --volumes --remove-orphans

3
backend/.dockerignore Normal file
View file

@ -0,0 +1,3 @@
__pycache__/
staticfiles/
*.pyc

13
backend/.gitignore vendored
View file

@ -2,7 +2,18 @@
*.sqlite3 *.sqlite3
__pycache__ __pycache__
resources right_tree/api/data/resources/*.cpg
right_tree/api/data/resources/*.dbf
right_tree/api/data/resources/*.pdf
right_tree/api/data/resources/*.prj
right_tree/api/data/resources/*.sbn
right_tree/api/data/resources/*.sbx
right_tree/api/data/resources/*.shp
right_tree/api/data/resources/*.shx
right_tree/api/data/resources/*.txt
right_tree/api/data/resources/*.xml
right_tree/api/data/resources/*.zip
right_tree/api/data/fixtures/plants.json right_tree/api/data/fixtures/plants.json
right_tree/staticfiles right_tree/staticfiles
right_tree/api/data/generated_resources/* right_tree/api/data/generated_resources/*

View file

@ -1,17 +1,11 @@
FROM python:3.8-slim-bullseye FROM python:3.11-slim-bullseye
WORKDIR /app WORKDIR /app
RUN apt update && \ RUN apt update \
apt install -y --no-install-recommends \ && apt install -y --no-install-recommends gdal-bin \
gdal-bin \ && rm -rf /var/lib/apt/lists/* \
libxml2 libxml2-dev gettext \ && apt clean
libxslt1-dev libjpeg-dev libpng-dev libpq-dev libgdal-dev \
software-properties-common g++ \
zlib1g-dev libgeos-dev libproj-dev \
sqlite3 spatialite-bin libsqlite3-mod-spatialite \
wkhtmltopdf && \
apt clean
COPY ./requirements.txt /app/requirements.txt COPY ./requirements.txt /app/requirements.txt

View file

@ -1,10 +1,10 @@
Django==3.2.8 Django==3.2.17
psycopg2-binary>=2.8 psycopg2-binary>=2.9.5
djangorestframework==3.12.4 djangorestframework==3.14.0
django-cors-headers==3.10.0 django-cors-headers==3.13.0
openpyxl==3.0.9 openpyxl==3.1.0
requests==2.26.0 requests==2.28.2
gunicorn==20.1.0 gunicorn==20.1.0
pandas==1.3.4 pandas==1.5.3
pdfkit==1.0.0 pdfkit==1.0.0
PyPDF2==1.26.0 PyPDF2==1.28.6

View file

@ -3,7 +3,7 @@
"model": "api.ecologicalregion", "model": "api.ecologicalregion",
"pk": 1, "pk": 1,
"fields": { "fields": {
"name": "Aorrangi" "name": "Aorangi"
} }
}, },
{ {
@ -549,7 +549,7 @@
"model": "api.ecologicalregion", "model": "api.ecologicalregion",
"pk": 79, "pk": 79,
"fields": { "fields": {
"name": "Whatkatane" "name": "Whakatane"
} }
} }
] ]

Binary file not shown.

View file

@ -1,7 +1,9 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.contrib.gis.utils import LayerMapping from django.contrib.gis.utils import LayerMapping
from glob import iglob
from pathlib import Path from pathlib import Path
from zipfile import ZipFile, is_zipfile
import right_tree.api.data import right_tree.api.data
from right_tree.api.models import SoilLayer, EcologicalDistrictLayer, ChristchurchRegion from right_tree.api.models import SoilLayer, EcologicalDistrictLayer, ChristchurchRegion
@ -32,15 +34,24 @@ christchurchregion_mapping = {
'geom': 'MULTIPOLYGON', 'geom': 'MULTIPOLYGON',
} }
# Shapefiles resources_path = Path(right_tree.api.data.__file__).resolve().parent / "resources"
soillayer_shp = Path(right_tree.api.data.__file__).resolve().parent / 'resources' / 'fundamental_soil_layers' / 'fundamental-soil-layers-new-zealand-soil-classification.shp'
ecologicaldistrictlayer_shp = Path(right_tree.api.data.__file__).resolve().parent / 'resources' / 'ecological_districts' / 'DOC_EcologicalDistricts_2021_08_02.shp' soillayer_shp = resources_path / "fundamental-soil-layers-new-zealand-soil-classification.shp"
christchurchregion_shp = Path(right_tree.api.data.__file__).resolve().parent / 'resources' / 'chch_zone' / 'Greater_Christchurch_Area.shp' ecologicaldistrictlayer_shp = resources_path / "Ecological_Districts.shp"
christchurchregion_shp = resources_path / "Greater_Christchurch_Area.shp"
class Command(BaseCommand): class Command(BaseCommand):
help = 'Ingests the shapefile data for ecological regions and soil layers.' help = 'Ingests the shapefile data for ecological regions and soil layers.'
def handle(self, *args, **options): def handle(self, *args, **options):
query = str(resources_path / "*.zip")
sources = [ZipFile(path) for path in iglob(query) if is_zipfile(path)]
for zf in sources:
zf.extractall(resources_path)
zf.close()
self.stdout.write('Loading soil layers...') self.stdout.write('Loading soil layers...')
soil_lm = LayerMapping(SoilLayer, soillayer_shp, soillayer_mapping, transform=False) soil_lm = LayerMapping(SoilLayer, soillayer_shp, soillayer_mapping, transform=False)
soil_lm.save(strict=True) soil_lm.save(strict=True)

View file

@ -85,11 +85,11 @@ WSGI_APPLICATION = 'right_tree.wsgi.application'
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.contrib.gis.db.backends.postgis', 'ENGINE': 'django.contrib.gis.db.backends.postgis',
'NAME': os.getenv("RIGHTTREE_DB", "postgres"), 'NAME': os.getenv("DATABASE_NAME", "righttree"),
'USER': os.getenv("RIGHTTREE_DB_USER", "postgres"), 'USER': os.getenv("DATABASE_USER", "righttree"),
'PASSWORD': os.getenv("RIGHTTREE_DB_PASSWORD", "postgres"), 'PASSWORD': os.getenv("DATABASE_PASSWORD", "righttree"),
'HOST': "postgres", 'HOST': os.getenv("DATABASE_HOST", "postgres"),
'PORT': 5432, 'PORT': int(os.getenv("DATABASE_PORT", 5432)),
} }
} }
@ -139,13 +139,6 @@ STATIC_ROOT = os.path.join(PROJECT_DIR, 'staticfiles')
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
frontend_url = os.getenv('FRONTEND_BASE_URL', 'localhost:3000')
CORS_ALLOWED_ORIGINS = [
f"https://{frontend_url}",
f"http://{frontend_url}"
]
CORS_ALLOW_HEADERS = [ CORS_ALLOW_HEADERS = [
'access-control-allow-origin' 'access-control-allow-origin'
] ]

View file

@ -1,6 +1,11 @@
CREATE DATABASE righttree; CREATE DATABASE righttree;
CREATE USER righttree;
ALTER USER righttree WITH PASSWORD 'righttree';
\c righttree \c righttree
CREATE EXTENSION IF NOT EXISTS postgis; CREATE EXTENSION IF NOT EXISTS postgis;
GRANT ALL ON geometry_columns TO PUBLIC; GRANT ALL ON geometry_columns TO PUBLIC;
GRANT ALL ON spatial_ref_sys TO PUBLIC; GRANT ALL ON spatial_ref_sys TO PUBLIC;
ALTER DATABASE righttree OWNER TO righttree;
GRANT ALL PRIVILEGES ON DATABASE righttree TO righttree;

View file

@ -5,53 +5,86 @@ volumes:
name: righttree-postgres-data name: righttree-postgres-data
services: services:
backend:
restart: unless-stopped backend_migrate:
build: restart: on-failure
context: backend image: right-tree
dockerfile: Dockerfile container_name: backend_migrate
container_name: backend
depends_on: depends_on:
- postgres - postgres
volumes: volumes:
- ./backend:/app - ./backend:/app
env_file: .env user: "$UID:$GID"
command: bash -c "./manage.py runserver 0.0.0.0:8000" environment:
DATABASE_NAME: righttree
DATABASE_USER: righttree
DATABASE_PASSWORD: righttree
DATABASE_HOST: postgres
command:
- bash
- -c
- python manage.py makemigrations --noinput && python manage.py migrate --noinput
backend:
restart: unless-stopped
image: right-tree
container_name: backend
depends_on:
- postgres
- backend_migrate
volumes:
- ./backend:/app
user: "$UID:$GID"
expose:
- "8000"
environment:
LINZ_API_KEY: 3aa06ba7bb2949a9b23ba2c8ac315e2b
DATABASE_NAME: righttree
DATABASE_USER: righttree
DATABASE_PASSWORD: righttree
DATABASE_HOST: postgres
command:
- gunicorn
- --reload
- --bind=0.0.0.0:8000
- --timeout=300
- right_tree.wsgi
frontend: frontend:
image: node:16-alpine3.11 image: node:16-bullseye
restart: unless-stopped restart: unless-stopped
container_name: frontend container_name: frontend
volumes: volumes:
- ./frontend:/app - ./frontend:/app
working_dir: /app working_dir: /app
user: "$UID:$GID"
ports: ports:
- "3000:3000" - 3000:3000
command: sh -c "npm install; npm start" command:
- npm
- start
postgres: postgres:
image: postgis/postgis:13-3.0 image: postgis/postgis:13-3.1
restart: unless-stopped restart: unless-stopped
container_name: postgres container_name: postgres
volumes: volumes:
- righttree-postgres-data:/var/lib/postgresql/data - righttree-postgres-data:/var/lib/postgresql/data
- ./create_database.sql:/docker-entrypoint-initdb.d/create_database.sql - ./create_database.sql:/docker-entrypoint-initdb.d/create_database.sql
ports: ports:
- "5432:5432" - 5432:5432
environment: environment:
- POSTGRES_DB=${POSTGRES_DB} POSTGRES_PASSWORD: postgres
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
nginx: nginx:
image: nginx:1.23.3
restart: unless-stopped
container_name: nginx container_name: nginx
image: nginx
depends_on: depends_on:
- postgres
- backend - backend
- frontend - frontend
volumes: volumes:
- ./nginx.conf:/etc/nginx/nginx.conf - ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./backend/right_tree/staticfiles:/etc/nginx/html/staticfiles - ./backend/right_tree/staticfiles:/etc/nginx/html/staticfiles:ro
ports: ports:
- "9000:80" - "9000:80"

View file

@ -4898,6 +4898,17 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/ansi-escapes/node_modules/type-fest": {
"version": "0.21.3",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ansi-html": { "node_modules/ansi-html": {
"version": "0.0.7", "version": "0.0.7",
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
@ -21357,9 +21368,11 @@
} }
}, },
"node_modules/type-fest": { "node_modules/type-fest": {
"version": "0.21.3", "version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"optional": true,
"peer": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@ -27174,6 +27187,13 @@
"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
"requires": { "requires": {
"type-fest": "^0.21.3" "type-fest": "^0.21.3"
},
"dependencies": {
"type-fest": {
"version": "0.21.3",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="
}
} }
}, },
"ansi-html": { "ansi-html": {
@ -39934,9 +39954,11 @@
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
}, },
"type-fest": { "type-fest": {
"version": "0.21.3", "version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"optional": true,
"peer": true
}, },
"type-is": { "type-is": {
"version": "1.6.18", "version": "1.6.18",