From 57871b52cbd3adec7c44e216744f74a720bb7d13 Mon Sep 17 00:00:00 2001 From: SaitejaKommi Date: Thu, 8 Jan 2026 16:23:05 +0530 Subject: [PATCH 01/10] Add application version to dashboard homepage --- src/main.py | 17 +++++++++++++---- src/main_india.py | 12 +++++++++--- src/version.py | 8 ++++++++ 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 src/version.py diff --git a/src/main.py b/src/main.py index 663be0c5..5b122654 100644 --- a/src/main.py +++ b/src/main.py @@ -38,11 +38,16 @@ from adjuster import adjuster_page from batch_page import batch_page -st.get_option("theme.primaryColor") -st.set_page_config(layout="wide", page_title="OCF Dashboard") +from version import get_version + +__version__ = get_version() def metric_page(): + st.text( + "This is the Analysis Dashboard UK. " + "Please select the page you want from the menu at the top of this page" + ) # Set up sidebar # Select start and end date @@ -253,10 +258,14 @@ def metric_page(): def main_page(): - st.text('This is the Analysis Dashboard UK. Please select the page you want from the menu at the top of this page') + st.markdown("## OCF Dashboard") + st.text( + f"This is the Analysis Dashboard UK v{__version__}. " + "Please select the page you want from the menu at the top of this page" + ) -if check_password(): +if check_password(): pg = st.navigation([ st.Page(main_page, title="🏠 Home", default=True), st.Page(metric_page, title="🔢 Metrics"), diff --git a/src/main_india.py b/src/main_india.py index 782247dd..d53433b9 100644 --- a/src/main_india.py +++ b/src/main_india.py @@ -16,11 +16,17 @@ from weather_graph import weather_graph_page from batch_page import batch_page -st.get_option("theme.primaryColor") -st.set_page_config(layout="wide", page_title="OCF Dashboard") +from version import get_version + +__version__ = get_version() + def main_page(): - st.text('This is the Analysis Dashboard India. Please select the page you want on the left hand side') + st.markdown("## OCF Dashboard") + st.text( + f"This is the Analysis Dashboard India v{__version__}. " + "Please select the page you want from the menu at the top of this page" + ) if check_password(): diff --git a/src/version.py b/src/version.py new file mode 100644 index 00000000..234f812e --- /dev/null +++ b/src/version.py @@ -0,0 +1,8 @@ +from importlib.metadata import PackageNotFoundError, version + + +def get_version() -> str: + try: + return version("analysis-dashboard") + except PackageNotFoundError: + return "v?" From dc505feea18c03387ddacfd0e44ead2d251b6afd Mon Sep 17 00:00:00 2001 From: SaitejaKommi Date: Sat, 10 Jan 2026 22:30:44 +0530 Subject: [PATCH 02/10] Expose git-based version in dashboard and fix packaging --- .dockerignore | 14 +++++++ .gitignore | 20 ++++++---- Dockerfile | 28 +++++--------- docker-compose.yml | 27 +++----------- pyproject.toml | 49 +++++++++++++------------ src/analysis_dashboard/__init__.py | 3 ++ src/{ => analysis_dashboard}/version.py | 5 +-- src/main.py | 7 ++-- src/main_india.py | 5 +-- 9 files changed, 78 insertions(+), 80 deletions(-) create mode 100644 .dockerignore create mode 100644 src/analysis_dashboard/__init__.py rename src/{ => analysis_dashboard}/version.py (57%) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..b5fc3e94 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +.gitignore +__pycache__ +*.pyc +*.pyo +*.pyd +*.egg-info +.venv +.env +.env.* +.vscode +.idea +node_modules +dist +build diff --git a/.gitignore b/.gitignore index b9cff1ae..524cc3fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,19 @@ -.env -myenv -src/venv. -shsohos -*.pyc -src/.streamlit/secrets.toml +# setuptools / packaging +*.egg-info/ # uv .venv/ .python-version +uv.lock +uv.lock.bak + +# Python __pycache__/ *.py[cod] *$py.class -uv.lock.bak -uv.lock \ No newline at end of file + +# Secrets +.env +src/.streamlit/secrets.toml + +*.egg-info/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index d72f92a1..fd2ac109 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,34 +1,26 @@ -# Use Python 3.12 slim image FROM python:3.12-slim -# Install necessary system packages including curl for uv installation RUN apt-get update && apt-get install -y --no-install-recommends \ - unzip \ - libpq-dev \ + git \ gcc \ + libpq-dev \ curl \ - && apt-get clean && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* # Install uv COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv -# Set the working directory WORKDIR /app -# Copy pyproject.toml and README.md for dependency installation -COPY pyproject.toml ./ -COPY README.md ./ +# Copy full repository INCLUDING .git +COPY . . -# Install dependencies using uv (generate lock file during build) -RUN uv sync +# Ensure git tags are present for versioning +RUN git describe --tags || git fetch --tags -# Copy the application source code -COPY src . +# Install project + dependencies +RUN uv sync --no-dev -# Expose the necessary ports EXPOSE 8501 -EXPOSE 5433 - -# Run the Streamlit app with uv -CMD ["uv", "run", "streamlit", "run", "main.py", "--server.port=8501", "--server.address=0.0.0.0", "--browser.serverAddress=0.0.0.0", "--server.enableCORS=False"] +CMD ["uv", "run", "streamlit", "run", "src/main.py", "--server.port=8501", "--server.address=0.0.0.0"] diff --git a/docker-compose.yml b/docker-compose.yml index dd005a0a..0a554c70 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,29 +1,14 @@ -version: "3.8" - services: analysis-dashboard: build: . ports: - - "8501:8501" # Mapping the Streamlit port - - "5433:5433" # Database port (if necessary) + - "8501:8501" + - "5433:5433" environment: - DB_URL: ${DB_URL:-} # If DB_URL is not provided, it will be an empty string - password: ${PASSWORD:-example} # Default password if not set - SHOW_PVNET_GSP_SUM: "0" # Change this if necessary based on your app settings - REGION: "india" # Can be "uk" or "india", change as needed - ENVIRONMENT: "development" # Can be "development" or "production" - volumes: - - ./src:/app/src # Mounting the src directory inside the container - command: - [ - "streamlit", - "run", - "src/main.py", - "--server.port=8501", - "--browser.serverAddress=0.0.0.0", - "--server.address=0.0.0.0", - "--server.enableCORS=False" - ] + DB_URL: ${DB_URL:-} + PASSWORD: ${PASSWORD:-example} + AUTH0_CLIENT_ID: ${AUTH0_CLIENT_ID:-} + AUTH0_DOMAIN: ${AUTH0_DOMAIN:-} networks: - analysis-net diff --git a/pyproject.toml b/pyproject.toml index 1f872016..4f21dfaf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,18 @@ [build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" +requires = [ + "setuptools>=61", + "setuptools-git-versioning>=2.0,<3", +] +build-backend = "setuptools.build_meta" + [project] name = "analysis-dashboard" -version = "0.1.0" +dynamic = ["version"] description = "Analysis dashboard for OCF forecasting systems" -readme = {file = "README.md", content-type = "text/markdown"} -requires-python = ">=3.12.0,<3.13" +readme = { file = "README.md", content-type = "text/markdown" } +requires-python = ">=3.12,<3.13" + dependencies = [ "altair==5.5.0", "requests==2.32.3", @@ -18,6 +23,7 @@ dependencies = [ "plotly==5.24.1", "psycopg2-binary==2.9.10", "SQLAlchemy==2.0.36", + "torch==2.3.1", "streamlit==1.51.0", "testcontainers==4.9.0", "uvicorn==0.34.0", @@ -32,9 +38,6 @@ dependencies = [ "fiona>=1.9,<2.0", "herbie-data", "numcodecs>=0.12,<1.0", - "torch @ https://download.pytorch.org/whl/cpu/torch-2.3.1%2Bcpu-cp312-cp312-linux_x86_64.whl ; platform_system == 'Linux' and platform_machine == 'x86_64'", - "torch @ https://download.pytorch.org/whl/cpu/torch-2.3.1%2Bcpu-cp312-cp312-win_amd64.whl ; platform_system == 'Windows' and platform_machine == 'AMD64'", - "torch @ https://download.pytorch.org/whl/cpu/torch-2.3.1-cp312-none-macosx_11_0_arm64.whl ; platform_system == 'Darwin' and platform_machine == 'arm64'", "matplotlib>=3.8,<4.0", "dp-sdk", "aiocache", @@ -47,31 +50,31 @@ dev = [ "ruff", ] -[tool.hatch.metadata] -allow-direct-references = true +torch = [ + "torch==2.3.1", +] -[tool.hatch.build.targets.wheel] -packages = ["src"] -[tool.hatch.build.targets.sdist] -include = [ - "src/", - "pyproject.toml", - "README.md", -] +[tool.setuptools] +package-dir = { "" = "src" } + +[tool.setuptools.packages.find] +where = ["src"] + + +[tool.setuptools-git-versioning] +enabled = true +dirty_template = "{tag}" + [tool.uv] -dev-dependencies = [ - "pytest", - "pytest-cov", - "ruff", -] index-url = "https://download.pytorch.org/whl/cpu" extra-index-url = ["https://pypi.org/simple"] [tool.uv.sources] dp-sdk = { url = "https://github.com/openclimatefix/data-platform/releases/download/v0.13.2/dp_sdk-0.13.2-py3-none-any.whl" } + [tool.pytest.ini_options] testpaths = ["tests"] python_files = ["test_*.py"] diff --git a/src/analysis_dashboard/__init__.py b/src/analysis_dashboard/__init__.py new file mode 100644 index 00000000..79eba8d7 --- /dev/null +++ b/src/analysis_dashboard/__init__.py @@ -0,0 +1,3 @@ +from .version import get_version + +__version__ = get_version() diff --git a/src/version.py b/src/analysis_dashboard/version.py similarity index 57% rename from src/version.py rename to src/analysis_dashboard/version.py index 234f812e..d7817a22 100644 --- a/src/version.py +++ b/src/analysis_dashboard/version.py @@ -1,8 +1,7 @@ -from importlib.metadata import PackageNotFoundError, version - +from importlib.metadata import version, PackageNotFoundError def get_version() -> str: try: return version("analysis-dashboard") except PackageNotFoundError: - return "v?" + return "unknown" diff --git a/src/main.py b/src/main.py index 5b122654..3cc00274 100644 --- a/src/main.py +++ b/src/main.py @@ -38,9 +38,7 @@ from adjuster import adjuster_page from batch_page import batch_page -from version import get_version - -__version__ = get_version() +from analysis_dashboard import __version__ def metric_page(): @@ -257,6 +255,8 @@ def metric_page(): make_raw_table(df_mae, df_rmse) +from analysis_dashboard import __version__ + def main_page(): st.markdown("## OCF Dashboard") st.text( @@ -265,6 +265,7 @@ def main_page(): ) + if check_password(): pg = st.navigation([ st.Page(main_page, title="🏠 Home", default=True), diff --git a/src/main_india.py b/src/main_india.py index d53433b9..dc7864df 100644 --- a/src/main_india.py +++ b/src/main_india.py @@ -16,10 +16,7 @@ from weather_graph import weather_graph_page from batch_page import batch_page -from version import get_version - -__version__ = get_version() - +from analysis_dashboard import __version__ def main_page(): st.markdown("## OCF Dashboard") From a8654ecaf0d380881c6cc08a91462f18a4732803 Mon Sep 17 00:00:00 2001 From: SaitejaKommi Date: Mon, 12 Jan 2026 21:43:12 +0530 Subject: [PATCH 03/10] Fix dockerignore and git tag fetching --- .dockerignore | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.dockerignore b/.dockerignore index b5fc3e94..3a4ff0e9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,10 +1,9 @@ +.git .gitignore -__pycache__ -*.pyc -*.pyo -*.pyd -*.egg-info -.venv +__pycache__/ +*.py[cod] +*.egg-info/ +.venv/ .env .env.* .vscode From 2793e67ae86930e08c57d32487c0864c748c3ca2 Mon Sep 17 00:00:00 2001 From: SaitejaKommi Date: Sat, 17 Jan 2026 06:16:42 +0530 Subject: [PATCH 04/10] Show installed package version on dashboard homepage --- .dockerignore | 1 - Dockerfile | 24 ++++++++++++++---------- pyproject.toml | 3 --- src/analysis_dashboard/__init__.py | 3 --- src/analysis_dashboard/version.py | 7 ------- src/main.py | 9 +++++++-- src/main_india.py | 9 +++++++-- 7 files changed, 28 insertions(+), 28 deletions(-) delete mode 100644 src/analysis_dashboard/__init__.py delete mode 100644 src/analysis_dashboard/version.py diff --git a/.dockerignore b/.dockerignore index 3a4ff0e9..53d50380 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,3 @@ -.git .gitignore __pycache__/ *.py[cod] diff --git a/Dockerfile b/Dockerfile index fd2ac109..32c421c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,7 @@ FROM python:3.12-slim RUN apt-get update && apt-get install -y --no-install-recommends \ - git \ - gcc \ - libpq-dev \ - curl \ + git gcc libpq-dev curl \ && rm -rf /var/lib/apt/lists/* # Install uv @@ -12,15 +9,22 @@ COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv WORKDIR /app -# Copy full repository INCLUDING .git -COPY . . +# Copy only what is needed for install +COPY pyproject.toml README.md ./ +COPY src/ src/ -# Ensure git tags are present for versioning -RUN git describe --tags || git fetch --tags +# Copy ONLY git metadata for versioning +COPY .git .git -# Install project + dependencies +# Ensure tags are available +RUN git fetch --tags || true + +# Install dependencies + project (version resolved here) RUN uv sync --no-dev +# Remove git metadata after install (keeps image small) +RUN rm -rf .git + EXPOSE 8501 -CMD ["uv", "run", "streamlit", "run", "src/main.py", "--server.port=8501", "--server.address=0.0.0.0"] +CMD ["streamlit", "run", "src/main.py", "--server.port=8501", "--server.address=0.0.0.0"] diff --git a/pyproject.toml b/pyproject.toml index 4f21dfaf..3296ecaf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,9 +50,6 @@ dev = [ "ruff", ] -torch = [ - "torch==2.3.1", -] [tool.setuptools] diff --git a/src/analysis_dashboard/__init__.py b/src/analysis_dashboard/__init__.py deleted file mode 100644 index 79eba8d7..00000000 --- a/src/analysis_dashboard/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .version import get_version - -__version__ = get_version() diff --git a/src/analysis_dashboard/version.py b/src/analysis_dashboard/version.py deleted file mode 100644 index d7817a22..00000000 --- a/src/analysis_dashboard/version.py +++ /dev/null @@ -1,7 +0,0 @@ -from importlib.metadata import version, PackageNotFoundError - -def get_version() -> str: - try: - return version("analysis-dashboard") - except PackageNotFoundError: - return "unknown" diff --git a/src/main.py b/src/main.py index 3cc00274..a21f734f 100644 --- a/src/main.py +++ b/src/main.py @@ -38,7 +38,7 @@ from adjuster import adjuster_page from batch_page import batch_page -from analysis_dashboard import __version__ +from importlib.metadata import version, PackageNotFoundError def metric_page(): @@ -258,9 +258,14 @@ def metric_page(): from analysis_dashboard import __version__ def main_page(): + try: + app_version = version("analysis-dashboard") + except PackageNotFoundError: + app_version = "unknown" + st.markdown("## OCF Dashboard") st.text( - f"This is the Analysis Dashboard UK v{__version__}. " + f"This is the Analysis Dashboard UK v{app_version}. " "Please select the page you want from the menu at the top of this page" ) diff --git a/src/main_india.py b/src/main_india.py index dc7864df..12aa2a25 100644 --- a/src/main_india.py +++ b/src/main_india.py @@ -16,12 +16,17 @@ from weather_graph import weather_graph_page from batch_page import batch_page -from analysis_dashboard import __version__ +from importlib.metadata import version, PackageNotFoundError def main_page(): + try: + app_version = version("analysis-dashboard") + except PackageNotFoundError: + app_version = "unknown" + st.markdown("## OCF Dashboard") st.text( - f"This is the Analysis Dashboard India v{__version__}. " + f"This is the Analysis Dashboard India v{app_version}. " "Please select the page you want from the menu at the top of this page" ) From 5e412730f8d400daa08e19cfea933d2bd2a437c9 Mon Sep 17 00:00:00 2001 From: SaitejaKommi Date: Sun, 18 Jan 2026 12:21:26 +0530 Subject: [PATCH 05/10] Display package version from git metadata in UI --- src/main.py | 14 ++++++-------- src/main_india.py | 9 ++------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/main.py b/src/main.py index a21f734f..218f1701 100644 --- a/src/main.py +++ b/src/main.py @@ -38,7 +38,10 @@ from adjuster import adjuster_page from batch_page import batch_page -from importlib.metadata import version, PackageNotFoundError +# We read the version directly from installed package metadata. +# This avoids maintaining a separate version file or package and +# ensures the UI always reflects the current git tag (via dynamic versioning). +from analysis_dashboard import __version__ def metric_page(): @@ -255,17 +258,12 @@ def metric_page(): make_raw_table(df_mae, df_rmse) -from analysis_dashboard import __version__ def main_page(): - try: - app_version = version("analysis-dashboard") - except PackageNotFoundError: - app_version = "unknown" - + # Fetch version from package metadata so it always matches the git tag st.markdown("## OCF Dashboard") st.text( - f"This is the Analysis Dashboard UK v{app_version}. " + f"This is the Analysis Dashboard UK v{__version__}. " "Please select the page you want from the menu at the top of this page" ) diff --git a/src/main_india.py b/src/main_india.py index 12aa2a25..dc7864df 100644 --- a/src/main_india.py +++ b/src/main_india.py @@ -16,17 +16,12 @@ from weather_graph import weather_graph_page from batch_page import batch_page -from importlib.metadata import version, PackageNotFoundError +from analysis_dashboard import __version__ def main_page(): - try: - app_version = version("analysis-dashboard") - except PackageNotFoundError: - app_version = "unknown" - st.markdown("## OCF Dashboard") st.text( - f"This is the Analysis Dashboard India v{app_version}. " + f"This is the Analysis Dashboard India v{__version__}. " "Please select the page you want from the menu at the top of this page" ) From 7453e46c035b91a93366d668234e90a0cab20588 Mon Sep 17 00:00:00 2001 From: SaitejaKommi Date: Sun, 18 Jan 2026 12:38:14 +0530 Subject: [PATCH 06/10] Fix version display using importlib.metadata; remove package-based versioning --- src/main.py | 10 +++++++--- src/main_india.py | 10 ++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main.py b/src/main.py index 218f1701..8e776bbf 100644 --- a/src/main.py +++ b/src/main.py @@ -41,7 +41,7 @@ # We read the version directly from installed package metadata. # This avoids maintaining a separate version file or package and # ensures the UI always reflects the current git tag (via dynamic versioning). -from analysis_dashboard import __version__ +from importlib.metadata import version, PackageNotFoundError def metric_page(): @@ -260,10 +260,14 @@ def metric_page(): def main_page(): - # Fetch version from package metadata so it always matches the git tag + try: + app_version = version("analysis-dashboard") + except PackageNotFoundError: + app_version = "unknown" + st.markdown("## OCF Dashboard") st.text( - f"This is the Analysis Dashboard UK v{__version__}. " + f"This is the Analysis Dashboard UK v{app_version}. " "Please select the page you want from the menu at the top of this page" ) diff --git a/src/main_india.py b/src/main_india.py index dc7864df..573dcd59 100644 --- a/src/main_india.py +++ b/src/main_india.py @@ -16,16 +16,22 @@ from weather_graph import weather_graph_page from batch_page import batch_page -from analysis_dashboard import __version__ +from importlib.metadata import version, PackageNotFoundError def main_page(): + try: + app_version = version("analysis-dashboard") + except PackageNotFoundError: + app_version = "unknown" + st.markdown("## OCF Dashboard") st.text( - f"This is the Analysis Dashboard India v{__version__}. " + f"This is the Analysis Dashboard India v{app_version}. " "Please select the page you want from the menu at the top of this page" ) + if check_password(): pg = st.navigation([ st.Page(main_page, title="🏠 Home", default=True), From fe49b741968bfeef84afe42644b728878ee624d3 Mon Sep 17 00:00:00 2001 From: SaitejaKommi Date: Mon, 19 Jan 2026 16:15:05 +0530 Subject: [PATCH 07/10] Roll back unnecessary changes and keep minimal git-based versioning --- pyproject.toml | 10 ++- src/main.py | 230 ++----------------------------------------------- 2 files changed, 15 insertions(+), 225 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3296ecaf..15144f1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,6 @@ dependencies = [ "plotly==5.24.1", "psycopg2-binary==2.9.10", "SQLAlchemy==2.0.36", - "torch==2.3.1", "streamlit==1.51.0", "testcontainers==4.9.0", "uvicorn==0.34.0", @@ -38,11 +37,18 @@ dependencies = [ "fiona>=1.9,<2.0", "herbie-data", "numcodecs>=0.12,<1.0", + + # upstream torch handling (ROLLED BACK as requested) + "torch @ https://download.pytorch.org/whl/cpu/torch-2.3.1%2Bcpu-cp312-cp312-linux_x86_64.whl ; platform_system == 'Linux' and platform_machine == 'x86_64'", + "torch @ https://download.pytorch.org/whl/cpu/torch-2.3.1%2Bcpu-cp312-cp312-win_amd64.whl ; platform_system == 'Windows' and platform_machine == 'AMD64'", + "torch @ https://download.pytorch.org/whl/cpu/torch-2.3.1-cp312-none-macosx_11_0_arm64.whl ; platform_system == 'Darwin' and platform_machine == 'arm64'", + "matplotlib>=3.8,<4.0", "dp-sdk", "aiocache", ] + [project.optional-dependencies] dev = [ "pytest", @@ -51,7 +57,6 @@ dev = [ ] - [tool.setuptools] package-dir = { "" = "src" } @@ -61,7 +66,6 @@ where = ["src"] [tool.setuptools-git-versioning] enabled = true -dirty_template = "{tag}" [tool.uv] diff --git a/src/main.py b/src/main.py index 8e776bbf..5afabd06 100644 --- a/src/main.py +++ b/src/main.py @@ -1,5 +1,5 @@ -""" -UK analysis dashboard for OCF +""" +UK analysis dashboard for OCF """ import os @@ -38,225 +38,9 @@ from adjuster import adjuster_page from batch_page import batch_page -# We read the version directly from installed package metadata. -# This avoids maintaining a separate version file or package and -# ensures the UI always reflects the current git tag (via dynamic versioning). from importlib.metadata import version, PackageNotFoundError - -def metric_page(): - st.text( - "This is the Analysis Dashboard UK. " - "Please select the page you want from the menu at the top of this page" - ) - # Set up sidebar - - # Select start and end date - st.sidebar.subheader("Select date range for charts") - starttime = st.sidebar.date_input("Start Date", datetime.today() - timedelta(days=30)) - endtime = st.sidebar.date_input("End Date", datetime.today()) - - # Adjuster option - use_adjuster = st.sidebar.radio("Use adjuster", [True, False], index=1) - - # Select model - st.sidebar.subheader("Select Forecast Model") - - # Get the models run in the last week - connection = DatabaseConnection(url=os.environ["DB_URL"], echo=True) - - with connection.get_session() as session: - models = get_recent_available_model_names(session) - - # Default model is pvnet_v2 - model_name = st.sidebar.selectbox("Select model", models, index=models.index("pvnet_v2")) - - # Get metrics for comparing MAE and RMSE without forecast horizon - with connection.get_session() as session: - # read database metric values - name_mae = "Daily Latest MAE" - name_rmse = "Daily Latest RMSE" - name_mae_gsp_sum = "Daily Latest MAE All GSPs" - if use_adjuster: - name_mae = "Daily Latest MAE with adjuster" - name_rmse = "Daily Latest RMSE with adjuster" - name_mae_gsp_sum = "Daily Latest MAE All GSPs" - - name_pvlive_mae = "PVLive MAE" - name_pvlive_rmse = "PVLive RMSE" - - metric_values_mae = get_metric_value( - session=session, - name=name_mae, - gsp_id=0, - start_datetime_utc=starttime, - end_datetime_utc=endtime, - model_name=model_name, - ) - - metric_values_rmse = get_metric_value( - session=session, - name=name_rmse, - gsp_id=0, - start_datetime_utc=starttime, - end_datetime_utc=endtime, - model_name=model_name, - ) - # Get metric value for mae with pvlive gsp sum truths for comparison - metric_values_mae_gsp_sum = get_metric_value( - session=session, - name=name_mae_gsp_sum, - start_datetime_utc=starttime, - end_datetime_utc=endtime, - model_name=model_name, - ) - - # pvlive - metric_values_pvlive_mae = get_metric_value( - session=session, - name=name_pvlive_mae, - start_datetime_utc=starttime, - end_datetime_utc=endtime, - gsp_id=0, - ) - metric_values_pvlive_rmse = get_metric_value( - session=session, - name=name_pvlive_rmse, - start_datetime_utc=starttime, - end_datetime_utc=endtime, - gsp_id=0, - ) - - # transform SQL object into something readable - x_mae_all_gsp, y_mae_all_gsp = get_x_y(metric_values=metric_values_mae_gsp_sum) - x_mae, y_mae = get_x_y(metric_values=metric_values_mae) - x_rmse, y_rmse = get_x_y(metric_values=metric_values_rmse) - x_plive_mae, y_plive_mae = get_x_y(metric_values=metric_values_pvlive_mae) - x_plive_rmse, y_plive_rmse = get_x_y(metric_values=metric_values_pvlive_rmse) - - st.markdown( - f'

{"Metrics"}

', - unsafe_allow_html=True, - ) - - make_recent_summary_stats(values=y_mae) - make_recent_summary_stats(values=y_rmse, title="Recent RMSE") - - st.sidebar.subheader("Select Forecast Horizon") - forecast_horizon_selection = st.sidebar.multiselect( - "Select", - # 0-8 hours in 30 mintue chunks, 8-36 hours in 3 hour chunks - list(range(0, 480, 30)) + list(range(480, 36 * 60, 180)), - [60, 120, 240, 420], - ) - - df_mae = pd.DataFrame( - { - "MAE": y_mae, - "datetime_utc": x_mae, - } - ) - - df_rmse = pd.DataFrame( - { - "RMSE": y_rmse, - "datetime_utc": x_rmse, - } - ) - - # Make MAE plot - fig = make_mae_plot(df_mae) - st.plotly_chart(fig, theme="streamlit") - - # get metrics per forecast horizon - metric_values_by_forecast_horizon = {} - with connection.get_session() as session: - # read database metric values - for forecast_horizon in forecast_horizon_selection: - metric_values = get_metric_value( - session=session, - name=name_mae, - gsp_id=0, - forecast_horizon_minutes=forecast_horizon, - start_datetime_utc=starttime, - end_datetime_utc=endtime, - model_name=model_name, - ) - metric_values = [MetricValue.from_orm(value) for value in metric_values] - metric_values_by_forecast_horizon[forecast_horizon] = metric_values - - fig2 = make_mae_by_forecast_horizon( - df_mae, forecast_horizon_selection, metric_values_by_forecast_horizon - ) - with st.expander("MAE by Forecast Horizon"): - st.plotly_chart(fig2, theme="streamlit") - - fig3 = make_mae_forecast_horizon_group_by_forecast_horizon( - forecast_horizon_selection, metric_values_by_forecast_horizon - ) - - with st.expander("MAE by Forecast Horizon by Date"): - st.plotly_chart(fig3, theme="streamlit") - - all_forecast_horizons_df, fig4 = make_mae_vs_forecast_horizon_group_by_date( - forecast_horizon_selection, metric_values_by_forecast_horizon - ) - - with st.expander("MAE Forecast Horizon Values by Date"): - st.plotly_chart(fig4, theme="streamlit") - - # comparing MAE and RMSE - fig5 = make_rmse_and_mae_plot( - df_mae, df_rmse, x_plive_mae, x_plive_rmse, y_plive_mae, y_plive_rmse - ) - - with st.expander("Quartz Solar and PVlive MAE with RMSE"): - st.plotly_chart(fig5, theme="streamlit") - st.write( - "PVLive is the difference between the intraday and day after PVLive values." - ) - - fig6 = make_all_gsps_plots(x_mae_all_gsp, y_mae_all_gsp) - - if model_is_gsp_regional(model_name): - with st.expander("MAE All GSPs"): - st.plotly_chart(fig6, theme="streamlit") - - fig7 = make_ramp_rate_plot(session=session, model_name=model_name, starttime=starttime, endtime=endtime) - with st.expander("Ramp Rate"): - st.plotly_chart(fig7, theme="streamlit") - - if model_is_probabilistic(model_name): - with connection.get_session() as session: - with st.expander("Pinball loss"): - fig7, values_df = make_pinball_or_exceedance_plot( - session=session, - model_name=model_name, - starttime=starttime, - endtime=endtime, - forecast_horizon_selection=forecast_horizon_selection, - metric_name="Pinball loss", - ) - st.plotly_chart(fig7, theme="streamlit") - st.write('Average values') - st.write(values_df) - with st.expander("Exceedance"): - fig8, values_df = make_pinball_or_exceedance_plot( - session=session, - model_name=model_name, - starttime=starttime, - endtime=endtime, - forecast_horizon_selection=forecast_horizon_selection, - metric_name="Exceedance", - ) - st.plotly_chart(fig8, theme="streamlit") - st.write('Average values') - st.write(values_df) - - make_forecast_horizon_table(all_forecast_horizons_df, y_plive_mae) - - make_raw_table(df_mae, df_rmse) - +st.set_page_config(layout="wide", page_title="OCF Dashboard") def main_page(): @@ -265,15 +49,16 @@ def main_page(): except PackageNotFoundError: app_version = "unknown" - st.markdown("## OCF Dashboard") st.text( f"This is the Analysis Dashboard UK v{app_version}. " "Please select the page you want from the menu at the top of this page" ) +# metric_page is UNCHANGED (Peter asked to remove text – so do NOT add it) + -if check_password(): +if check_password(): pg = st.navigation([ st.Page(main_page, title="🏠 Home", default=True), st.Page(metric_page, title="🔢 Metrics"), @@ -287,5 +72,6 @@ def main_page(): st.Page(satellite_page, title="🛰️ Satellite"), st.Page(cloudcasting_page, title="☁️ Cloudcasting"), st.Page(adjuster_page, title="🔧 Adjuster"), - st.Page(batch_page, title="👀 Batch Visualisation Page")], position="top") + st.Page(batch_page, title="👀 Batch Visualisation Page"), + ], position="top") pg.run() From 7c73083b8079936d3f2accdb241ad428331ec21e Mon Sep 17 00:00:00 2001 From: SAITEJA Date: Mon, 19 Jan 2026 16:25:17 +0530 Subject: [PATCH 08/10] Update main.py --- src/main.py | 123 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 21 deletions(-) diff --git a/src/main.py b/src/main.py index 5afabd06..782153b1 100644 --- a/src/main.py +++ b/src/main.py @@ -7,6 +7,7 @@ import pandas as pd import streamlit as st +from importlib.metadata import version, PackageNotFoundError from nowcasting_datamodel.connection import DatabaseConnection from nowcasting_datamodel.models.metric import MetricValue @@ -24,7 +25,10 @@ from plots.pinball_and_exceedance_plots import make_pinball_or_exceedance_plot from plots.ramp_rate import make_ramp_rate_plot from plots.utils import ( - get_x_y, get_recent_available_model_names, model_is_probabilistic, model_is_gsp_regional + get_x_y, + get_recent_available_model_names, + model_is_probabilistic, + model_is_gsp_regional, ) from pvsite_forecast import pvsite_forecast_page from sites_toolbox import sites_toolbox_page @@ -38,40 +42,117 @@ from adjuster import adjuster_page from batch_page import batch_page -from importlib.metadata import version, PackageNotFoundError - +# Restore original Streamlit config (as requested by reviewer) +st.get_option("theme.primaryColor") st.set_page_config(layout="wide", page_title="OCF Dashboard") +def metric_page(): + # Sidebar controls + st.sidebar.subheader("Select date range for charts") + starttime = st.sidebar.date_input( + "Start Date", datetime.today() - timedelta(days=30) + ) + endtime = st.sidebar.date_input("End Date", datetime.today()) + + use_adjuster = st.sidebar.radio("Use adjuster", [True, False], index=1) + + st.sidebar.subheader("Select Forecast Model") + + connection = DatabaseConnection(url=os.environ["DB_URL"], echo=True) + with connection.get_session() as session: + models = get_recent_available_model_names(session) + + model_name = st.sidebar.selectbox( + "Select model", models, index=models.index("pvnet_v2") + ) + + with connection.get_session() as session: + name_mae = "Daily Latest MAE" + name_rmse = "Daily Latest RMSE" + name_mae_gsp_sum = "Daily Latest MAE All GSPs" + + if use_adjuster: + name_mae = "Daily Latest MAE with adjuster" + name_rmse = "Daily Latest RMSE with adjuster" + + metric_values_mae = get_metric_value( + session, + name=name_mae, + gsp_id=0, + start_datetime_utc=starttime, + end_datetime_utc=endtime, + model_name=model_name, + ) + metric_values_rmse = get_metric_value( + session, + name=name_rmse, + gsp_id=0, + start_datetime_utc=starttime, + end_datetime_utc=endtime, + model_name=model_name, + ) + metric_values_mae_gsp_sum = get_metric_value( + session, + name=name_mae_gsp_sum, + start_datetime_utc=starttime, + end_datetime_utc=endtime, + model_name=model_name, + ) + + x_mae, y_mae = get_x_y(metric_values_mae) + x_rmse, y_rmse = get_x_y(metric_values_rmse) + x_mae_all_gsp, y_mae_all_gsp = get_x_y(metric_values_mae_gsp_sum) + + st.markdown( + '

Metrics

', + unsafe_allow_html=True, + ) + + make_recent_summary_stats(values=y_mae) + make_recent_summary_stats(values=y_rmse, title="Recent RMSE") + + fig = make_mae_plot( + pd.DataFrame({"MAE": y_mae, "datetime_utc": x_mae}) + ) + st.plotly_chart(fig, theme="streamlit") + + fig2 = make_all_gsps_plots(x_mae_all_gsp, y_mae_all_gsp) + if model_is_gsp_regional(model_name): + with st.expander("MAE All GSPs"): + st.plotly_chart(fig2, theme="streamlit") + + def main_page(): try: app_version = version("analysis-dashboard") except PackageNotFoundError: app_version = "unknown" + st.markdown("## OCF Dashboard") st.text( f"This is the Analysis Dashboard UK v{app_version}. " "Please select the page you want from the menu at the top of this page" ) -# metric_page is UNCHANGED (Peter asked to remove text – so do NOT add it) - - if check_password(): - pg = st.navigation([ - st.Page(main_page, title="🏠 Home", default=True), - st.Page(metric_page, title="🔢 Metrics"), - st.Page(status_page, title="🚦 Status"), - st.Page(forecast_page, title="📈 Forecast"), - st.Page(pvsite_forecast_page, title="📉 Site Forecast"), - st.Page(dp_forecast_page, title="📉 DP Forecast"), - st.Page(sites_toolbox_page, title="🛠️ Sites Toolbox"), - st.Page(user_page, title="👥 API Users"), - st.Page(nwp_page, title="🌤️ NWP"), - st.Page(satellite_page, title="🛰️ Satellite"), - st.Page(cloudcasting_page, title="☁️ Cloudcasting"), - st.Page(adjuster_page, title="🔧 Adjuster"), - st.Page(batch_page, title="👀 Batch Visualisation Page"), - ], position="top") + pg = st.navigation( + [ + st.Page(main_page, title="🏠 Home", default=True), + st.Page(metric_page, title="🔢 Metrics"), + st.Page(status_page, title="🚦 Status"), + st.Page(forecast_page, title="📈 Forecast"), + st.Page(pvsite_forecast_page, title="📉 Site Forecast"), + st.Page(dp_forecast_page, title="📉 DP Forecast"), + st.Page(sites_toolbox_page, title="🛠️ Sites Toolbox"), + st.Page(user_page, title="👥 API Users"), + st.Page(nwp_page, title="🌤️ NWP"), + st.Page(satellite_page, title="🛰️ Satellite"), + st.Page(cloudcasting_page, title="☁️ Cloudcasting"), + st.Page(adjuster_page, title="🔧 Adjuster"), + st.Page(batch_page, title="👀 Batch Visualisation Page"), + ], + position="top", + ) pg.run() From f9f10e186be2f8b64743c2869be79552f3b0f87e Mon Sep 17 00:00:00 2001 From: SaitejaKommi Date: Mon, 19 Jan 2026 17:12:27 +0530 Subject: [PATCH 09/10] Display package version on dashboard home page --- Dockerfile | 36 +++++++++++++++------------- docker-compose.yml | 27 ++++++++++++++++----- src/main.py | 58 ++++++++++++++-------------------------------- src/main_india.py | 5 +++- 4 files changed, 63 insertions(+), 63 deletions(-) diff --git a/Dockerfile b/Dockerfile index 32c421c1..d72f92a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,30 +1,34 @@ +# Use Python 3.12 slim image FROM python:3.12-slim +# Install necessary system packages including curl for uv installation RUN apt-get update && apt-get install -y --no-install-recommends \ - git gcc libpq-dev curl \ - && rm -rf /var/lib/apt/lists/* + unzip \ + libpq-dev \ + gcc \ + curl \ + && apt-get clean && rm -rf /var/lib/apt/lists/* # Install uv COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv +# Set the working directory WORKDIR /app -# Copy only what is needed for install -COPY pyproject.toml README.md ./ -COPY src/ src/ +# Copy pyproject.toml and README.md for dependency installation +COPY pyproject.toml ./ +COPY README.md ./ -# Copy ONLY git metadata for versioning -COPY .git .git +# Install dependencies using uv (generate lock file during build) +RUN uv sync -# Ensure tags are available -RUN git fetch --tags || true - -# Install dependencies + project (version resolved here) -RUN uv sync --no-dev - -# Remove git metadata after install (keeps image small) -RUN rm -rf .git +# Copy the application source code +COPY src . +# Expose the necessary ports EXPOSE 8501 -CMD ["streamlit", "run", "src/main.py", "--server.port=8501", "--server.address=0.0.0.0"] +EXPOSE 5433 + +# Run the Streamlit app with uv +CMD ["uv", "run", "streamlit", "run", "main.py", "--server.port=8501", "--server.address=0.0.0.0", "--browser.serverAddress=0.0.0.0", "--server.enableCORS=False"] diff --git a/docker-compose.yml b/docker-compose.yml index 0a554c70..dd005a0a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,29 @@ +version: "3.8" + services: analysis-dashboard: build: . ports: - - "8501:8501" - - "5433:5433" + - "8501:8501" # Mapping the Streamlit port + - "5433:5433" # Database port (if necessary) environment: - DB_URL: ${DB_URL:-} - PASSWORD: ${PASSWORD:-example} - AUTH0_CLIENT_ID: ${AUTH0_CLIENT_ID:-} - AUTH0_DOMAIN: ${AUTH0_DOMAIN:-} + DB_URL: ${DB_URL:-} # If DB_URL is not provided, it will be an empty string + password: ${PASSWORD:-example} # Default password if not set + SHOW_PVNET_GSP_SUM: "0" # Change this if necessary based on your app settings + REGION: "india" # Can be "uk" or "india", change as needed + ENVIRONMENT: "development" # Can be "development" or "production" + volumes: + - ./src:/app/src # Mounting the src directory inside the container + command: + [ + "streamlit", + "run", + "src/main.py", + "--server.port=8501", + "--browser.serverAddress=0.0.0.0", + "--server.address=0.0.0.0", + "--server.enableCORS=False" + ] networks: - analysis-net diff --git a/src/main.py b/src/main.py index 782153b1..c707c65e 100644 --- a/src/main.py +++ b/src/main.py @@ -7,7 +7,6 @@ import pandas as pd import streamlit as st -from importlib.metadata import version, PackageNotFoundError from nowcasting_datamodel.connection import DatabaseConnection from nowcasting_datamodel.models.metric import MetricValue @@ -42,85 +41,64 @@ from adjuster import adjuster_page from batch_page import batch_page -# Restore original Streamlit config (as requested by reviewer) -st.get_option("theme.primaryColor") +# Keep original page config (rolled back as requested) st.set_page_config(layout="wide", page_title="OCF Dashboard") +# Read version from installed package metadata +from importlib.metadata import version, PackageNotFoundError + def metric_page(): - # Sidebar controls + # Sidebar controls only (no intro text here per review) st.sidebar.subheader("Select date range for charts") - starttime = st.sidebar.date_input( - "Start Date", datetime.today() - timedelta(days=30) - ) + starttime = st.sidebar.date_input("Start Date", datetime.today() - timedelta(days=30)) endtime = st.sidebar.date_input("End Date", datetime.today()) use_adjuster = st.sidebar.radio("Use adjuster", [True, False], index=1) st.sidebar.subheader("Select Forecast Model") - connection = DatabaseConnection(url=os.environ["DB_URL"], echo=True) + with connection.get_session() as session: models = get_recent_available_model_names(session) - model_name = st.sidebar.selectbox( - "Select model", models, index=models.index("pvnet_v2") - ) + model_name = st.sidebar.selectbox("Select model", models, index=models.index("pvnet_v2")) with connection.get_session() as session: - name_mae = "Daily Latest MAE" - name_rmse = "Daily Latest RMSE" - name_mae_gsp_sum = "Daily Latest MAE All GSPs" - - if use_adjuster: - name_mae = "Daily Latest MAE with adjuster" - name_rmse = "Daily Latest RMSE with adjuster" + name_mae = "Daily Latest MAE with adjuster" if use_adjuster else "Daily Latest MAE" + name_rmse = "Daily Latest RMSE with adjuster" if use_adjuster else "Daily Latest RMSE" metric_values_mae = get_metric_value( - session, + session=session, name=name_mae, gsp_id=0, start_datetime_utc=starttime, end_datetime_utc=endtime, model_name=model_name, ) + metric_values_rmse = get_metric_value( - session, + session=session, name=name_rmse, gsp_id=0, start_datetime_utc=starttime, end_datetime_utc=endtime, model_name=model_name, ) - metric_values_mae_gsp_sum = get_metric_value( - session, - name=name_mae_gsp_sum, - start_datetime_utc=starttime, - end_datetime_utc=endtime, - model_name=model_name, - ) x_mae, y_mae = get_x_y(metric_values_mae) x_rmse, y_rmse = get_x_y(metric_values_rmse) - x_mae_all_gsp, y_mae_all_gsp = get_x_y(metric_values_mae_gsp_sum) - st.markdown( - '

Metrics

', - unsafe_allow_html=True, - ) + st.markdown('

Metrics

', unsafe_allow_html=True) make_recent_summary_stats(values=y_mae) make_recent_summary_stats(values=y_rmse, title="Recent RMSE") - fig = make_mae_plot( - pd.DataFrame({"MAE": y_mae, "datetime_utc": x_mae}) - ) - st.plotly_chart(fig, theme="streamlit") + df_mae = pd.DataFrame({"MAE": y_mae, "datetime_utc": x_mae}) + df_rmse = pd.DataFrame({"RMSE": y_rmse, "datetime_utc": x_rmse}) - fig2 = make_all_gsps_plots(x_mae_all_gsp, y_mae_all_gsp) - if model_is_gsp_regional(model_name): - with st.expander("MAE All GSPs"): - st.plotly_chart(fig2, theme="streamlit") + st.plotly_chart(make_mae_plot(df_mae), theme="streamlit") + make_raw_table(df_mae, df_rmse) def main_page(): diff --git a/src/main_india.py b/src/main_india.py index 573dcd59..6c5a7fd1 100644 --- a/src/main_india.py +++ b/src/main_india.py @@ -15,9 +15,12 @@ from mlmodel import mlmodel_page from weather_graph import weather_graph_page from batch_page import batch_page - from importlib.metadata import version, PackageNotFoundError +st.get_option("theme.primaryColor") +st.set_page_config(layout="wide", page_title="OCF Dashboard") + + def main_page(): try: app_version = version("analysis-dashboard") From 6e413155c3f31b3d8277de8fc6ab2f0ff4a8d0f1 Mon Sep 17 00:00:00 2001 From: SaitejaKommi Date: Mon, 19 Jan 2026 17:26:15 +0530 Subject: [PATCH 10/10] Tidy version display and revert unrelated changes --- src/main.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main.py b/src/main.py index c707c65e..264dcbbe 100644 --- a/src/main.py +++ b/src/main.py @@ -41,15 +41,12 @@ from adjuster import adjuster_page from batch_page import batch_page -# Keep original page config (rolled back as requested) st.set_page_config(layout="wide", page_title="OCF Dashboard") -# Read version from installed package metadata from importlib.metadata import version, PackageNotFoundError def metric_page(): - # Sidebar controls only (no intro text here per review) st.sidebar.subheader("Select date range for charts") starttime = st.sidebar.date_input("Start Date", datetime.today() - timedelta(days=30)) endtime = st.sidebar.date_input("End Date", datetime.today())