Skip to content
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
5 changes: 3 additions & 2 deletions .github/workflows/ci-auto-fix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ jobs:
- name: Run ruff and black auto-fixes
run: |
set -e
ruff format --quiet --fix . || true
black --quiet . || true
python -m ruff check --fix . || true
python -m ruff format --quiet . || true
python -m black . || true

- name: Check for formatting changes
id: git-check
Expand Down
1 change: 1 addition & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""PyPath Shiny Dashboard Application."""

import sys
from pathlib import Path

Expand Down
162 changes: 123 additions & 39 deletions app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
implementing Ecopath mass-balance and Ecosim dynamic simulation.
"""

from shiny import App, Inputs, Outputs, Session, reactive, render, ui
from pathlib import Path
import logging
import sys
from datetime import datetime
from pathlib import Path

import shinyswatch
import sys
import logging
from shiny import App, Inputs, Outputs, Session, reactive, ui

# Get logger
logger = logging.getLogger('pypath_app')
logger = logging.getLogger("pypath_app")

# App directory for static assets
APP_DIR = Path(__file__).parent
Expand All @@ -28,32 +29,58 @@
# Fall back to local imports when running app/app.py directly (e.g., 'uvicorn app.app:app' run from inside the app/ directory).
try:
# Core pages
from app.pages import home, data_import, ecopath, prebalance, ecosim, results, analysis, about
# Advanced features
from app.pages import multistanza, forcing_demo, diet_rewiring_demo, optimization_demo, ecospace
# Configuration imports
from app.config import UI

# Advanced features
from app.pages import (
about,
analysis,
data_import,
diet_rewiring_demo,
ecopath,
ecosim,
ecospace,
forcing_demo,
home,
multistanza,
optimization_demo,
prebalance,
results,
)
except ModuleNotFoundError:
# Fallback: import modules as top-level local modules (when script is executed from the app/ dir)
from pages import home, data_import, ecopath, prebalance, ecosim, results, analysis, about
from pages import multistanza, forcing_demo, diet_rewiring_demo, optimization_demo, ecospace
from config import UI

from pages import (
about,
analysis,
data_import,
diet_rewiring_demo,
ecopath,
ecosim,
ecospace,
forcing_demo,
home,
multistanza,
optimization_demo,
prebalance,
results,
)

# App UI with dashboard layout and Bootstrap theme
app_ui = ui.page_navbar(
# Include Bootstrap Icons CSS and custom styles
ui.head_content(
ui.tags.link(
rel="stylesheet",
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css",
),
# Load custom CSS file
ui.tags.link(
rel="stylesheet",
href="custom.css"
),
ui.tags.link(rel="stylesheet", href="custom.css"),
# Additional CSS for DataGrid styling
ui.tags.style(f"""
ui.tags.style(
f"""
/* Make Group column wider in DataGrids */
.shiny-data-grid td:first-child,
.shiny-data-grid th:first-child {{
Expand All @@ -65,7 +92,8 @@
text-align: right;
font-family: monospace;
}}
""")
"""
),
),
# Navigation pages
ui.nav_panel("Home", home.home_ui()),
Expand All @@ -78,9 +106,11 @@
ui.nav_panel("ECOSPACE Spatial Modeling", ecospace.ecospace_ui()),
ui.nav_panel("Multi-Stanza Groups", multistanza.multistanza_ui()),
ui.nav_panel("State-Variable Forcing", forcing_demo.forcing_demo_ui()),
ui.nav_panel("Dynamic Diet Rewiring", diet_rewiring_demo.diet_rewiring_demo_ui()),
ui.nav_panel(
"Dynamic Diet Rewiring", diet_rewiring_demo.diet_rewiring_demo_ui()
),
ui.nav_panel("Bayesian Optimization", optimization_demo.optimization_demo_ui()),
icon=ui.tags.i(class_="bi bi-stars")
icon=ui.tags.i(class_="bi bi-stars"),
),
ui.nav_panel("Analysis", analysis.analysis_ui()),
ui.nav_panel("Results", results.results_ui()),
Expand All @@ -91,27 +121,38 @@
"btn_settings",
ui.tags.i(class_="bi bi-gear-fill"),
class_="btn btn-link nav-link p-2",
title="Settings"
title="Settings",
)
),
ui.nav_panel("About", about.about_ui()),

# Navbar settings
title=ui.tags.span(
ui.tags.img(src="icon.svg", height=UI.icon_height_px, style="margin-right: 8px; vertical-align: middle;"),
ui.tags.span("PyPath", style="font-weight: 600; vertical-align: middle;")
ui.tags.img(
src="icon.svg",
height=UI.icon_height_px,
style="margin-right: 8px; vertical-align: middle;",
),
ui.tags.span("PyPath", style="font-weight: 600; vertical-align: middle;"),
),
id="main_navbar",
footer=ui.div(
ui.tags.hr(),
ui.tags.p(
f"PyPath © {datetime.now().year} | ",
ui.tags.a("Documentation", href="https://github.com/razinkele/PyPath", class_="text-decoration-none"),
ui.tags.a(
"Documentation",
href="https://github.com/razinkele/PyPath",
class_="text-decoration-none",
),
" | ",
ui.tags.a("Report Issue", href="https://github.com/razinkele/PyPath/issues", class_="text-decoration-none"),
class_="text-center text-muted small"
ui.tags.a(
"Report Issue",
href="https://github.com/razinkele/PyPath/issues",
class_="text-decoration-none",
),
class_="text-center text-muted small",
),
class_="p-2"
class_="p-2",
),
fillable=True,
# Apply a clean modern theme - 'flatly' is professional and readable
Expand Down Expand Up @@ -182,7 +223,10 @@ class SharedData:
sim_results: Reactive value for simulation results (shared with core pages)
params: Reactive value for model parameters (for advanced features)
"""
def __init__(self, model_data_ref: reactive.Value, sim_results_ref: reactive.Value):

def __init__(
self, model_data_ref: reactive.Value, sim_results_ref: reactive.Value
):
# Reference the primary reactive values (no duplication)
self.model_data = model_data_ref
self.sim_results = sim_results_ref
Expand All @@ -198,7 +242,7 @@ def sync_model_data():
data = model_data()
if data is not None:
# For RpathParams objects (have model and diet attributes), store directly
if hasattr(data, 'model') and hasattr(data, 'diet'):
if hasattr(data, "model") and hasattr(data, "diet"):
shared_data.params.set(data)
else:
# For other data structures, store as-is
Expand All @@ -207,17 +251,57 @@ def sync_model_data():
# Initialize page servers with error handling
server_modules = [
("Home", lambda: home.home_server(input, output, session, model_data)),
("Data Import", lambda: data_import.import_server(input, output, session, model_data)),
(
"Data Import",
lambda: data_import.import_server(input, output, session, model_data),
),
("Ecopath", lambda: ecopath.ecopath_server(input, output, session, model_data)),
("Pre-Balance Diagnostics", lambda: prebalance.prebalance_server(input, output, session, model_data)),
("Ecosim", lambda: ecosim.ecosim_server(input, output, session, model_data, sim_results)),
("Ecospace", lambda: ecospace.ecospace_server(input, output, session, model_data, sim_results)),
("Multi-Stanza", lambda: multistanza.multistanza_server(input, output, session, shared_data)),
("Forcing Demo", lambda: forcing_demo.forcing_demo_server(input, output, session)),
("Diet Rewiring Demo", lambda: diet_rewiring_demo.diet_rewiring_demo_server(input, output, session)),
("Optimization Demo", lambda: optimization_demo.optimization_demo_server(input, output, session)),
("Analysis", lambda: analysis.analysis_server(input, output, session, model_data, sim_results)),
("Results", lambda: results.results_server(input, output, session, model_data, sim_results)),
(
"Pre-Balance Diagnostics",
lambda: prebalance.prebalance_server(input, output, session, model_data),
),
(
"Ecosim",
lambda: ecosim.ecosim_server(
input, output, session, model_data, sim_results
),
),
(
"Ecospace",
lambda: ecospace.ecospace_server(
input, output, session, model_data, sim_results
),
),
(
"Multi-Stanza",
lambda: multistanza.multistanza_server(input, output, session, shared_data),
),
(
"Forcing Demo",
lambda: forcing_demo.forcing_demo_server(input, output, session),
),
(
"Diet Rewiring Demo",
lambda: diet_rewiring_demo.diet_rewiring_demo_server(
input, output, session
),
),
(
"Optimization Demo",
lambda: optimization_demo.optimization_demo_server(input, output, session),
),
(
"Analysis",
lambda: analysis.analysis_server(
input, output, session, model_data, sim_results
),
),
(
"Results",
lambda: results.results_server(
input, output, session, model_data, sim_results
),
),
("About", lambda: about.about_server(input, output, session)),
]

Expand Down
Loading
Loading