Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update API to use events and task states + fixes to backend refactor #1838

Merged
merged 43 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
6858efb
build: add migrations to update task_history --> task_events
spwoodcock Oct 26, 2024
0bcf994
refactor(backend): wip update to use TaskEvent / State enums
spwoodcock Oct 26, 2024
ac83e40
refactor(frontend): wip update to use TaskEvent / State enums
spwoodcock Oct 26, 2024
e6b1bb9
build: add trigger to set task_event.state automatically from event
spwoodcock Oct 26, 2024
fe40d22
refactor: continued wip to use task events
spwoodcock Oct 26, 2024
577d0d6
refactor: fixes to task state/event distinction
spwoodcock Oct 26, 2024
2e2b4b2
fix: additional fixes to backend usage action --> event
spwoodcock Oct 26, 2024
9fae6a0
build: fix migration conflict if task_history does not exist
spwoodcock Oct 26, 2024
41ccba6
build: add conflate to enums in case for future
spwoodcock Oct 26, 2024
b9d9db9
refactor: further wip updates for task events
spwoodcock Oct 26, 2024
55e220f
refactor(frontend): update enums
spwoodcock Oct 28, 2024
a675478
feat: set db default for UUID --> gen_random_uuid()
spwoodcock Oct 28, 2024
894f6e1
build: fix during development building postgis db images
spwoodcock Oct 28, 2024
43f8ccd
build: fix migrations
spwoodcock Oct 28, 2024
2fa6d2d
fix: working event creation endpoint on tasks
spwoodcock Oct 28, 2024
44ed164
fix(frontend): update to use new /events endpoint
spwoodcock Oct 28, 2024
1f73120
fix: basemap generation via frontend (use POST)
spwoodcock Oct 28, 2024
2e8cbcb
build: add validation and comment to automatic state trigger func
spwoodcock Oct 28, 2024
ccc688a
test: fix task event + comment routes
spwoodcock Oct 28, 2024
210b7ce
fix: usage of task events endpoints
spwoodcock Oct 28, 2024
4ceb30d
fix: activities panel status --> state
spwoodcock Oct 28, 2024
468d32b
build: move api healthcheck to docker-compose file
spwoodcock Oct 28, 2024
646d9e1
build: fix up-mapper working with playwright
spwoodcock Oct 29, 2024
090b6b1
build: update playwright on mapper frontend to match react frontend
spwoodcock Oct 29, 2024
bc87fd6
Fix/event driven tasks continuation (#1839)
NSUWAL123 Oct 29, 2024
f7069fb
fix(backend): fix logic returning all tasks with project
spwoodcock Oct 29, 2024
4eafa50
refactor: update logging on task event
spwoodcock Oct 29, 2024
9859d74
build: add NOT NULL constraints to required task_event fields
spwoodcock Oct 29, 2024
308e03e
fix(frontend): marking task as mapped when not all buildings done
spwoodcock Oct 29, 2024
a123d10
fix(frontend): getting task history comments=false, comments=true
spwoodcock Oct 29, 2024
e7130b1
fix: enable tasks/activity endpoint until merged with dashboard
spwoodcock Oct 29, 2024
ce4f59e
build: add Just command to load prod data into current db
spwoodcock Oct 29, 2024
8e00b0d
fix: use timezone aware timezones from db, fix dashboard api
spwoodcock Oct 29, 2024
0f40363
build: ensure task_events.created_at has timezone
spwoodcock Oct 29, 2024
a04d399
test: fix e2e test expecting 'mapped' when 'unlocked_to_validate'
spwoodcock Oct 29, 2024
f314374
fix(frontend): bug where task state was not updated in task dialog
spwoodcock Oct 29, 2024
ce4519b
fix: remove duplicate use of state selector
spwoodcock Oct 29, 2024
37ebe24
test: small fixes to playwright test strings to validate
spwoodcock Oct 29, 2024
98c3e0d
build: disable ui-mapper in playwright tests for now
spwoodcock Oct 29, 2024
a4cd768
build: update cloudflare/cloudflared version 2024.5.0 --> 2024.10.1
spwoodcock Oct 29, 2024
bc1faea
build: simplify justfile command for tunnelling
spwoodcock Oct 29, 2024
a33fb1b
fix(frontend): check for empty taskId when new geopoint submission
spwoodcock Oct 29, 2024
ce23578
fix: updating review state for a submission
spwoodcock Oct 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ API_PREFIX=${API_PREFIX}
OSM_CLIENT_ID=${OSM_CLIENT_ID}
OSM_CLIENT_SECRET=${OSM_CLIENT_SECRET}
OSM_URL=${OSM_URL:-"https://www.openstreetmap.org"}
OSM_SCOPE=${OSM_SCOPE:-["read_prefs", "send_messages"]}
OSM_SCOPE=${OSM_SCOPE:-'["read_prefs", "send_messages"]'}
OSM_LOGIN_REDIRECT_URI="http${FMTM_DOMAIN:+s}://${FMTM_DOMAIN:-127.0.0.1:7051}/osmauth/"
OSM_SECRET_KEY=${OSM_SECRET_KEY}

Expand Down
2 changes: 2 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
# along with FMTM. If not, see <https:#www.gnu.org/licenses/>.
#

set dotenv-load

mod start 'contrib/just/start/Justfile'
mod stop 'contrib/just/stop/Justfile'
mod build 'contrib/just/build/Justfile'
Expand Down
14 changes: 5 additions & 9 deletions contrib/just/start/Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ tunnel:
docker compose \
-f docker-compose.yml \
-f contrib/tunnel/docker-compose.yml \
up -d --wait
up --wait

# Workaround to until PR merged:
# https://github.com/cloudflare/cloudflared/pull/1135
Expand All @@ -93,18 +93,14 @@ tunnel:
just --unstable dotenv update "EXTRA_CORS_ORIGINS" "${fmtm_url}"
just --unstable dotenv update "S3_ENDPOINT" "${s3_url}"

# Restart the API and UI with environment variables set
API_URL="${api_url}" docker compose \
-f docker-compose.yml \
-f contrib/tunnel/docker-compose.yml \
up -d api ui

# Restart ODK Central with domain override (for form download urls)
# Restart the containers with env vars set
# (API url for frontend, domain for Central form download urls)
CENTRAL_DOMAIN_OVERRIDE="$(echo "${odk_url}" | sed 's|^https://||')" \
API_URL="$(echo "${api_url}" | sed 's|^https://||')" \
docker compose \
-f docker-compose.yml \
-f contrib/tunnel/docker-compose.yml \
up -d central
up -d api ui central

just --unstable start _print-tunnel-urls "$fmtm_url" "$api_url" "$odk_url" "$s3_url"

Expand Down
34 changes: 34 additions & 0 deletions contrib/just/test/Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,37 @@ frontend-interactive:
coverage:
docker compose run --rm --entrypoint='sh -c' api \
'coverage run -m pytest && coverage report -m'

# Load prod data into current database (WARNING: deletes local db data)
[no-cd]
load-prod-data:
#!/usr/bin/env sh
cd {{justfile_directory()}}

docker compose up --wait
# We cannot have electric using a logical replication slot though
docker compose down electric

# Get latest db dump filename
docker compose exec --no-TTY s3 mc alias set prod https://s3.fmtm.hotosm.org "" ""
latest_file=$(docker compose exec --no-TTY s3 mc ls prod/fmtm-db-backups/fmtm \
| awk '{print $NF}' | sort | tail -n 1)
echo "Latest backup file: $latest_file"

# Copy file to current machine
docker compose exec --no-TTY s3 \
mc cp prod/fmtm-db-backups/fmtm/"$latest_file" /tmp/"$latest_file"
docker compose cp s3:/tmp/"$latest_file" /tmp/"$latest_file"

echo "Dropping existing database ${FMTM_DB_NAME} as user ${FMTM_DB_USER}"
docker compose exec --no-TTY -e PGPASSWORD=${FMTM_DB_PASSWORD} ${FMTM_DB_HOST} \
dropdb --echo --if-exists --force -U ${FMTM_DB_USER} ${FMTM_DB_NAME}

echo "Creating new database ${FMTM_DB_NAME} as user ${FMTM_DB_USER}"
docker compose exec --no-TTY -e PGPASSWORD=${FMTM_DB_PASSWORD} ${FMTM_DB_HOST} \
createdb --echo -U ${FMTM_DB_USER} -O ${FMTM_DB_USER} ${FMTM_DB_NAME}

echo "Loading data into database ${FMTM_DB_NAME} as user ${FMTM_DB_USER}"
gunzip -c /tmp/"$latest_file" | \
docker compose exec --no-TTY -e PGPASSWORD=${FMTM_DB_PASSWORD} ${FMTM_DB_HOST} \
pg_restore --verbose -U ${FMTM_DB_USER} -d ${FMTM_DB_NAME}
19 changes: 13 additions & 6 deletions contrib/playwright/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ services:
"critical",
"--no-access-log",
]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/__lbheartbeat__"]
start_period: 60s
interval: 10s
timeout: 5s
retries: 10

ui:
# This hostname is used for Playwright test internal networking
Expand All @@ -50,6 +44,19 @@ services:
start_period: 5s
timeout: 5s

# ui-mapper:
# # This hostname is used for Playwright test internal networking
# hostname: fmtm.dev.test
# environment:
# VITE_API_URL: "http://fmtm.dev.test:8000"
# command: --port 80
# healthcheck:
# test: timeout 5s bash -c ':> /dev/tcp/127.0.0.1/80' || exit 1
# interval: 5s
# retries: 3
# start_period: 5s
# timeout: 5s

ui-test:
image: "mcr.microsoft.com/playwright:${PLAYWRIGHT_TAG:-v1.48.1}"
depends_on:
Expand Down
8 changes: 4 additions & 4 deletions contrib/tunnel/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ networks:

services:
frontend-tunnel:
image: "docker.io/cloudflare/cloudflared:2024.5.0"
image: "docker.io/cloudflare/cloudflared:2024.10.1"
depends_on:
proxy:
condition: service_healthy
Expand All @@ -31,7 +31,7 @@ services:
command: tunnel --url http://proxy:80

api-tunnel:
image: "docker.io/cloudflare/cloudflared:2024.5.0"
image: "docker.io/cloudflare/cloudflared:2024.10.1"
depends_on:
api:
condition: service_healthy
Expand All @@ -41,7 +41,7 @@ services:
command: tunnel --url http://api:8000

central-tunnel:
image: "docker.io/cloudflare/cloudflared:2024.5.0"
image: "docker.io/cloudflare/cloudflared:2024.10.1"
depends_on:
central:
condition: service_healthy
Expand All @@ -51,7 +51,7 @@ services:
command: tunnel --url http://central:8383

s3-tunnel:
image: "docker.io/cloudflare/cloudflared:2024.5.0"
image: "docker.io/cloudflare/cloudflared:2024.10.1"
depends_on:
s3:
condition: service_healthy
Expand Down
12 changes: 10 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ services:
condition: service_started
ui:
condition: service_started
ui-mapper:
condition: service_started
central:
condition: service_started
required: false
Expand Down Expand Up @@ -97,6 +99,12 @@ services:
networks:
- fmtm-net
restart: "unless-stopped"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/__lbheartbeat__"]
start_period: 60s
interval: 10s
timeout: 5s
retries: 10
deploy:
replicas: ${API_REPLICAS:-1}
resources:
Expand Down Expand Up @@ -237,7 +245,7 @@ services:
image: "postgis/postgis:${POSTGIS_TAG:-14-3.5-alpine}"
# Temp workaround until https://github.com/postgis/docker-postgis/issues/216
build:
context: https://github.com/postgis/docker-postgis.git#master:14-3.4/alpine
context: https://github.com/postgis/docker-postgis.git#master:14-3.5/alpine
command: -c 'max_connections=300' -c 'wal_level=logical'
volumes:
- fmtm_db_data:/var/lib/postgresql/data/
Expand Down Expand Up @@ -274,7 +282,7 @@ services:
# AUTH_JWT_KEY: ${ENCRYPTION_KEY}
# AUTH_JWT_AUD: ${FMTM_DOMAIN}
ports:
- "7055:3000"
- "7055:7055"
networks:
- fmtm-net
restart: "unless-stopped"
Expand Down
2 changes: 1 addition & 1 deletion nginx/templates/dev/fmtm.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ upstream mapper {
# Enable sticky sessions based on an incoming client IP address
ip_hash;

server ui-mapper:3000;
server ui-mapper:7055;
}

server {
Expand Down
8 changes: 4 additions & 4 deletions src/backend/app/central/central_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

from app.central import central_deps, central_schemas
from app.config import settings
from app.db.enums import EntityStatus, HTTPStatus
from app.db.enums import EntityState, HTTPStatus
from app.db.models import DbXLSForm
from app.db.postgis_utils import (
geojson_to_javarosa_geom,
Expand Down Expand Up @@ -511,7 +511,7 @@ async def feature_geojson_to_entity_dict(
properties = {
str(key): str(value) for key, value in feature.get("properties", {}).items()
}
# Set to TaskStatus enum READY value (0)
# Set to MappingState enum READY value (0)
properties["status"] = "0"

task_id = properties.get("task_id")
Expand Down Expand Up @@ -769,7 +769,7 @@ async def update_entity_mapping_status(
odk_id: int,
entity_uuid: str,
label: str,
status: EntityStatus,
status: EntityState,
dataset_name: str = "features",
) -> dict:
"""Update the Entity mapping status.
Expand All @@ -781,7 +781,7 @@ async def update_entity_mapping_status(
odk_id (str): The project ID in ODK Central.
entity_uuid (str): The unique entity UUID for ODK Central.
label (str): New label, with emoji prepended for status.
status (EntityStatus): New EntityStatus to assign, in string form.
status (EntityState): New EntityState to assign, in string form.
dataset_name (str): Override the default dataset / Entity list name 'features'.

Returns:
Expand Down
16 changes: 8 additions & 8 deletions src/backend/app/central/central_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from pydantic.functional_validators import field_validator, model_validator

from app.config import HttpUrlStr, decrypt_value, encrypt_value
from app.db.enums import EntityStatus
from app.db.enums import EntityState


class ODKCentral(BaseModel):
Expand Down Expand Up @@ -225,7 +225,7 @@ class EntityMappingStatus(EntityOsmID, EntityTaskID):
"""The status for mapping an Entity/feature."""

updatedAt: Optional[str] = Field(exclude=True) # noqa: N815
status: Optional[EntityStatus] = None
status: Optional[EntityState] = None

@computed_field
@property
Expand All @@ -238,18 +238,18 @@ class EntityMappingStatusIn(BaseModel):
"""Update the mapping status for an Entity."""

entity_id: str
status: EntityStatus
status: EntityState
label: str

@field_validator("label", mode="before")
@classmethod
def append_status_emoji(cls, value: str, info: ValidationInfo) -> str:
"""Add πŸ”’ (locked), βœ… (complete) or ❌ (invalid) emojis."""
status = info.data.get("status", EntityStatus.UNLOCKED.value)
status = info.data.get("status", EntityState.READY.value)
emojis = {
str(EntityStatus.LOCKED.value): "πŸ”’",
str(EntityStatus.MAPPED.value): "βœ…",
str(EntityStatus.BAD.value): "❌",
str(EntityState.OPENED_IN_ODK.value): "πŸ”’",
str(EntityState.SURVEY_SUBMITTED.value): "βœ…",
str(EntityState.MARKED_BAD.value): "❌",
}

# Remove any existing emoji at the start of the label
Expand All @@ -265,6 +265,6 @@ def append_status_emoji(cls, value: str, info: ValidationInfo) -> str:

@field_validator("status", mode="after")
@classmethod
def integer_status_to_string(cls, value: EntityStatus) -> str:
def integer_status_to_string(cls, value: EntityState) -> str:
"""Convert integer status to string for ODK Entity data."""
return str(value.value)
Loading
Loading