Skip to content
This repository was archived by the owner on Sep 29, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,9 @@ ENV PATH="$PATH:/$USER/.local/bin" \
PYTHONUSERBASE="/$USER/.local"

# install devcontainer requirements
RUN pip install -e .[dev,test]

RUN pip install .[dev,test]
# install docs requirements
RUN pip install --no-cache-dir -r docs/requirements.txt

# install streamlit requirements
RUN pip install --no-cache-dir -r streamlit_app/requirements.txt
# install source packages as editable
RUN pip install -e ./pems_streamlit -e ./pems_web
3 changes: 1 addition & 2 deletions .github/workflows/tests-pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ jobs:

- name: Install Python dependencies
run: |
pip install -e .[test]
pip install -r streamlit_app/requirements.txt
pip install .[test] ./pems_streamlit ./pems_web

- name: Run tests
run: ./tests/pytest/run.sh
Expand Down
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"type": "debugpy",
"request": "launch",
"module": "streamlit",
"args": ["run", "streamlit_app/main.py"],
"args": ["run", "pems_streamlit/src/pems_streamlit/main.py"],
"env": {
"PYTHONBUFFERED": "1",
"PYTHONWARNINGS": "default",
Expand All @@ -45,7 +45,7 @@
"type": "debugpy",
"request": "launch",
"module": "streamlit",
"args": ["run", "streamlit_app/main.py"],
"args": ["run", "pems_streamlit/src/pems_streamlit/main.py"],
"env": {
"PYTHONBUFFERED": "1",
"PYTHONWARNINGS": "default",
Expand Down
4 changes: 1 addition & 3 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,11 @@ services:
streamlit:
build:
context: .
dockerfile: streamlit_app/Dockerfile
dockerfile: pems_streamlit/container/Dockerfile
image: caltrans/pems:streamlit
env_file: .env
ports:
- "${STREAMLIT_LOCAL_PORT:-8501}:8501"
volumes:
- ./:/caltrans/app

volumes:
pgdata:
Expand Down
2 changes: 1 addition & 1 deletion infra/copilot/streamlit/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ http:
image:
# Docker build arguments. For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/backend-service/#image-build
build:
dockerfile: ../streamlit_app/Dockerfile
dockerfile: ../pems_streamlit/container/Dockerfile
context: ../
# Port exposed through your container to route traffic to it.
port: 8501
Expand Down
61 changes: 61 additions & 0 deletions pems_streamlit/container/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
ARG PYTHON_VERSION=3.12

# multi-stage build
#
# stage 1: builds the package wheel from source
# using the git metadata for version info
FROM python:${PYTHON_VERSION} AS build_wheel
WORKDIR /build

# upgrade pip, install build dependencies
RUN python -m pip install --upgrade pip build setuptools_scm

# copy source files
COPY . .
RUN git config --global --add safe.directory /build

# Move into directory to run the build
WORKDIR /build/pems_streamlit

# build package
RUN python -m build

# multi-stage build
#
# stage 2: installs the wheel in a fresh base container
# using the pre-built package, and copying only needed source
FROM python:${PYTHON_VERSION} AS app_container

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
USER=caltrans

EXPOSE 8501

# create non-root $USER and home directory
RUN useradd --create-home --shell /bin/bash $USER && \
python -m pip install --upgrade pip

COPY LICENSE LICENSE

# switch to non-root $USER
USER $USER

# update env for local pip installs
# see https://docs.python.org/3/using/cmdline.html#envvar-PYTHONUSERBASE
# since all `pip install` commands are in the context of $USER
# $PYTHONUSERBASE is the location used by default
ENV PATH="$PATH:/$USER/.local/bin" \
PYTHONUSERBASE="/$USER/.local" \
PYTHONPATH="$PYTHONPATH:/$USER/app"

WORKDIR /$USER/app

COPY .streamlit .streamlit
COPY pems_streamlit/container/entrypoint.sh entrypoint.sh
COPY pems_streamlit/container/run.py run.py
COPY --from=build_wheel /build/pems_streamlit/dist /wheels

RUN pip install $(find /wheels -name pems_streamlit*.whl)

ENTRYPOINT ["./entrypoint.sh"]
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ BASE_URL_PATH=${STREAMLIT_BASE_URL:-""}
echo "Starting Streamlit with base URL path: ${BASE_URL_PATH}"

# Execute the Streamlit command, passing the base URL path
exec streamlit run streamlit_app/main.py \
# Use the run.py helper since streamlit run wants the path to a python file
exec streamlit run run.py \
--server.port=8501 \
--server.address=0.0.0.0 \
--server.baseUrlPath="${BASE_URL_PATH}"
10 changes: 10 additions & 0 deletions pems_streamlit/container/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python
import runpy

# since we're installing pems_streamlit as a package now, the main.py file won't exist
# in the running container, so we can't point `streamlit run` at it

# instead, use runpy to run the pems_streamlit.main module directly.

if __name__ == "__main__":
runpy.run_module("pems_streamlit.main", run_name="__main__", alter_sys=True)
27 changes: 27 additions & 0 deletions pems_streamlit/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[project]
name = "pems_streamlit"
description = "The Streamlit application for PeMS data visualizations."
dynamic = ["version"]
requires-python = ">=3.12"
dependencies = [
"boto3==1.39.7",
"django==5.2.3",
"pandas==2.3.0",
"streamlit==1.45.1",
]

[build-system]
requires = ["setuptools>=75", "setuptools_scm>=8"]
build-backend = "setuptools.build_meta"

[tool.setuptools.packages.find]
where = ["src"]
include = ["pems_streamlit*"]
namespaces = false

[tool.setuptools.package-data]
pems_streamlit = ["*.txt"]

[tool.setuptools_scm]
# Tell scm to look one directory up for the .git folder
root = ".."
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import logging
import os


import streamlit as st

from streamlit_app.utils import discover_apps
from pems_streamlit.utils import discover_apps

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
Expand Down
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't need a default app page anymore, we can remove _default_app_page(), creating the default app page, and inserting the default app page in this file.

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def _convert_to_pages(apps: list[Path]) -> list[StreamlitPage]:


def _default_app_page() -> StreamlitPage:
"""Creates a no-op default page."""
return st.Page(APP_DIR / "__init__.py", default=True, title="PeMS Streamlit apps")


Expand Down Expand Up @@ -54,5 +55,6 @@ def discover_apps() -> list[StreamlitPage]:
default_app_page = _default_app_page()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we have to add back in _default_app_page(), creating the default app page, and inserting the default app page to have at least one page tagged as default. But we still leave the __init__.py blank as we have it right now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I should have checked. Fixed and rebased.

apps = _discover_apps()
pages = _convert_to_pages(apps)
# Streamlit needs at least one page marked as default
pages.insert(0, default_app_page)
return pages
11 changes: 6 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ license-files = ["LICENSE"]
classifiers = ["Programming Language :: Python :: 3 :: Only"]
requires-python = ">=3.12"
maintainers = [{ name = "Compiler LLC", email = "dev@compiler.la" }]
# Link all the local packages so they are installed together
dependencies = ["pems_web @ file:./pems_web"]
# local packages are installed as part of devcontainer build
# to ensure they are all installed in editable mode
dependencies = []

[project.optional-dependencies]
dev = [
Expand Down Expand Up @@ -41,8 +42,8 @@ include = '\.pyi?$'
[tool.coverage.run]
branch = true
relative_files = true
source = ["pems_web", "streamlit_app"]
omit = ["streamlit_app/apps/*"]
source = ["pems_streamlit", "pems_web"]
omit = ["pems_streamlit/src/pems_streamlit/apps/*"]

[tool.djlint]
ignore = "H006,H017,H031"
Expand All @@ -56,4 +57,4 @@ use_gitignore = true
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "tests.pytest.pems_web.settings"
# Explicitly add src directories to pytest's python path.
pythonpath = ["pems_web/src"]
pythonpath = ["pems_streamlit/src", "pems_web/src"]
42 changes: 0 additions & 42 deletions streamlit_app/Dockerfile

This file was deleted.

3 changes: 0 additions & 3 deletions streamlit_app/apps/__init__.py

This file was deleted.

4 changes: 0 additions & 4 deletions streamlit_app/requirements.txt

This file was deleted.

Empty file.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest
from streamlit_app import main

from pems_streamlit import main


@pytest.mark.parametrize(
Expand All @@ -19,8 +20,8 @@ def test_main(mocker, monkeypatch, nav_option, expected_nav):
else:
monkeypatch.delenv(nav_key, False)

mock_discover = mocker.patch("streamlit_app.main.discover_apps", return_value=apps)
mock_navigate = mocker.patch("streamlit_app.main.st.navigation")
mock_discover = mocker.patch("pems_streamlit.main.discover_apps", return_value=apps)
mock_navigate = mocker.patch("pems_streamlit.main.st.navigation")

main.main()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest
from streamlit.navigation.page import StreamlitPage

from streamlit_app import utils
from pems_streamlit import utils


@pytest.fixture
Expand All @@ -25,7 +25,7 @@ def app_paths(test_apps_path):


def test_convert_to_pages(mocker, app_paths):
page_factory = mocker.patch("streamlit_app.utils._make_app_page", return_value="page")
page_factory = mocker.patch("pems_streamlit.utils._make_app_page", return_value="page")

pages = utils._convert_to_pages(app_paths)

Expand Down Expand Up @@ -67,8 +67,8 @@ def test_make_app_page(app_paths):


def test_discover_apps(mocker):
mock_discover = mocker.patch("streamlit_app.utils._discover_apps", return_value=[])
mock_convert = mocker.patch("streamlit_app.utils._convert_to_pages", return_value=[])
mock_discover = mocker.patch("pems_streamlit.utils._discover_apps", return_value=[])
mock_convert = mocker.patch("pems_streamlit.utils._convert_to_pages", return_value=[])

result = utils.discover_apps()

Expand Down
Loading