From f053081854b37f552eb0b09eb2c1898449c9462f Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 16:10:50 +0100 Subject: [PATCH 01/19] add build ui or pull ui image to install script --- install.sh | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/install.sh b/install.sh index 6f4f71c..966ee6d 100755 --- a/install.sh +++ b/install.sh @@ -9,18 +9,64 @@ set -e SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +# ============================================================================ +# Options +# ============================================================================ + +MODEL_COMPOSE_ARG="" +BUILD_UI=false +UNINSTALL=false + +usage() { + cat <<'USAGE' +Usage: ./install.sh [options] + + -m, --models Model set to deploy: a name resolved to + docker-compose.models..yml, or a path to a + compose file. Defaults to MODEL_COMPOSE in .env, + falling back to 'piwind'. + --build-ui Rebuild the UI docker container. + -u, --uninstall Bring the stack down and delete its volumes. + -h, --help Show this message. +USAGE +} + +require_value() { + if [ -z "$2" ]; then + echo "ERROR: $1 needs a value" >&2 + exit 1 + fi +} + +while [ $# -gt 0 ]; do + case "$1" in + -m|--models) require_value "$1" "${2:-}"; MODEL_COMPOSE_ARG="$2"; shift 2 ;; + --build-ui) BUILD_UI=true; shift ;; + -u|--uninstall) UNINSTALL=true; shift ;; + -h|--help) usage; exit 0 ;; + *) echo "ERROR: unknown option '$1'" >&2; usage >&2; exit 1 ;; + esac +done + # ============================================================================ # Uninstall mode # ============================================================================ -if [[ "$1" == "--uninstall" || "$1" == "-u" ]]; then +if [ "$UNINSTALL" = true ]; then echo "Uninstalling Oasis platform (docker compose down only)..." + # Every model set, so model workers come down whichever one was deployed + MODEL_FILES=() + for model_file in "$SCRIPT_DIR"/docker-compose.models.*.yml; do + [ -f "$model_file" ] && MODEL_FILES+=(-f "$model_file") + done + set +e docker compose -f "$SCRIPT_DIR/docker-compose.yml" \ -f "$SCRIPT_DIR/docker-compose.ui.yml" \ -f "$SCRIPT_DIR/docker-compose.keycloak.yml" \ -f "$SCRIPT_DIR/docker-compose.authentik.yml" \ + "${MODEL_FILES[@]}" \ down --remove-orphans -v 2>/dev/null # Also try old files in case of migration docker compose -f "$SCRIPT_DIR/oasis-platform.yml" \ @@ -197,20 +243,32 @@ echo "--- Pulling images ---" set +e docker pull "${SERVER_IMG:-coreoasis/api_server}:${VERS_API:-latest}" docker pull "${WORKER_IMG:-coreoasis/model_worker}:${VERS_WORKER:-latest}" -docker pull "${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" set -e echo "" +# ============================================================================ +# Build UI if necessary +# ============================================================================ + +if [ "$BUILD_UI" = true ]; then + echo " -> Building UI image" + docker compose $COMPOSE_FILES build --no-cache pythonui +else + echo " -> Using UI image ${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" + set +e + docker pull "${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" + set -e +fi + +exit 0 + # ============================================================================ # Deploy services # ============================================================================ echo "--- Deploying services ---" -# Build UI -docker compose $COMPOSE_FILES build --no-cache pythonui - # Start all services docker compose $COMPOSE_FILES up -d From b023fff944936c36695e47d917e3c3df73bcdaee Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 16:11:06 +0100 Subject: [PATCH 02/19] makefile to build and push ui images --- Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..60dade8 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +#!make +include .env + +build: + docker build -f oasisui_st_app.Dockerfile . -t ${PYTHONUI_IMG}:${VERS_UI} + +push: + docker push ${PYTHONUI_IMG}:${VERS_UI} + +build_and_push: build push From 712959877be9f830ba21ab51607206cb36777b60 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 16:51:35 +0100 Subject: [PATCH 03/19] enable programmatic model set retrieval --- install.sh | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/install.sh b/install.sh index 966ee6d..c4b500e 100755 --- a/install.sh +++ b/install.sh @@ -13,7 +13,7 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Options # ============================================================================ -MODEL_COMPOSE_ARG="" +MODEL_SET_ARG="" BUILD_UI=false UNINSTALL=false @@ -21,11 +21,12 @@ usage() { cat <<'USAGE' Usage: ./install.sh [options] - -m, --models Model set to deploy: a name resolved to - docker-compose.models..yml, or a path to a - compose file. Defaults to MODEL_COMPOSE in .env, - falling back to 'piwind'. - --build-ui Rebuild the UI docker container. + -m, --model-set Name of model set to deploy, defaults to + 'piwind'. Expects to find + `docker-compose.models..yml file and + optionally a get-.sh script to deploy the + model in the root directory. + --build-ui Rebuild the UI docker container. -u, --uninstall Bring the stack down and delete its volumes. -h, --help Show this message. USAGE @@ -40,7 +41,7 @@ require_value() { while [ $# -gt 0 ]; do case "$1" in - -m|--models) require_value "$1" "${2:-}"; MODEL_COMPOSE_ARG="$2"; shift 2 ;; + -m|--model-set) require_value "$1" "${2:-}"; MODEL_SET_ARG="$2"; shift 2 ;; --build-ui) BUILD_UI=true; shift ;; -u|--uninstall) UNINSTALL=true; shift ;; -h|--help) usage; exit 0 ;; @@ -96,6 +97,7 @@ set -a source "$SCRIPT_DIR/.env" set +a + # ============================================================================ # Auto-detect Docker socket if not set # ============================================================================ @@ -110,11 +112,25 @@ elif [ ! -S "$DOCKER_SOCK" ]; then export DOCKER_SOCK=/var/run/docker.sock fi +# ============================================================================ +# Resolve the model compose files +# ============================================================================ + +MODEL_SET_NAME="${MODEL_SET_ARG:-${MODEL_SET:-piwind}}" +MODEL_COMPOSE_FILE="$SCRIPT_DIR/docker-compose.models.$MODEL_SET_NAME.yml" +MODEL_SETUP_SCRIPT="$SCRIPT_DIR/get-$MODEL_SET_NAME.sh" +if [ ! -f "$MODEL_COMPOSE_FILE" ]; then + echo "ERROR: no model set '$MODEL_SET_NAME': $(basename "$MODEL_COMPOSE_FILE") not found" + echo "" + exit 1 +fi + echo "========================================" echo " OasisPythonUI Installer" echo "========================================" echo "" echo " Auth type: $API_AUTH_TYPE" +echo " Model set: $MODEL_SET_NAME" echo " Hostname: $OASIS_UI_HOSTNAME" echo " Protocol: $OASIS_PROTOCOL" echo " Docker socket: $DOCKER_SOCK" @@ -198,23 +214,20 @@ fi echo " -> Compose files: $COMPOSE_FILES" echo "" + # ============================================================================ -# Clone PiWind model (if not present) +# Get model data # ============================================================================ +echo "--- Retrieving Model Data ---" -GIT_PIWIND=OasisPiWind - -if [ ! -d "$SCRIPT_DIR/$GIT_PIWIND/.git" ]; then - echo "--- Cloning PiWind model ---" - mkdir -p "$SCRIPT_DIR/$GIT_PIWIND" - cd "$SCRIPT_DIR/$GIT_PIWIND" - git clone --depth 1 --branch "${VERS_PIWIND}" "https://github.com/OasisLMF/$GIT_PIWIND.git" . - cd "$SCRIPT_DIR" - echo "" +if [ -f "$MODEL_SETUP_SCRIPT" ]; then + bash "$MODEL_SETUP_SCRIPT" else - echo " -> PiWind model already cloned" - echo "" + echo " -> No get-$MODEL_NAME.sh, expecting the $MODEL_NAME model data to be in place" fi +echo "" + +exit 0 # ============================================================================ # Check for previous install @@ -261,7 +274,6 @@ else set -e fi -exit 0 # ============================================================================ # Deploy services From 949e0b8ff6900be2600523e777afbddd3647f32b Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:12:02 +0100 Subject: [PATCH 04/19] add model compose and allow disabling ui pull --- install.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/install.sh b/install.sh index c4b500e..83ad3a2 100755 --- a/install.sh +++ b/install.sh @@ -15,6 +15,7 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" MODEL_SET_ARG="" BUILD_UI=false +PULL_UI=true UNINSTALL=false usage() { @@ -27,6 +28,7 @@ Usage: ./install.sh [options] optionally a get-.sh script to deploy the model in the root directory. --build-ui Rebuild the UI docker container. + --no-pull Do not pull the UI container from docker. -u, --uninstall Bring the stack down and delete its volumes. -h, --help Show this message. USAGE @@ -43,6 +45,7 @@ while [ $# -gt 0 ]; do case "$1" in -m|--model-set) require_value "$1" "${2:-}"; MODEL_SET_ARG="$2"; shift 2 ;; --build-ui) BUILD_UI=true; shift ;; + --no-pull) PULL_UI=false; shift ;; -u|--uninstall) UNINSTALL=true; shift ;; -h|--help) usage; exit 0 ;; *) echo "ERROR: unknown option '$1'" >&2; usage >&2; exit 1 ;; @@ -201,7 +204,7 @@ fi # Build compose file list # ============================================================================ -COMPOSE_FILES="-f $SCRIPT_DIR/docker-compose.yml -f $SCRIPT_DIR/docker-compose.ui.yml" +COMPOSE_FILES="-f $SCRIPT_DIR/docker-compose.yml -f $MODEL_COMPOSE_FILE -f $SCRIPT_DIR/docker-compose.ui.yml" if [ "$API_AUTH_TYPE" = "keycloak" ]; then COMPOSE_FILES="$COMPOSE_FILES -f $SCRIPT_DIR/docker-compose.keycloak.yml" @@ -227,8 +230,6 @@ else fi echo "" -exit 0 - # ============================================================================ # Check for previous install # ============================================================================ @@ -267,13 +268,14 @@ echo "" if [ "$BUILD_UI" = true ]; then echo " -> Building UI image" docker compose $COMPOSE_FILES build --no-cache pythonui -else - echo " -> Using UI image ${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" +elif [ "$PULL_UI" = true ]; then + echo " -> Pulling UI image ${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" set +e docker pull "${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" set -e fi +echo " -> Using UI image ${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" # ============================================================================ # Deploy services From ad72025e2b64ef9509b3e53a4e26d591b888de9e Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:12:51 +0100 Subject: [PATCH 05/19] fix docker compose --- docker-compose.authentik.yml | 2 +- docker-compose.keycloak.yml | 2 +- docker-compose.ui.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.authentik.yml b/docker-compose.authentik.yml index edc03c8..fa93b7d 100644 --- a/docker-compose.authentik.yml +++ b/docker-compose.authentik.yml @@ -2,7 +2,7 @@ volumes: authentik-db-data: services: - model-registration: + server: depends_on: authentik-server: condition: service_healthy diff --git a/docker-compose.keycloak.yml b/docker-compose.keycloak.yml index d360fcc..9a4f43b 100644 --- a/docker-compose.keycloak.yml +++ b/docker-compose.keycloak.yml @@ -2,7 +2,7 @@ volumes: keycloak-db-data: services: - model-registration: + server: depends_on: keycloak: condition: service_healthy diff --git a/docker-compose.ui.yml b/docker-compose.ui.yml index 3d11d41..143a313 100644 --- a/docker-compose.ui.yml +++ b/docker-compose.ui.yml @@ -1,7 +1,7 @@ services: pythonui: restart: always - image: ${PYTHONUI_IMG}:${VERS_UI} + image: ${PYTHONUI_IMG-coreoasis/oasispythonui_app}:${VERS_UI-latest} build: dockerfile: ./oasisui_st_app.Dockerfile ports: From 44aaf524c7fa091ecc7f8208554631286d4ee165 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:14:34 +0100 Subject: [PATCH 06/19] model retrieval scripts --- get-piwind.sh | 15 +++++++++++++++ get-scenarios.sh | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 get-piwind.sh create mode 100644 get-scenarios.sh diff --git a/get-piwind.sh b/get-piwind.sh new file mode 100644 index 0000000..2575395 --- /dev/null +++ b/get-piwind.sh @@ -0,0 +1,15 @@ +#!/bin/bash +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +GIT_PIWIND=OasisPiWind + +if [ ! -d "$SCRIPT_DIR/$GIT_PIWIND/.git" ]; then + echo "--- Cloning PiWind model ---" + mkdir -p "$SCRIPT_DIR/$GIT_PIWIND" + cd "$SCRIPT_DIR/$GIT_PIWIND" + git clone --depth 1 --branch "${VERS_PIWIND}" "https://github.com/OasisLMF/$GIT_PIWIND.git" . + cd "$SCRIPT_DIR" + echo "" +else + echo " -> PiWind model already cloned" + echo "" +fi diff --git a/get-scenarios.sh b/get-scenarios.sh new file mode 100644 index 0000000..341ae87 --- /dev/null +++ b/get-scenarios.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# +# Put the scenario model data in place for docker-compose.models.scenarios.yml. +# +# Run by install.sh when the scenarios model set is selected, and safe to run +# on its own. Idempotent: it clones the Scenarios repository and pulls down the +# model files once, then no-ops. +# +# Reads SCENARIOS_PATH from the environment (install.sh exports it from .env): +# the directory the scenario workers mount their model data from. + +set -e + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +SCENARIOS_REPO="https://github.com/OasisLMF/Scenarios.git" +# One of the model directories the compose file mounts, used to tell whether +# the model files have been pulled down from s3 yet +SENTINEL_MODEL="ImpactForecasting/MAEQ/1.0.0" + +if [ -z "$SCENARIOS_PATH" ]; then + echo "ERROR: SCENARIOS_PATH is not set." + echo " Set it in .env to the directory the scenario models live in, for" + echo " example SCENARIOS_PATH=/home/ubuntu/Scenarios. This script clones" + echo " $SCENARIOS_REPO there if it is missing." + exit 1 +fi + +# The scenarios set runs PiWind alongside the scenario models +bash "$SCRIPT_DIR/get-piwind.sh" + +if [ -d "$SCENARIOS_PATH" ]; then + echo " -> Scenario models already checked out at $SCENARIOS_PATH" +else + echo " -> Cloning scenario models into $SCENARIOS_PATH" + git clone --depth 1 "$SCENARIOS_REPO" "$SCENARIOS_PATH" +fi + +if [ -d "$SCENARIOS_PATH/$SENTINEL_MODEL" ]; then + echo " -> Scenario model files already downloaded" + exit 0 +fi + +if [ ! -x "$SCENARIOS_PATH/get_s3_data_reduced.sh" ]; then + echo "ERROR: $SCENARIOS_PATH/get_s3_data_reduced.sh not found." + exit 1 +fi + +echo " -> Downloading scenario model files (this takes a while)" +( cd "$SCENARIOS_PATH" && ./get_s3_data_reduced.sh ) From a96c9911481ed969c834fc2e417c22150e4fc709 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:21:22 +0100 Subject: [PATCH 07/19] remove pull ui disable option and just suppress pull failures --- install.sh | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/install.sh b/install.sh index 83ad3a2..d039d49 100755 --- a/install.sh +++ b/install.sh @@ -15,7 +15,6 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" MODEL_SET_ARG="" BUILD_UI=false -PULL_UI=true UNINSTALL=false usage() { @@ -28,7 +27,6 @@ Usage: ./install.sh [options] optionally a get-.sh script to deploy the model in the root directory. --build-ui Rebuild the UI docker container. - --no-pull Do not pull the UI container from docker. -u, --uninstall Bring the stack down and delete its volumes. -h, --help Show this message. USAGE @@ -45,7 +43,6 @@ while [ $# -gt 0 ]; do case "$1" in -m|--model-set) require_value "$1" "${2:-}"; MODEL_SET_ARG="$2"; shift 2 ;; --build-ui) BUILD_UI=true; shift ;; - --no-pull) PULL_UI=false; shift ;; -u|--uninstall) UNINSTALL=true; shift ;; -h|--help) usage; exit 0 ;; *) echo "ERROR: unknown option '$1'" >&2; usage >&2; exit 1 ;; @@ -255,8 +252,8 @@ fi echo "--- Pulling images ---" set +e -docker pull "${SERVER_IMG:-coreoasis/api_server}:${VERS_API:-latest}" -docker pull "${WORKER_IMG:-coreoasis/model_worker}:${VERS_WORKER:-latest}" +docker pull "${SERVER_IMG:-coreoasis/api_server}:${VERS_API:-latest}" --ignore-pull-failures +docker pull "${WORKER_IMG:-coreoasis/model_worker}:${VERS_WORKER:-latest}" --ignore-pull-failures set -e echo "" @@ -268,15 +265,13 @@ echo "" if [ "$BUILD_UI" = true ]; then echo " -> Building UI image" docker compose $COMPOSE_FILES build --no-cache pythonui -elif [ "$PULL_UI" = true ]; then +else echo " -> Pulling UI image ${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" set +e - docker pull "${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" + docker pull "${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" --ignore-pull-failures set -e fi -echo " -> Using UI image ${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" - # ============================================================================ # Deploy services # ============================================================================ From 445e49851adfc2dbf1d9d9e372bbbd8c7838ee32 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:40:58 +0100 Subject: [PATCH 08/19] make ui control configurable during deployment --- .streamlit/secrets.toml | 2 +- docker-compose.ui.yml | 6 ++-- scenarios/.streamlit/config.toml | 14 +++++++++ scenarios/.streamlit/secrets.toml | 4 +++ ...0_ghana-e760461_jba-analysis_settings.json | 0 ...1_nepal-e151185_jba-analysis_settings.json | 0 ...2_nepal-e432557_jba-analysis_settings.json | 0 ...3_nepal-e505423_jba-analysis_settings.json | 0 .../14_france-hail_ipe-analysis_settings.json | 0 .../1_piwind_oasislmf-analysis_settings.json | 31 +++++++++++++++++++ ..._impact-forecasting-analysis_settings.json | 0 ..._impact-forecasting-analysis_settings.json | 0 ..._impact-forecasting-analysis_settings.json | 0 ...taiwan-defended_jba-analysis_settings.json | 0 ...iwan-undefended_jba-analysis_settings.json | 0 .../7_us-hurricane_ara-analysis_settings.json | 0 ...8_ghana-e635900_jba-analysis_settings.json | 0 ...9_ghana-e689982_jba-analysis_settings.json | 0 18 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 scenarios/.streamlit/config.toml create mode 100644 scenarios/.streamlit/secrets.toml rename {defaults => scenarios/defaults}/10_ghana-e760461_jba-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/11_nepal-e151185_jba-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/12_nepal-e432557_jba-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/13_nepal-e505423_jba-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/14_france-hail_ipe-analysis_settings.json (100%) create mode 100644 scenarios/defaults/1_piwind_oasislmf-analysis_settings.json rename {defaults => scenarios/defaults}/2_maeq_impact-forecasting-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/3_euws_impact-forecasting-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/4_treq_impact-forecasting-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/5_philippines-taiwan-defended_jba-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/6_philippines-taiwan-undefended_jba-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/7_us-hurricane_ara-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/8_ghana-e635900_jba-analysis_settings.json (100%) rename {defaults => scenarios/defaults}/9_ghana-e689982_jba-analysis_settings.json (100%) diff --git a/.streamlit/secrets.toml b/.streamlit/secrets.toml index e356f22..78245c7 100644 --- a/.streamlit/secrets.toml +++ b/.streamlit/secrets.toml @@ -1,4 +1,4 @@ -auth_type='oidc' +auth_type='simple' user='admin' password='password' diff --git a/docker-compose.ui.yml b/docker-compose.ui.yml index 143a313..deec61f 100644 --- a/docker-compose.ui.yml +++ b/docker-compose.ui.yml @@ -25,9 +25,9 @@ services: - OASIS_HOSTNAME=${OASIS_UI_HOSTNAME} - OASIS_PROTOCOL=${OASIS_PROTOCOL:-http} volumes: - - ./defaults/:/usr/src/app/defaults/ - - ./ui-config.json:/usr/src/app/ui-config.json:ro - - ./.streamlit/:/usr/src/app/.streamlit:ro + - ${UI_DEFAULTS:-./defaults/}:/usr/src/app/defaults/ + - ${UI_CONFIG:-./ui-config.json}:/usr/src/app/ui-config.json:ro + - ${UI_STREAMLIT:-./.streamlit/}:/usr/src/app/.streamlit:ro healthcheck: test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8501/_stcore/health')"] interval: 30s diff --git a/scenarios/.streamlit/config.toml b/scenarios/.streamlit/config.toml new file mode 100644 index 0000000..fe84af0 --- /dev/null +++ b/scenarios/.streamlit/config.toml @@ -0,0 +1,14 @@ +[client] +showSidebarNavigation = false +showErrorDetails = "type" +toolbarMode = "minimal" + +[server] +fileWatcherType = "none" +headless = true +enableXsrfProtection = false +enableCORS = false + +[browser] +gatherUsageStats = false +showSidebarNavigation = false diff --git a/scenarios/.streamlit/secrets.toml b/scenarios/.streamlit/secrets.toml new file mode 100644 index 0000000..3c61bf5 --- /dev/null +++ b/scenarios/.streamlit/secrets.toml @@ -0,0 +1,4 @@ +auth_type='simple' + +user='admin' +password='password' diff --git a/defaults/10_ghana-e760461_jba-analysis_settings.json b/scenarios/defaults/10_ghana-e760461_jba-analysis_settings.json similarity index 100% rename from defaults/10_ghana-e760461_jba-analysis_settings.json rename to scenarios/defaults/10_ghana-e760461_jba-analysis_settings.json diff --git a/defaults/11_nepal-e151185_jba-analysis_settings.json b/scenarios/defaults/11_nepal-e151185_jba-analysis_settings.json similarity index 100% rename from defaults/11_nepal-e151185_jba-analysis_settings.json rename to scenarios/defaults/11_nepal-e151185_jba-analysis_settings.json diff --git a/defaults/12_nepal-e432557_jba-analysis_settings.json b/scenarios/defaults/12_nepal-e432557_jba-analysis_settings.json similarity index 100% rename from defaults/12_nepal-e432557_jba-analysis_settings.json rename to scenarios/defaults/12_nepal-e432557_jba-analysis_settings.json diff --git a/defaults/13_nepal-e505423_jba-analysis_settings.json b/scenarios/defaults/13_nepal-e505423_jba-analysis_settings.json similarity index 100% rename from defaults/13_nepal-e505423_jba-analysis_settings.json rename to scenarios/defaults/13_nepal-e505423_jba-analysis_settings.json diff --git a/defaults/14_france-hail_ipe-analysis_settings.json b/scenarios/defaults/14_france-hail_ipe-analysis_settings.json similarity index 100% rename from defaults/14_france-hail_ipe-analysis_settings.json rename to scenarios/defaults/14_france-hail_ipe-analysis_settings.json diff --git a/scenarios/defaults/1_piwind_oasislmf-analysis_settings.json b/scenarios/defaults/1_piwind_oasislmf-analysis_settings.json new file mode 100644 index 0000000..3b5431f --- /dev/null +++ b/scenarios/defaults/1_piwind_oasislmf-analysis_settings.json @@ -0,0 +1,31 @@ +{ + "model_name_id": "PiWind", + "model_supplier_id": "OasisLMF", + "gul_threshold": 0, + "gul_output": true, + "model_settings": { + "event_set": "p", + "event_occurrence_id": "lt" + }, + "gul_summaries": [ + { + "eltcalc": true, + "aalcalc": false, + "lec_output": false, + "leccalc": { + "full_uncertainty_aep": true, + "full_uncertainty_oep": true, + "return_period_file": true + }, + "id": 1 + } + ], + "il_output": true, + "il_summaries": [ + { + "eltcalc": true, + "id": 1 + } + ], + "number_of_samples": 10 +} diff --git a/defaults/2_maeq_impact-forecasting-analysis_settings.json b/scenarios/defaults/2_maeq_impact-forecasting-analysis_settings.json similarity index 100% rename from defaults/2_maeq_impact-forecasting-analysis_settings.json rename to scenarios/defaults/2_maeq_impact-forecasting-analysis_settings.json diff --git a/defaults/3_euws_impact-forecasting-analysis_settings.json b/scenarios/defaults/3_euws_impact-forecasting-analysis_settings.json similarity index 100% rename from defaults/3_euws_impact-forecasting-analysis_settings.json rename to scenarios/defaults/3_euws_impact-forecasting-analysis_settings.json diff --git a/defaults/4_treq_impact-forecasting-analysis_settings.json b/scenarios/defaults/4_treq_impact-forecasting-analysis_settings.json similarity index 100% rename from defaults/4_treq_impact-forecasting-analysis_settings.json rename to scenarios/defaults/4_treq_impact-forecasting-analysis_settings.json diff --git a/defaults/5_philippines-taiwan-defended_jba-analysis_settings.json b/scenarios/defaults/5_philippines-taiwan-defended_jba-analysis_settings.json similarity index 100% rename from defaults/5_philippines-taiwan-defended_jba-analysis_settings.json rename to scenarios/defaults/5_philippines-taiwan-defended_jba-analysis_settings.json diff --git a/defaults/6_philippines-taiwan-undefended_jba-analysis_settings.json b/scenarios/defaults/6_philippines-taiwan-undefended_jba-analysis_settings.json similarity index 100% rename from defaults/6_philippines-taiwan-undefended_jba-analysis_settings.json rename to scenarios/defaults/6_philippines-taiwan-undefended_jba-analysis_settings.json diff --git a/defaults/7_us-hurricane_ara-analysis_settings.json b/scenarios/defaults/7_us-hurricane_ara-analysis_settings.json similarity index 100% rename from defaults/7_us-hurricane_ara-analysis_settings.json rename to scenarios/defaults/7_us-hurricane_ara-analysis_settings.json diff --git a/defaults/8_ghana-e635900_jba-analysis_settings.json b/scenarios/defaults/8_ghana-e635900_jba-analysis_settings.json similarity index 100% rename from defaults/8_ghana-e635900_jba-analysis_settings.json rename to scenarios/defaults/8_ghana-e635900_jba-analysis_settings.json diff --git a/defaults/9_ghana-e689982_jba-analysis_settings.json b/scenarios/defaults/9_ghana-e689982_jba-analysis_settings.json similarity index 100% rename from defaults/9_ghana-e689982_jba-analysis_settings.json rename to scenarios/defaults/9_ghana-e689982_jba-analysis_settings.json From ed00a6ae404340d15f8a5527960c4345371ba216 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:41:17 +0100 Subject: [PATCH 09/19] remove leftover model worker in server compose --- docker-compose.yml | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a72e3cf..24fd7e1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -206,49 +206,6 @@ services: broker: condition: service_healthy - model-registration: - restart: "no" - image: ${SERVER_IMG:-coreoasis/api_server}:${VERS_API:-latest} - command: ["python3", "/scripts/model_registration.py"] - environment: - <<: *shared-env - OASIS_API_URL: "http://server:8000" - OASIS_MODEL_SUPPLIER_ID: OasisLMF - OASIS_MODEL_ID: PiWind - OASIS_MODEL_VERSION_ID: v2 - OASIS_RUN_MODE: v2 - OASIS_MODEL_DATA_DIRECTORY: /model/meta-data - volumes: - - ./OasisPiWind/:/model:ro - - ./scripts:/scripts:ro - depends_on: - server: - condition: service_healthy - - piwind-worker: - restart: always - image: ${WORKER_IMG:-coreoasis/model_worker}:${VERS_WORKER:-latest} - environment: - <<: *shared-env - OASIS_MODEL_SUPPLIER_ID: OasisLMF - OASIS_MODEL_ID: PiWind - OASIS_MODEL_VERSION_ID: v2 - OASIS_RUN_MODE: v2 - OASIS_OASISLMF_VERSION: ${OASIS_OASISLMF_VERSION} - OASIS_ODS_VERSION: ${OASIS_ODS_VERSION} - OASIS_ODM_VERSION: ${OASIS_ODM_VERSION} - OASIS_OED_SCHEMA_INFO: ${OASIS_OED_SCHEMA_INFO} - volumes: - - ./OasisPiWind/:/home/worker/model - - filestore-data:/shared-fs:rw - depends_on: - celery-db: - condition: service_healthy - broker: - condition: service_healthy - model-registration: - condition: service_completed_successfully - server-db: restart: always image: postgres:15-alpine From 836fa6f80162f84102d91027ca3f0c1af0d388ca Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:07:33 +0100 Subject: [PATCH 10/19] fix warnings --- install.sh | 6 +++--- modules/nav.py | 2 +- pages/analyses.py | 12 ++++++------ pages/components/create.py | 6 +++--- pages/components/display.py | 6 +++--- pages/components/output.py | 8 ++++---- pages/scenarios.py | 10 +++++----- scripts/add_settings_template.py | 4 ++-- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/install.sh b/install.sh index d039d49..af95e6e 100755 --- a/install.sh +++ b/install.sh @@ -252,8 +252,8 @@ fi echo "--- Pulling images ---" set +e -docker pull "${SERVER_IMG:-coreoasis/api_server}:${VERS_API:-latest}" --ignore-pull-failures -docker pull "${WORKER_IMG:-coreoasis/model_worker}:${VERS_WORKER:-latest}" --ignore-pull-failures +docker pull --ignore-pull-failures "${SERVER_IMG:-coreoasis/api_server}:${VERS_API:-latest}" +docker pull --ignore-pull-failures "${WORKER_IMG:-coreoasis/model_worker}:${VERS_WORKER:-latest}" set -e echo "" @@ -268,7 +268,7 @@ if [ "$BUILD_UI" = true ]; then else echo " -> Pulling UI image ${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" set +e - docker pull "${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" --ignore-pull-failures + docker pull --ignore-pull-failures "${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" set -e fi diff --git a/modules/nav.py b/modules/nav.py index 86f3b45..1c5468f 100644 --- a/modules/nav.py +++ b/modules/nav.py @@ -18,7 +18,7 @@ def SidebarNav(no_client=False): f'' '') else: - if st.button("Logout", use_container_width=True): + if st.button("Logout", width='stretch'): logout() else: st.page_link('app.py', label="Login") diff --git a/pages/analyses.py b/pages/analyses.py index aeb2f46..bc3756b 100644 --- a/pages/analyses.py +++ b/pages/analyses.py @@ -221,7 +221,7 @@ def run_analysis(re_handler): left, middle, right = st.columns(3, vertical_alignment='center') left.markdown("2) Generate Inputs:") - if middle.button("Generate", use_container_width=True, disabled=not validations.is_valid()): + if middle.button("Generate", width='stretch', disabled=not validations.is_valid()): try: client.analyses.generate(selected['id']) st.success('Input generation started.') @@ -290,7 +290,7 @@ def set_analysis_settings(analysis): validations.add_validation(NotNoneValidation('Analysis'), selected) validations.add_validation(KeyInValuesValidation('Status'), selected, 'status', ['READY', 'NEW', 'RUN_ERROR', 'RUN_CANCELLED', 'RUN_COMPLETED']) if middle.button("Upload Settings File", disabled=not validations.is_valid(), - use_container_width=True): + width='stretch'): upload_settings_file(selected) # Set settings button @@ -302,7 +302,7 @@ def set_analysis_settings(analysis): if not enable_button: msg = validations.message if right.button("Set Analysis Settings", disabled=not enable_button, help=msg, - use_container_width=True): + width='stretch'): set_analysis_settings(selected) created_analysis_settings = consume_analysis_settings() @@ -341,7 +341,7 @@ def set_analysis_settings(analysis): if not run_enabled: msg = validations.message - if middle.button("Run", use_container_width=True, disabled=not run_enabled, help=msg): + if middle.button("Run", width='stretch', disabled=not run_enabled, help=msg): try: client_interface.run(selected['id']) @@ -359,7 +359,7 @@ def set_analysis_settings(analysis): validation = NotNoneValidation('Selected analysis') button_enabled = validation.is_valid(selected) - if left.button("Delete", use_container_width=True, disabled = not button_enabled, help=validation.message): + if left.button("Delete", width='stretch', disabled = not button_enabled, help=validation.message): try: client.analyses.delete(selected['id']) st.rerun() @@ -375,7 +375,7 @@ def set_analysis_settings(analysis): if not run_enabled: msg = validations.message - if middle.button("Logs", use_container_width=True, disabled=not run_enabled, help=msg): + if middle.button("Logs", width='stretch', disabled=not run_enabled, help=msg): @st.dialog("Log", width='large') def error_log_dialog(analysis_id): input_gen_traceback = client_interface.analyses.get_traceback(analysis_id, 'input_generation') diff --git a/pages/components/create.py b/pages/components/create.py index e1e11ab..3c2c0c5 100644 --- a/pages/components/create.py +++ b/pages/components/create.py @@ -339,7 +339,7 @@ def summary_settings_fragment(oed_fields, perspective): st.session_state[f'editing_level_{perspective}'] = False if col1.button('Add Level', key=f'{perspective}_summary_add_button', - use_container_width=True): + width='stretch'): st.session_state[f'adding_level_{perspective}'] = not st.session_state[f'adding_level_{perspective}'] st.session_state[f'{perspective}_summaries'] = original_summaries @@ -356,14 +356,14 @@ def summary_settings_fragment(oed_fields, perspective): st.rerun(scope='fragment') if col2.button('Delete Level', key=f'{perspective}_summary_delete_button', - use_container_width=True, disabled=selected is None): + width='stretch', disabled=selected is None): pos = [i for i, el in enumerate(curr_summaries) if el['id'] == selected][0] curr_summaries.pop(pos) st.session_state[f'{perspective}_summaries'] = curr_summaries st.rerun(scope='fragment') if col3.button('Edit Level', key=f'{perspective}_summary_edit_button', - use_container_width=True, disabled=selected is None): + width='stretch', disabled=selected is None): st.session_state[f'editing_level_{perspective}'] = not st.session_state[f'editing_level_{perspective}'] st.session_state[f'{perspective}_summaries'] = original_summaries diff --git a/pages/components/display.py b/pages/components/display.py index 67b2d42..4e6380c 100644 --- a/pages/components/display.py +++ b/pages/components/display.py @@ -87,14 +87,14 @@ def display(self, max_rows=1000, key=None): if self.data.empty: st.dataframe(pd.DataFrame(columns=self.display_cols), hide_index=self.hide_index, column_config=self.column_config, - column_order=self.display_cols, use_container_width=True, + column_order=self.display_cols, width='stretch', key=key) return None args = { 'hide_index': self.hide_index, 'column_config': self.column_config, - 'use_container_width': True, + 'width': 'stretch', 'column_order': self.display_cols, 'key': key } @@ -280,7 +280,7 @@ def find_zoom_level(lon_range): len=500 )) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width='stretch') def generate_choropleth(self): # Get country GeoJSON diff --git a/pages/components/output.py b/pages/components/output.py index 8da840f..8112873 100644 --- a/pages/components/output.py +++ b/pages/components/output.py @@ -432,7 +432,7 @@ def elt_ord_table(result, perspective, oed_fields = None, key_prefix=None, table_df = table_df.sort_values(by=order_col, ascending=False) # OED Filters - with st.popover("OED Filters", use_container_width=True): + with st.popover("OED Filters", width='stretch'): for oed_field in oed_fields: options = table_df[oed_field].unique() oed_filter = st.multiselect(f"{oed_field} Filter:", options, @@ -765,7 +765,7 @@ def generate_aalcalc_fragment(p, vis): if breakdown_field_invalid: st.error("Too many values in group field.") - st.plotly_chart(graph, use_container_width=True) + st.plotly_chart(graph, width='stretch') @st.fragment def generate_alt_fragment(p, vis, output_type='alt_meanonly'): @@ -802,7 +802,7 @@ def generate_alt_fragment(p, vis, output_type='alt_meanonly'): if breakdown_field_invalid: st.error("Too many values in group field.") - st.plotly_chart(graph, use_container_width=True, key=f'{output_type}_graph') + st.plotly_chart(graph, width='stretch', key=f'{output_type}_graph') def generate_leccalc_fragment(p, vis, lec_outputs): lec_options = [option for option in lec_outputs.keys() if lec_outputs[option]] @@ -1296,7 +1296,7 @@ def generate_aalcalc_comparison_fragment(p, outputs, names = None): labels = {'mean': 'Mean', 'name': 'Analysis Name'}, color_discrete_sequence= px.colors.sequential.RdBu, category_orders={'name': names}) - st.plotly_chart(graph, use_container_width=True) + st.plotly_chart(graph, width='stretch') if breakdown_field_invalid: st.error("Too many values in group field.") diff --git a/pages/scenarios.py b/pages/scenarios.py index 6032b19..77d2293 100644 --- a/pages/scenarios.py +++ b/pages/scenarios.py @@ -147,7 +147,7 @@ def filter_valid_rows(df, key, valid_map, filter_col): cols = st.columns([0.25, 0.25, 0.25, 0.25]) with cols[0]: - with st.popover("Create Analysis", disabled=not enable_popover, help=msg, use_container_width=True): + with st.popover("Create Analysis", disabled=not enable_popover, help=msg, width='stretch'): if enable_popover: resp = create_analysis_form(portfolios=[selected_portfolio.to_dict()], models=[selected_model.to_dict()]) if resp: @@ -165,7 +165,7 @@ def filter_valid_rows(df, key, valid_map, filter_col): enable_map_button = validation.is_valid(selected_portfolio) if st.button("Exposure Map", disabled=not enable_map_button, - help=validation.get_message(), use_container_width=True): + help=validation.get_message(), width='stretch'): @st.dialog("Locations Map", width='large') def show_locations_map(): with st.spinner('Loading map...'): @@ -189,7 +189,7 @@ def show_locations_map(): enable_model_details = validation.is_valid(selected_model) if st.button("Scenario Details", disabled=not enable_model_details, - help = validation.get_message(), use_container_width=True): + help = validation.get_message(), width='stretch'): try: model_settings = client_interface.models.settings.get(selected_model['id']) except HTTPError as e: @@ -279,7 +279,7 @@ def analysis_fragment(): run_started = False with columns[0]: - if st.button('Run', disabled = not run_enabled, help=msg, use_container_width=True): + if st.button('Run', disabled = not run_enabled, help=msg, width='stretch'): try: # Load from platform templates = client_interface.models.setting_templates.get(selected['model']) @@ -420,7 +420,7 @@ def generate_perspective_visualisation(perspective, summaries_settings): download_enabled = validations.is_valid() with columns[1]: - if st.button("Show Output", use_container_width=True, disabled = not download_enabled): + if st.button("Show Output", width='stretch', disabled = not download_enabled): display_outputs(client_interface, selected["id"], selected['model']) diff --git a/scripts/add_settings_template.py b/scripts/add_settings_template.py index 90a6a6c..5ebcffa 100644 --- a/scripts/add_settings_template.py +++ b/scripts/add_settings_template.py @@ -12,12 +12,12 @@ def add_model_analysis_settings(client, model_id, analysis_settings_path): models = client.models.search({'model_id': str(model_id)}).json() if not models: - logger.warn(f"model id: {model_id} not found") + logger.warning(f"model id: {model_id} not found") return analysis_settings_path = Path(analysis_settings_path) if not analysis_settings_path.is_file(): - logger.warn(f"analysis settings file for model_id {model_id} not found") + logger.warning(f"analysis settings file for model_id {model_id} not found") return with open(analysis_settings_path, "r") as f: From 60801ee3d4142fd36cde40da26f4ca7610066117 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:35:46 +0100 Subject: [PATCH 11/19] make for scenarios --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 60dade8..7603d4f 100644 --- a/Makefile +++ b/Makefile @@ -7,4 +7,12 @@ build: push: docker push ${PYTHONUI_IMG}:${VERS_UI} +scenarios_portfolios: + python ./scripts/add_test_portfolios.py -c ./scenarios/portfolios.json + +scenarios_settings: + python ./scripts/add_settings_template.py -c ./scenarios/a_settings.json + +scenarios: scenarios_portfolios scenarios_settings + build_and_push: build push From 4a65243172b676ce8f4dd44adc2c8bc244fbcfb9 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:36:09 +0100 Subject: [PATCH 12/19] update a_settings for scenarios --- scenarios/a_settings.json | 28 +++++++++---------- ..._impact-forecasting-analysis_settings.json | 19 +++---------- ..._impact-forecasting-analysis_settings.json | 16 ++--------- scenarios/deploy_scenarios_ui.sh | 0 scenarios/ui-config.json | 2 +- 5 files changed, 22 insertions(+), 43 deletions(-) mode change 100644 => 100755 scenarios/deploy_scenarios_ui.sh diff --git a/scenarios/a_settings.json b/scenarios/a_settings.json index e41a5da..e30f085 100644 --- a/scenarios/a_settings.json +++ b/scenarios/a_settings.json @@ -1,58 +1,58 @@ [ { "model_name_id": "PiWind", - "settings_path": "./defaults/1_piwind_oasislmf-analysis_settings.json" + "settings_path": "./scenarios/defaults/1_piwind_oasislmf-analysis_settings.json" }, { "model_name_id": "EUWS", - "settings_path": "./defaults/3_euws_impact-forecasting-analysis_settings.json" + "settings_path": "./scenarios/defaults/3_euws_impact-forecasting-analysis_settings.json" }, { "model_name_id": "MAEQ", - "settings_path": "./defaults/2_maeq_impact-forecasting-analysis_settings.json" + "settings_path": "./scenarios/defaults/2_maeq_impact-forecasting-analysis_settings.json" }, { "model_name_id": "Philippines-Taiwan-Undefended", - "settings_path": "./defaults/6_philippines-taiwan-undefended_jba-analysis_settings.json" + "settings_path": "./scenarios/defaults/6_philippines-taiwan-undefended_jba-analysis_settings.json" }, { "model_name_id": "Philippines-Taiwan-Defended", - "settings_path": "./defaults/5_philippines-taiwan-defended_jba-analysis_settings.json" + "settings_path": "./scenarios/defaults/5_philippines-taiwan-defended_jba-analysis_settings.json" }, { "model_name_id": "France-Hail", - "settings_path": "./defaults/14_france-hail_ipe-analysis_settings.json" + "settings_path": "./scenarios/defaults/14_france-hail_ipe-analysis_settings.json" }, { "model_name_id": "Nepal-E432557", - "settings_path": "./defaults/12_nepal-e432557_jba-analysis_settings.json" + "settings_path": "./scenarios/defaults/12_nepal-e432557_jba-analysis_settings.json" }, { "model_name_id": "Ghana-E760461", - "settings_path": "./defaults/10_ghana-e760461_jba-analysis_settings.json" + "settings_path": "./scenarios/defaults/10_ghana-e760461_jba-analysis_settings.json" }, { "model_name_id": "Ghana-E635900", - "settings_path": "./defaults/8_ghana-e635900_jba-analysis_settings.json" + "settings_path": "./scenarios/defaults/8_ghana-e635900_jba-analysis_settings.json" }, { "model_name_id": "Nepal-E151185", - "settings_path": "./defaults/11_nepal-e151185_jba-analysis_settings.json" + "settings_path": "./scenarios/defaults/11_nepal-e151185_jba-analysis_settings.json" }, { "model_name_id": "Nepal-E505423", - "settings_path": "./defaults/13_nepal-e505423_jba-analysis_settings.json" + "settings_path": "./scenarios/defaults/13_nepal-e505423_jba-analysis_settings.json" }, { "model_name_id": "Ghana-E689982", - "settings_path": "./defaults/9_ghana-e689982_jba-analysis_settings.json" + "settings_path": "./scenarios/defaults/9_ghana-e689982_jba-analysis_settings.json" }, { "model_name_id": "TREQ", - "settings_path": "./defaults/4_treq_impact-forecasting-analysis_settings.json" + "settings_path": "./scenarios/defaults/4_treq_impact-forecasting-analysis_settings.json" }, { "model_name_id": "US-Hurricane", - "settings_path": "./defaults/7_us-hurricane_ara-analysis_settings.json" + "settings_path": "./scenarios/defaults/7_us-hurricane_ara-analysis_settings.json" } ] diff --git a/scenarios/defaults/2_maeq_impact-forecasting-analysis_settings.json b/scenarios/defaults/2_maeq_impact-forecasting-analysis_settings.json index cd41e90..e9a78c7 100644 --- a/scenarios/defaults/2_maeq_impact-forecasting-analysis_settings.json +++ b/scenarios/defaults/2_maeq_impact-forecasting-analysis_settings.json @@ -10,23 +10,12 @@ }, "gul_output": true, "gul_summaries": [ - { + { "id": 1, - "summarycalc": false, - "aalcalc": false, - "eltcalc": true, - "lec_output": false, - "leccalc": { - "outputs": { - "full_uncertainty_aep": false, - "full_uncertainty_oep": false, - "sample_mean_aep": false, - "sample_mean_oep": false - }, - "return_period_file": false - } + "ord_output": { + "elt_moment": true + } } ], "gul_threshold": 0 - } diff --git a/scenarios/defaults/3_euws_impact-forecasting-analysis_settings.json b/scenarios/defaults/3_euws_impact-forecasting-analysis_settings.json index 60a36b3..fa661ed 100644 --- a/scenarios/defaults/3_euws_impact-forecasting-analysis_settings.json +++ b/scenarios/defaults/3_euws_impact-forecasting-analysis_settings.json @@ -10,19 +10,9 @@ "gul_summaries": [ { "id": 1, - "summarycalc": false, - "aalcalc": false, - "eltcalc": true, - "lec_output": false, - "leccalc": { - "outputs": { - "full_uncertainty_aep": false, - "full_uncertainty_oep": false, - "sample_mean_aep": false, - "sample_mean_oep": false - }, - "return_period_file": false - } + "ord_output": { + "elt_moment": true + } } ], "gul_threshold": 0 diff --git a/scenarios/deploy_scenarios_ui.sh b/scenarios/deploy_scenarios_ui.sh old mode 100644 new mode 100755 diff --git a/scenarios/ui-config.json b/scenarios/ui-config.json index 4b3fe47..5d4a9bd 100644 --- a/scenarios/ui-config.json +++ b/scenarios/ui-config.json @@ -22,7 +22,7 @@ "France-Hail": ["france-hail-flat"] }, "footer" : { - "path": "scenarios/assets/footer.md", + "path": "ui_assets/footer.md", "pages": [ "scenarios", "comparison" From 9c5b054b40b6cd9ca7934ec2c27b43a202db8922 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:36:30 +0100 Subject: [PATCH 13/19] ignore pull failures does not exist for image --- install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install.sh b/install.sh index af95e6e..f0adcc8 100755 --- a/install.sh +++ b/install.sh @@ -252,8 +252,8 @@ fi echo "--- Pulling images ---" set +e -docker pull --ignore-pull-failures "${SERVER_IMG:-coreoasis/api_server}:${VERS_API:-latest}" -docker pull --ignore-pull-failures "${WORKER_IMG:-coreoasis/model_worker}:${VERS_WORKER:-latest}" +docker pull "${SERVER_IMG:-coreoasis/api_server}:${VERS_API:-latest}" +docker pull "${WORKER_IMG:-coreoasis/model_worker}:${VERS_WORKER:-latest}" set -e echo "" @@ -268,7 +268,7 @@ if [ "$BUILD_UI" = true ]; then else echo " -> Pulling UI image ${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" set +e - docker pull --ignore-pull-failures "${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" + docker pull "${PYTHONUI_IMG:-coreoasis/oasispythonui_app}:${VERS_UI:-latest}" set -e fi From 0a30a811ee0c7c90b61806dbd13d21eaf040e29e Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:36:54 +0100 Subject: [PATCH 14/19] dummy ui_assets directory --- ui_assets/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 ui_assets/README.md diff --git a/ui_assets/README.md b/ui_assets/README.md new file mode 100644 index 0000000..b429a55 --- /dev/null +++ b/ui_assets/README.md @@ -0,0 +1,14 @@ +# UI Assets + +Files the UI's `ui-config.json` points at — a footer, a logo, anything a +deployment wants to drop in without rebuilding the image. + +This directory is mounted read-only at `/usr/src/app/ui_assets` in the UI +container, so a config refers to what lands here as `ui_assets/`: + +Point `UI_ASSETS` in `.env` at a different directory to swap the set, the way +`UI_CONFIG` swaps the page set. The scenarios deployment uses +`UI_ASSETS=./scenarios/assets`. + +Not to be confused with `../assets/`, which holds map data the application code +reads directly and which ships inside the image. From 6eb3b35e1c63d3e7a9eba5596a650885e4a9c03f Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Wed, 26 Aug 2026 16:53:27 +0100 Subject: [PATCH 15/19] fix default settings to ord --- ...0_ghana-e760461_jba-analysis_settings.json | 15 ++++------- ...1_nepal-e151185_jba-analysis_settings.json | 15 ++++------- ...2_nepal-e432557_jba-analysis_settings.json | 15 ++++------- ...3_nepal-e505423_jba-analysis_settings.json | 15 ++++------- .../14_france-hail_ipe-analysis_settings.json | 15 ++++------- ..._impact-forecasting-analysis_settings.json | 20 ++++---------- ...taiwan-defended_jba-analysis_settings.json | 13 ++++----- ...iwan-undefended_jba-analysis_settings.json | 15 ++++------- .../7_us-hurricane_ara-analysis_settings.json | 27 ++++--------------- ...8_ghana-e635900_jba-analysis_settings.json | 15 ++++------- ...9_ghana-e689982_jba-analysis_settings.json | 15 ++++------- 11 files changed, 55 insertions(+), 125 deletions(-) diff --git a/scenarios/defaults/10_ghana-e760461_jba-analysis_settings.json b/scenarios/defaults/10_ghana-e760461_jba-analysis_settings.json index 176a163..157a141 100644 --- a/scenarios/defaults/10_ghana-e760461_jba-analysis_settings.json +++ b/scenarios/defaults/10_ghana-e760461_jba-analysis_settings.json @@ -2,16 +2,11 @@ "gul_output": true, "gul_summaries": [ { - "aalcalc": true, - "eltcalc": true, - "id": 1, - "return_period_file": false, - "lec_output": false, - "leccalc": { - "full_uncertainty_aep": true, - "full_uncertainty_oep": true, - "return_period_file": true - } + "ord_output": { + "elt_moment": true, + "alt_period": true + }, + "id": 1 } ], "gul_threshold": 0, diff --git a/scenarios/defaults/11_nepal-e151185_jba-analysis_settings.json b/scenarios/defaults/11_nepal-e151185_jba-analysis_settings.json index fef0975..7b21ffb 100644 --- a/scenarios/defaults/11_nepal-e151185_jba-analysis_settings.json +++ b/scenarios/defaults/11_nepal-e151185_jba-analysis_settings.json @@ -2,16 +2,11 @@ "gul_output": true, "gul_summaries": [ { - "aalcalc": true, - "eltcalc": true, - "id": 1, - "return_period_file": false, - "lec_output": false, - "leccalc": { - "full_uncertainty_aep": true, - "full_uncertainty_oep": true, - "return_period_file": true - } + "ord_output": { + "elt_moment": true, + "alt_period": true + }, + "id": 1 } ], "gul_threshold": 0, diff --git a/scenarios/defaults/12_nepal-e432557_jba-analysis_settings.json b/scenarios/defaults/12_nepal-e432557_jba-analysis_settings.json index 55adbc0..48e204a 100644 --- a/scenarios/defaults/12_nepal-e432557_jba-analysis_settings.json +++ b/scenarios/defaults/12_nepal-e432557_jba-analysis_settings.json @@ -2,16 +2,11 @@ "gul_output": true, "gul_summaries": [ { - "aalcalc": true, - "eltcalc": true, - "id": 1, - "return_period_file": false, - "lec_output": false, - "leccalc": { - "full_uncertainty_aep": true, - "full_uncertainty_oep": true, - "return_period_file": true - } + "ord_output": { + "elt_moment": true, + "alt_period": true + }, + "id": 1 } ], "gul_threshold": 0, diff --git a/scenarios/defaults/13_nepal-e505423_jba-analysis_settings.json b/scenarios/defaults/13_nepal-e505423_jba-analysis_settings.json index 8120c0e..53b9613 100644 --- a/scenarios/defaults/13_nepal-e505423_jba-analysis_settings.json +++ b/scenarios/defaults/13_nepal-e505423_jba-analysis_settings.json @@ -2,16 +2,11 @@ "gul_output": true, "gul_summaries": [ { - "aalcalc": true, - "eltcalc": true, - "id": 1, - "return_period_file": false, - "lec_output": false, - "leccalc": { - "full_uncertainty_aep": true, - "full_uncertainty_oep": true, - "return_period_file": true - } + "ord_output": { + "elt_moment": true, + "alt_period": true + }, + "id": 1 } ], "gul_threshold": 0, diff --git a/scenarios/defaults/14_france-hail_ipe-analysis_settings.json b/scenarios/defaults/14_france-hail_ipe-analysis_settings.json index eda5db7..daec002 100644 --- a/scenarios/defaults/14_france-hail_ipe-analysis_settings.json +++ b/scenarios/defaults/14_france-hail_ipe-analysis_settings.json @@ -2,16 +2,11 @@ "gul_output": true, "gul_summaries": [ { - "aalcalc": true, - "eltcalc": true, - "id": 1, - "return_period_file": false, - "lec_output": false, - "leccalc": { - "full_uncertainty_aep": true, - "full_uncertainty_oep": true, - "return_period_file": true - } + "ord_output": { + "elt_moment": true, + "alt_period": true + }, + "id": 1 } ], "gul_threshold": 0, diff --git a/scenarios/defaults/4_treq_impact-forecasting-analysis_settings.json b/scenarios/defaults/4_treq_impact-forecasting-analysis_settings.json index 7fe9c2b..7839a21 100644 --- a/scenarios/defaults/4_treq_impact-forecasting-analysis_settings.json +++ b/scenarios/defaults/4_treq_impact-forecasting-analysis_settings.json @@ -12,23 +12,13 @@ }, "gul_output": true, "gul_summaries": [ - { + { "id": 1, - "summarycalc": false, - "aalcalc": false, - "eltcalc": true, - "lec_output": false, - "leccalc": { - "outputs": { - "full_uncertainty_aep": false, - "full_uncertainty_oep": false, - "sample_mean_aep": false, - "sample_mean_oep": false - }, - "return_period_file": false - } + "ord_output": { + "elt_moment": true + } } ], "gul_threshold": 0 - + } diff --git a/scenarios/defaults/5_philippines-taiwan-defended_jba-analysis_settings.json b/scenarios/defaults/5_philippines-taiwan-defended_jba-analysis_settings.json index 8721101..8b9fefe 100644 --- a/scenarios/defaults/5_philippines-taiwan-defended_jba-analysis_settings.json +++ b/scenarios/defaults/5_philippines-taiwan-defended_jba-analysis_settings.json @@ -2,15 +2,12 @@ "gul_output": true, "gul_summaries": [ { - "aalcalc": true, - "eltcalc": true, "id": 1, - "return_period_file": false, - "lec_output": false, - "leccalc": { - "full_uncertainty_aep": true, - "full_uncertainty_oep": true, - "return_period_file": true + "ord_output": { + "elt_moment": true, + "ept_full_uncertainty_aep": true, + "ept_full_uncertainty_oep": true, + "return_period_file": true } } ], diff --git a/scenarios/defaults/6_philippines-taiwan-undefended_jba-analysis_settings.json b/scenarios/defaults/6_philippines-taiwan-undefended_jba-analysis_settings.json index ef83b2a..4466fdb 100644 --- a/scenarios/defaults/6_philippines-taiwan-undefended_jba-analysis_settings.json +++ b/scenarios/defaults/6_philippines-taiwan-undefended_jba-analysis_settings.json @@ -2,16 +2,11 @@ "gul_output": true, "gul_summaries": [ { - "aalcalc": true, - "eltcalc": true, - "id": 1, - "return_period_file": false, - "lec_output": false, - "leccalc": { - "full_uncertainty_aep": true, - "full_uncertainty_oep": true, - "return_period_file": true - } + "ord_output": { + "elt_moment": true, + "alt_period": true + }, + "id": 1 } ], "gul_threshold": 0, diff --git a/scenarios/defaults/7_us-hurricane_ara-analysis_settings.json b/scenarios/defaults/7_us-hurricane_ara-analysis_settings.json index 6fb2e39..6b1733d 100644 --- a/scenarios/defaults/7_us-hurricane_ara-analysis_settings.json +++ b/scenarios/defaults/7_us-hurricane_ara-analysis_settings.json @@ -2,31 +2,14 @@ "gul_output": true, "gul_summaries": [ { - "aalcalc": true, - "eltcalc": true, - "id": 1, "ord_output": { - "ept_per_sample_mean_aep": true - }, - "return_period_file": true - - } - ], - "gul_threshold": 0, - "il_output": false, - "il_summaries": [ - { - "aalcalc": true, - "eltcalc": true, + "elt_moment": true, + "alt_period": true, + "ept_per_sample_mean_aep": true + }, "id": 1, - "lec_output": true, - "leccalc": { - "outputs": { - "full_uncertainty_aep": true, - "full_uncertainty_oep": true - }, "return_period_file": true - } + } ], "gul_threshold": 0, diff --git a/scenarios/defaults/8_ghana-e635900_jba-analysis_settings.json b/scenarios/defaults/8_ghana-e635900_jba-analysis_settings.json index 9e2c117..06a6840 100644 --- a/scenarios/defaults/8_ghana-e635900_jba-analysis_settings.json +++ b/scenarios/defaults/8_ghana-e635900_jba-analysis_settings.json @@ -4,16 +4,11 @@ "gul_output": true, "gul_summaries": [ { - "aalcalc": true, - "eltcalc": true, - "id": 1, - "return_period_file": false, - "lec_output": false, - "leccalc": { - "full_uncertainty_aep": true, - "full_uncertainty_oep": true, - "return_period_file": true - } + "ord_output": { + "elt_moment": true, + "alt_period": true + }, + "id": 1 } ], "gul_threshold": 0, diff --git a/scenarios/defaults/9_ghana-e689982_jba-analysis_settings.json b/scenarios/defaults/9_ghana-e689982_jba-analysis_settings.json index b067095..7aaf90a 100644 --- a/scenarios/defaults/9_ghana-e689982_jba-analysis_settings.json +++ b/scenarios/defaults/9_ghana-e689982_jba-analysis_settings.json @@ -2,16 +2,11 @@ "gul_output": true, "gul_summaries": [ { - "aalcalc": true, - "eltcalc": true, - "id": 1, - "return_period_file": false, - "lec_output": false, - "leccalc": { - "full_uncertainty_aep": true, - "full_uncertainty_oep": true, - "return_period_file": true - } + "ord_output": { + "elt_moment": true, + "alt_period": true + }, + "id": 1 } ], "gul_threshold": 0, From 5bc11054a6febf62614e4d5d96682faa99860b18 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Wed, 26 Aug 2026 16:53:53 +0100 Subject: [PATCH 16/19] add model docker compose files --- docker-compose.models.piwind.yml | 103 ++++++++++++ docker-compose.models.scenarios.yml | 249 ++++++++++++++++++++++++++++ 2 files changed, 352 insertions(+) create mode 100644 docker-compose.models.piwind.yml create mode 100644 docker-compose.models.scenarios.yml diff --git a/docker-compose.models.piwind.yml b/docker-compose.models.piwind.yml new file mode 100644 index 0000000..2ee738c --- /dev/null +++ b/docker-compose.models.piwind.yml @@ -0,0 +1,103 @@ +# ============================================================================ +# PiWind demo model +# ============================================================================ +# The default model set, loaded by install.sh (MODEL_COMPOSE=piwind, or +# --models piwind). Always used together with docker-compose.yml, which +# provides the server, databases, broker and the filestore volume. +# + +x-shared-env: &shared-env + # Debug and basic config + OASIS_DEBUG: ${OASIS_DEBUG:-1} + OASIS_URL_SUB_PATH: 0 + + # Hostname configuration (matches Minikube ingress) + INGRESS_EXTERNAL_HOST: ${OASIS_UI_HOSTNAME:-localhost} + INGRESS_INTERNAL_HOST: traefik + + # Authentication configuration + OASIS_SERVER_API_AUTH_TYPE: ${API_AUTH_TYPE:-simple} + OASIS_SERVER_ALLOWED_OIDC_AUTH_PROVIDERS: ${OASIS_SERVER_ALLOWED_OIDC_AUTH_PROVIDERS} + + # Database connections + OASIS_SERVER_DB_ENGINE: django.db.backends.postgresql + OASIS_SERVER_DB_HOST: ${OASIS_SERVER_DB_HOST} + OASIS_SERVER_DB_PORT: ${OASIS_SERVER_DB_PORT} + OASIS_SERVER_DB_NAME: ${OASIS_SERVER_DB_NAME} + OASIS_SERVER_DB_USER: ${OASIS_SERVER_DB_USER} + OASIS_SERVER_DB_PASS: ${OASIS_SERVER_DB_PASS} + + OASIS_CELERY_DB_ENGINE: db+postgresql+psycopg + OASIS_CELERY_DB_HOST: ${OASIS_CELERY_DB_HOST} + OASIS_CELERY_DB_PORT: ${OASIS_CELERY_DB_PORT} + OASIS_CELERY_DB_NAME: ${OASIS_CELERY_DB_NAME} + OASIS_CELERY_DB_USER: ${OASIS_CELERY_DB_USER} + OASIS_CELERY_DB_PASS: ${OASIS_CELERY_DB_PASS} + + # Broker and channel layer + OASIS_CELERY_BROKER_URL: ${OASIS_CELERY_BROKER_URL} + OASIS_SERVER_CHANNEL_LAYER_HOST: ${REDIS_HOST} + OASIS_SERVER_CHANNEL_LAYER_PORT: ${REDIS_PORT:-6379} + OASIS_SERVER_CHANNEL_LAYER_SSL: "false" + + # Task queues + OASIS_TASK_CONTROLLER_QUEUE: task-controller + OASIS_INPUT_GENERATION_CONTROLLER_QUEUE: task-controller + OASIS_LOSSES_GENERATION_CONTROLLER_QUEUE: task-controller + + # Service account credentials (set dynamically by install.sh) + OASIS_SERVICE_USERNAME_OR_ID: ${OASIS_SERVICE_USERNAME_OR_ID} + OASIS_SERVICE_PASSWORD_OR_SECRET: ${OASIS_SERVICE_PASSWORD_OR_SECRET} + OASIS_USE_OIDC: ${OASIS_USE_OIDC} + + # OIDC configuration (only used when API_AUTH_TYPE != simple) + OASIS_SERVER_OIDC_ENDPOINT: ${OASIS_SERVER_OIDC_ENDPOINT:-} + OASIS_SERVER_OIDC_CLIENT_NAME: ${OASIS_SERVER_OIDC_CLIENT_NAME:-} + OASIS_SERVER_OIDC_CLIENT_SECRET: ${OASIS_SERVER_OIDC_CLIENT_SECRET:-} + OASIS_SERVER_OIDC_SERVICE_CLIENT_NAME: ${OASIS_SERVER_OIDC_SERVICE_CLIENT_NAME:-} + OASIS_SERVER_OIDC_SERVICE_CLIENT_SECRET: ${OASIS_SERVER_OIDC_SERVICE_CLIENT_SECRET:-} + +services: + model-registration: + restart: "no" + image: ${SERVER_IMG:-coreoasis/api_server}:${VERS_API:-latest} + command: ["python3", "/scripts/model_registration.py"] + environment: + <<: *shared-env + OASIS_API_URL: "http://server:8000" + OASIS_MODEL_SUPPLIER_ID: OasisLMF + OASIS_MODEL_ID: PiWind + OASIS_MODEL_VERSION_ID: v2 + OASIS_RUN_MODE: v2 + OASIS_MODEL_DATA_DIRECTORY: /model/meta-data + volumes: + - ./OasisPiWind/:/model:ro + - ./scripts:/scripts:ro + depends_on: + server: + condition: service_healthy + + piwind-worker: + restart: always + image: ${WORKER_IMG:-coreoasis/model_worker}:${VERS_WORKER:-latest} + environment: + <<: *shared-env + OASIS_MODEL_SUPPLIER_ID: OasisLMF + OASIS_MODEL_ID: PiWind + OASIS_MODEL_VERSION_ID: v2 + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_VERSION: ${OASIS_OASISLMF_VERSION} + OASIS_ODS_VERSION: ${OASIS_ODS_VERSION} + OASIS_ODM_VERSION: ${OASIS_ODM_VERSION} + OASIS_OED_SCHEMA_INFO: ${OASIS_OED_SCHEMA_INFO} + volumes: + - ./OasisPiWind/:/home/worker/model + - filestore-data:/shared-fs:rw + depends_on: + celery-db: + condition: service_healthy + broker: + condition: service_healthy + model-registration: + condition: service_completed_successfully + diff --git a/docker-compose.models.scenarios.yml b/docker-compose.models.scenarios.yml new file mode 100644 index 0000000..379fde2 --- /dev/null +++ b/docker-compose.models.scenarios.yml @@ -0,0 +1,249 @@ +# ============================================================================ +# PiWind demo model +# ============================================================================ +# The default model set, loaded by install.sh (MODEL_COMPOSE=piwind, or +# --models piwind). Always used together with docker-compose.yml, which +# provides the server, databases, broker and the filestore volume. +# + +x-shared-env: &worker-env + # Debug and basic config + OASIS_DEBUG: ${OASIS_DEBUG:-1} + OASIS_URL_SUB_PATH: 0 + + # Hostname configuration (matches Minikube ingress) + INGRESS_EXTERNAL_HOST: ${OASIS_UI_HOSTNAME:-localhost} + INGRESS_INTERNAL_HOST: traefik + + # Authentication configuration + OASIS_SERVER_API_AUTH_TYPE: ${API_AUTH_TYPE:-simple} + OASIS_SERVER_ALLOWED_OIDC_AUTH_PROVIDERS: ${OASIS_SERVER_ALLOWED_OIDC_AUTH_PROVIDERS} + + # Database connections + OASIS_SERVER_DB_ENGINE: django.db.backends.postgresql + OASIS_SERVER_DB_HOST: ${OASIS_SERVER_DB_HOST} + OASIS_SERVER_DB_PORT: ${OASIS_SERVER_DB_PORT} + OASIS_SERVER_DB_NAME: ${OASIS_SERVER_DB_NAME} + OASIS_SERVER_DB_USER: ${OASIS_SERVER_DB_USER} + OASIS_SERVER_DB_PASS: ${OASIS_SERVER_DB_PASS} + + OASIS_CELERY_DB_ENGINE: db+postgresql+psycopg + OASIS_CELERY_DB_HOST: ${OASIS_CELERY_DB_HOST} + OASIS_CELERY_DB_PORT: ${OASIS_CELERY_DB_PORT} + OASIS_CELERY_DB_NAME: ${OASIS_CELERY_DB_NAME} + OASIS_CELERY_DB_USER: ${OASIS_CELERY_DB_USER} + OASIS_CELERY_DB_PASS: ${OASIS_CELERY_DB_PASS} + + # Broker and channel layer + OASIS_CELERY_BROKER_URL: ${OASIS_CELERY_BROKER_URL} + OASIS_SERVER_CHANNEL_LAYER_HOST: ${REDIS_HOST} + OASIS_SERVER_CHANNEL_LAYER_PORT: ${REDIS_PORT:-6379} + OASIS_SERVER_CHANNEL_LAYER_SSL: "false" + + # Task queues + OASIS_TASK_CONTROLLER_QUEUE: task-controller + OASIS_INPUT_GENERATION_CONTROLLER_QUEUE: task-controller + OASIS_LOSSES_GENERATION_CONTROLLER_QUEUE: task-controller + + # Service account credentials (set dynamically by install.sh) + OASIS_SERVICE_USERNAME_OR_ID: ${OASIS_SERVICE_USERNAME_OR_ID} + OASIS_SERVICE_PASSWORD_OR_SECRET: ${OASIS_SERVICE_PASSWORD_OR_SECRET} + OASIS_USE_OIDC: ${OASIS_USE_OIDC} + + # OIDC configuration (only used when API_AUTH_TYPE != simple) + OASIS_SERVER_OIDC_ENDPOINT: ${OASIS_SERVER_OIDC_ENDPOINT:-} + OASIS_SERVER_OIDC_CLIENT_NAME: ${OASIS_SERVER_OIDC_CLIENT_NAME:-} + OASIS_SERVER_OIDC_CLIENT_SECRET: ${OASIS_SERVER_OIDC_CLIENT_SECRET:-} + OASIS_SERVER_OIDC_SERVICE_CLIENT_NAME: ${OASIS_SERVER_OIDC_SERVICE_CLIENT_NAME:-} + OASIS_SERVER_OIDC_SERVICE_CLIENT_SECRET: ${OASIS_SERVER_OIDC_SERVICE_CLIENT_SECRET:-} + +x-worker: &worker + restart: always + image: ${WORKER_IMG:-coreoasis/model_worker}:${VERS_WORKER:-latest} + depends_on: + server: + condition: service_healthy + celery-db: + condition: service_healthy + broker: + condition: service_healthy + + +services: + piwind-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: OasisLMF + OASIS_MODEL_ID: PiWind + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + volumes: + - ./OasisPiWind/:/home/worker/model + - filestore-data:/shared-fs:rw + + maeq-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: Impact Forecasting + OASIS_MODEL_ID: MAEQ + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + volumes: + - ${SCENARIOS_PATH}/ImpactForecasting/MAEQ/1.0.0/:/home/worker/model + - filestore-data:/shared-fs:rw + + euws-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: Impact Forecasting + OASIS_MODEL_ID: EUWS + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + volumes: + - ${SCENARIOS_PATH}/ImpactForecasting/EUWS/1.0.0/:/home/worker/model + - filestore-data:/shared-fs:rw + + treq-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: Impact Forecasting + OASIS_MODEL_ID: TREQ + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + volumes: + - ${SCENARIOS_PATH}/ImpactForecasting/TREQ/1.0.0/:/home/worker/model + - filestore-data:/shared-fs:rw + + philippines_taiwan_defended-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: JBA + OASIS_MODEL_ID: Philippines-Taiwan-Defended + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_CONFIG: /home/worker/model/oasislmf.json + volumes: + - ${SCENARIOS_PATH}/JBA/Philippines-Taiwan/combined-defended/:/home/worker/model + - filestore-data:/shared-fs:rw + + philippines_taiwan_undefended-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: JBA + OASIS_MODEL_ID: Philippines-Taiwan-Undefended + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_CONFIG: /home/worker/model/oasislmf.json + volumes: + - ${SCENARIOS_PATH}/JBA/Philippines-Taiwan/combined-undefended/:/home/worker/model + - filestore-data:/shared-fs:rw + + ghana-E635900-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: JBA + OASIS_MODEL_ID: Ghana-E635900 + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_CONFIG: /home/worker/model/oasislmf.json + volumes: + - ${SCENARIOS_PATH}/JBA/Ghana/E635900/combined/:/home/worker/model + - filestore-data:/shared-fs:rw + + ghana-E689982-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: JBA + OASIS_MODEL_ID: Ghana-E689982 + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_CONFIG: /home/worker/model/oasislmf.json + volumes: + - ${SCENARIOS_PATH}/JBA/Ghana/E689982/combined/:/home/worker/model + - filestore-data:/shared-fs:rw + + ghana-E760461-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: JBA + OASIS_MODEL_ID: Ghana-E760461 + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_CONFIG: /home/worker/model/oasislmf.json + volumes: + - ${SCENARIOS_PATH}/JBA/Ghana/E760461/combined/:/home/worker/model + - filestore-data:/shared-fs:rw + + nepal-E151185-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: JBA + OASIS_MODEL_ID: Nepal-E151185 + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_CONFIG: /home/worker/model/oasislmf.json + volumes: + - ${SCENARIOS_PATH}/JBA/Nepal/E151185/combined/:/home/worker/model + - filestore-data:/shared-fs:rw + + nepal-E432557-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: JBA + OASIS_MODEL_ID: Nepal-E432557 + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_CONFIG: /home/worker/model/oasislmf.json + volumes: + - ${SCENARIOS_PATH}/JBA/Nepal/E432557/combined/:/home/worker/model + - filestore-data:/shared-fs:rw + + nepal-E505423-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: JBA + OASIS_MODEL_ID: Nepal-E505423 + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_CONFIG: /home/worker/model/oasislmf.json + volumes: + - ${SCENARIOS_PATH}/JBA/Nepal/E505423/combined/:/home/worker/model + - filestore-data:/shared-fs:rw + + us_hurricane-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: ARA + OASIS_MODEL_ID: US-Hurricane + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_CONFIG: /home/worker/model/oasislmf.json + volumes: + - ${SCENARIOS_PATH}/ARA/US_Hurricane/:/home/worker/model + - filestore-data:/shared-fs:rw + + france_hail-worker: + <<: *worker + environment: + <<: *worker-env + OASIS_MODEL_SUPPLIER_ID: IPE + OASIS_MODEL_ID: France-Hail + OASIS_MODEL_VERSION_ID: 'v2' + OASIS_RUN_MODE: v2 + OASIS_OASISLMF_CONFIG: /home/worker/model/oasislmf.json + volumes: + - ${SCENARIOS_PATH}/IPE/FranceHail/:/home/worker/model + - filestore-data:/shared-fs:rw From d119e17a104cd49152961229d7ee24df8437d968 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Wed, 26 Aug 2026 16:54:13 +0100 Subject: [PATCH 17/19] mounting for ui assets --- docker-compose.ui.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.ui.yml b/docker-compose.ui.yml index deec61f..0d58bbc 100644 --- a/docker-compose.ui.yml +++ b/docker-compose.ui.yml @@ -28,6 +28,7 @@ services: - ${UI_DEFAULTS:-./defaults/}:/usr/src/app/defaults/ - ${UI_CONFIG:-./ui-config.json}:/usr/src/app/ui-config.json:ro - ${UI_STREAMLIT:-./.streamlit/}:/usr/src/app/.streamlit:ro + - ${UI_ASSETS:-./ui_assets/}:/usr/src/app/ui_assets:ro healthcheck: test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8501/_stcore/health')"] interval: 30s From feccd7cb6f188101b58dd8afadae3eef3d28c8d1 Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:49:12 +0100 Subject: [PATCH 18/19] allow configurable traefik ports --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 24fd7e1..f411c5d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -77,9 +77,9 @@ services: - "--ping=true" - "--api.insecure=true" ports: - - "80:80" - - "443:8443" - - "8090:8080" + - "${OASIS_HTTP_PORT-80}:80" + - "${OASIS_HTTPS_PORT-443}:8443" + - "${TRAEFIK_DASHBOARD_PORT-8090}:8080" volumes: - ${DOCKER_SOCK:-/var/run/docker.sock}:/var/run/docker.sock:ro healthcheck: From 8dd584553878fc23c2ba6b9e8c2d5aa177b0a24f Mon Sep 17 00:00:00 2001 From: Vinul Wimalaweera <16556525+vinulw@users.noreply.github.com> Date: Tue, 1 Sep 2026 15:42:52 +0100 Subject: [PATCH 19/19] update README.md --- README.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index aa06eb1..e2de835 100644 --- a/README.md +++ b/README.md @@ -65,9 +65,16 @@ echo "127.0.0.1 ui.oasis.local" | sudo tee -a /etc/hosts ./install.sh ``` -The installer clones the PiWind demo model, processes OIDC templates (if -applicable), builds the UI image, and starts all services. It will prompt -before redeploying if a previous installation is detected. +The installer stages the model data for the selected model set, processes OIDC +templates (if applicable), pulls the images, and starts all services. It will +prompt before redeploying if a previous installation is detected. + +| Option | Description | +|--------|-------------| +| `-m`, `--model-set ` | Model set to deploy. Defaults to `MODEL_SET` from `.env`, or `piwind`. | +| `--build-ui` | Build the UI image locally instead of pulling `PYTHONUI_IMG:VERS_UI`. | +| `-u`, `--uninstall` | Bring the stack down and delete its volumes. | +| `-h`, `--help` | Show usage. | To tear everything down (removes containers and volumes): @@ -104,19 +111,82 @@ All services are reachable on port 80 via Traefik after a successful install: ## Docker Compose Architecture -The stack is assembled from multiple Compose files depending on auth type: +`install.sh` layers several Compose files into a single `docker compose` +command. The model workers live in a file of their own, so the core platform, +the UI and the auth stack stay model-agnostic: ``` Always loaded: - docker-compose.yml # Core platform: server, worker, databases, broker - docker-compose.ui.yml # Streamlit UI + Traefik reverse proxy + docker-compose.yml # Core platform: Traefik, server, websocket, databases, broker, filestore + docker-compose.models..yml # Model workers and model registration for one model set + docker-compose.ui.yml # Streamlit UI Conditionally loaded: - docker-compose.keycloak.yml # Keycloak + its PostgreSQL DB (API_AUTH_TYPE=keycloak) + docker-compose.keycloak.yml # Keycloak + its PostgreSQL DB (API_AUTH_TYPE=keycloak) docker-compose.authentik.yml # Authentik + its PostgreSQL DB (API_AUTH_TYPE=authentik) ``` -`install.sh` builds the correct `docker compose -f ... up` command automatically. +### Model sets — `docker-compose.models..yml` + +`docker-compose.yml` defines no model workers at all. Everything +model-specific is confined to a `docker-compose.models..yml` file: + +- one worker service per model, carrying its `OASIS_MODEL_SUPPLIER_ID`, + `OASIS_MODEL_ID`, `OASIS_MODEL_VERSION_ID` and `OASIS_RUN_MODE` +- the volume mount that supplies that model's data +- any `model-registration` job needed to register the models with the API + +Exactly one model set is loaded per deployment. Pick it with `-m/--model-set`, +or set `MODEL_SET` in `.env` so a bare `./install.sh` deploys it: + +```bash +./install.sh -m piwind # default: the PiWind demo model +./install.sh -m scenarios # PiWind plus the public scenario models +``` + +Model sets shipped in this repository: + +| File | Contents | +|------|----------| +| `docker-compose.models.piwind.yml` | A `model-registration` one-shot and `piwind-worker`, with model data from `./OasisPiWind/`. | +| `docker-compose.models.scenarios.yml` | `piwind-worker` plus the scenario workers (Impact Forecasting, JBA, ARA, IPE), with model data from `${SCENARIOS_PATH}`. | + +Because the split is by filename, adding a model set means dropping two new +files into the root directory — no edits to `docker-compose.yml`, `install.sh` +or the auth files: + +1. Write `docker-compose.models..yml` with the worker services. The + workers join the core stack, so they can depend on `server`, `celery-db` and + `broker` and mount the shared `filestore-data` volume directly. +2. Optionally add `get-.sh` to fetch the model data (see below). +3. Deploy with `./install.sh -m `. + +`install.sh --uninstall` passes *every* `docker-compose.models.*.yml` to +`docker compose down`, so the model workers are torn down whichever set was +deployed. + +#### Model data — `get-.sh` + +The Compose file describes how a model runs; the matching `get-.sh` +puts its data on disk. Before bringing the stack up, `install.sh` runs +`get-$MODEL_SET.sh` if it exists, and otherwise assumes the data is already in +place. + +| Script | What it does | +|--------|--------------| +| `get-piwind.sh` | Clones `OasisLMF/OasisPiWind` at `VERS_PIWIND` into `./OasisPiWind/`. No-ops once cloned. | +| `get-scenarios.sh` | Runs `get-piwind.sh`, clones `OasisLMF/Scenarios` into `$SCENARIOS_PATH`, then runs that repository's `get_s3_data_reduced.sh` to download the model files. Each step is skipped if it has already been done. | + +The scripts are idempotent and safe to run on their own, which is the easy way +to pre-stage model data on a server before deploying, or to refresh it without +a redeploy: + +```bash +SCENARIOS_PATH=/home/ubuntu/Scenarios ./get-scenarios.sh +``` + +When writing your own, keep it re-runnable: `install.sh` calls it on every +deploy, including redeploys over an existing installation. ## Key Environment Variables @@ -131,6 +201,25 @@ Conditionally loaded: See the `.env.*` templates for the full list with inline comments. +### Deployment overrides + +These variables decide *what* gets deployed and *what the UI shows*, rather +than how the platform is wired together. They are read straight from `.env` by +`install.sh` and the Compose files, and each falls back to the layout in this +repository — so a plain checkout still deploys unchanged with none of them set. +They are not in the `.env.*` templates; add the ones a deployment needs. + +| Variable | Default | Purpose | +|----------|---------|---------| +| `MODEL_SET` | `piwind` | Which `docker-compose.models..yml` / `get-.sh` pair to deploy. `-m/--model-set` overrides it for one run. | +| `SCENARIOS_PATH` | *(unset)* | Host directory holding the scenario model data. Required by the `scenarios` model set — both `get-scenarios.sh` and the worker mounts read it. | +| `UI_CONFIG` | `./ui-config.json` | The UI's config file: pages, post-login page, model-to-exposure map, footer, and `skip_login`. | +| `UI_DEFAULTS` | `./defaults/` | Per-model default analysis settings the UI pre-fills when creating an analysis. | +| `UI_ASSETS` | `./ui_assets/` | Files `UI_CONFIG` refers to as `ui_assets/` — footer text, logos, anything a deployment supplies. | +| `UI_STREAMLIT` | `./.streamlit/` | The Streamlit directory mounted read-only: `secrets.toml` and `config.toml`. | +| `PYTHONUI_IMG` / `VERS_UI` | `coreoasis/oasispythonui_app` / `latest` | UI image pulled at deploy time, and the tag `make build` / `make push` produce. | + + ## Adding Users ### Simple auth @@ -166,6 +255,10 @@ Usually first thing to try before anything is clearing browser cache/cookies for - The IdP database container must be healthy first: `docker compose ps`. - First startup can take 2–3 minutes while blueprints and realms are imported. +**`install.sh` exits with `no model set ''`** +- The model set has no `docker-compose.models..yml` in the root directory. + Check the spelling of `-m/--model-set` or `MODEL_SET`. + **Logs and status** ```bash docker compose ps # service health