github-actions[bot] commited on
Commit ·
faed07e
1
Parent(s): 99ccdef
Deploy from GitHub Actions
Browse files- app.py +4 -4
- pages.py +4 -4
- plots/map.py +9 -0
- ui/pages/seasonal_maps.py +6 -16
- ui/pages/tables.py +99 -0
app.py
CHANGED
|
@@ -10,7 +10,6 @@ from pages import (
|
|
| 10 |
home_page,
|
| 11 |
nutrient_ratios_page,
|
| 12 |
parameter_correlations_page,
|
| 13 |
-
parameter_summary_tables_page,
|
| 14 |
raw_data_page,
|
| 15 |
scatter_plots_page,
|
| 16 |
seasonal_line_charts_page,
|
|
@@ -18,6 +17,7 @@ from pages import (
|
|
| 18 |
sector_compare_page,
|
| 19 |
sector_trends_page,
|
| 20 |
settings_page,
|
|
|
|
| 21 |
trends_by_station_page,
|
| 22 |
)
|
| 23 |
from utils.session import ensure_session_initialized
|
|
@@ -46,11 +46,11 @@ analytics = st.Page(
|
|
| 46 |
page_dict = {}
|
| 47 |
|
| 48 |
page_dict["Annual Report Draft Charts/Tables"] = [
|
| 49 |
-
seasonal_line_charts_page,
|
| 50 |
-
seasonal_maps_page,
|
| 51 |
sector_compare_page,
|
|
|
|
|
|
|
| 52 |
scatter_plots_page,
|
| 53 |
-
|
| 54 |
]
|
| 55 |
|
| 56 |
page_dict["Water Quality Portal"] = [
|
|
|
|
| 10 |
home_page,
|
| 11 |
nutrient_ratios_page,
|
| 12 |
parameter_correlations_page,
|
|
|
|
| 13 |
raw_data_page,
|
| 14 |
scatter_plots_page,
|
| 15 |
seasonal_line_charts_page,
|
|
|
|
| 17 |
sector_compare_page,
|
| 18 |
sector_trends_page,
|
| 19 |
settings_page,
|
| 20 |
+
tables_page,
|
| 21 |
trends_by_station_page,
|
| 22 |
)
|
| 23 |
from utils.session import ensure_session_initialized
|
|
|
|
| 46 |
page_dict = {}
|
| 47 |
|
| 48 |
page_dict["Annual Report Draft Charts/Tables"] = [
|
|
|
|
|
|
|
| 49 |
sector_compare_page,
|
| 50 |
+
seasonal_maps_page,
|
| 51 |
+
seasonal_line_charts_page,
|
| 52 |
scatter_plots_page,
|
| 53 |
+
tables_page,
|
| 54 |
]
|
| 55 |
|
| 56 |
page_dict["Water Quality Portal"] = [
|
pages.py
CHANGED
|
@@ -659,9 +659,9 @@ dissolved_oxygen_page = st.Page(
|
|
| 659 |
icon=":material/water_drop:",
|
| 660 |
)
|
| 661 |
|
| 662 |
-
|
| 663 |
-
"ui/pages/
|
| 664 |
-
title="
|
| 665 |
icon=":material/table_chart:",
|
| 666 |
)
|
| 667 |
scatter_plots_page = st.Page(
|
|
@@ -687,7 +687,7 @@ __all__ = [
|
|
| 687 |
"do_temp_relationship_page",
|
| 688 |
"raw_data_page",
|
| 689 |
"settings_page",
|
| 690 |
-
"
|
| 691 |
"scatter_plots_page",
|
| 692 |
"seasonal_line_charts_page",
|
| 693 |
"sector_compare_page",
|
|
|
|
| 659 |
icon=":material/water_drop:",
|
| 660 |
)
|
| 661 |
|
| 662 |
+
tables_page = st.Page(
|
| 663 |
+
"ui/pages/tables.py",
|
| 664 |
+
title="Tables",
|
| 665 |
icon=":material/table_chart:",
|
| 666 |
)
|
| 667 |
scatter_plots_page = st.Page(
|
|
|
|
| 687 |
"do_temp_relationship_page",
|
| 688 |
"raw_data_page",
|
| 689 |
"settings_page",
|
| 690 |
+
"tables_page",
|
| 691 |
"scatter_plots_page",
|
| 692 |
"seasonal_line_charts_page",
|
| 693 |
"sector_compare_page",
|
plots/map.py
CHANGED
|
@@ -13,6 +13,15 @@ from osgeo import gdal
|
|
| 13 |
|
| 14 |
from utils.data_loading import timer
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
@timer(include_params=True)
|
| 18 |
def generate_seasonal_plot(
|
|
|
|
| 13 |
|
| 14 |
from utils.data_loading import timer
|
| 15 |
|
| 16 |
+
BASEMAP_PROVIDERS = {
|
| 17 |
+
"USGS Topo": ctx.providers.USGS.USTopo, # type: ignore
|
| 18 |
+
"OpenStreetMap": ctx.providers.OpenStreetMap.Mapnik, # type: ignore
|
| 19 |
+
"CartoDB Light": ctx.providers.CartoDB.Positron, # type: ignore
|
| 20 |
+
"CartoDB Voyager": ctx.providers.CartoDB.Voyager, # type: ignore
|
| 21 |
+
"NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief": ctx.providers.NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief, # type: ignore
|
| 22 |
+
"OpenTopoMap": ctx.providers.OpenTopoMap, # type: ignore
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
|
| 26 |
@timer(include_params=True)
|
| 27 |
def generate_seasonal_plot(
|
ui/pages/seasonal_maps.py
CHANGED
|
@@ -1,12 +1,11 @@
|
|
| 1 |
import io
|
| 2 |
|
| 3 |
-
import contextily as ctx
|
| 4 |
import pandas as pd
|
| 5 |
import streamlit as st
|
| 6 |
|
| 7 |
from components import render_filtered_data_preview
|
| 8 |
from dashboard_analytics import log_visit
|
| 9 |
-
from plots.map import generate_seasonal_plot
|
| 10 |
from utils.data_loading import add_lat_long, get_stations_data
|
| 11 |
from utils.date_utils import get_reporting_year
|
| 12 |
|
|
@@ -24,7 +23,7 @@ timeframe = st.sidebar.radio(
|
|
| 24 |
# parameter selection
|
| 25 |
parameters = sorted(raw_df["Org_Analyte_Name"].unique())
|
| 26 |
selected_parameter = st.sidebar.selectbox(
|
| 27 |
-
"
|
| 28 |
parameters,
|
| 29 |
index=parameters.index("Salinity") if "Salinity" in parameters else 0,
|
| 30 |
key="seasonal_parameter_select",
|
|
@@ -38,7 +37,7 @@ param_years = sorted(
|
|
| 38 |
# Show either single year selector or year range based on timeframe
|
| 39 |
if timeframe == "Single Year":
|
| 40 |
selected_year = st.sidebar.selectbox(
|
| 41 |
-
"
|
| 42 |
param_years,
|
| 43 |
index=len(param_years) - 1,
|
| 44 |
key="seasonal_year_select",
|
|
@@ -55,7 +54,7 @@ else:
|
|
| 55 |
|
| 56 |
# Add reporting year end month selector
|
| 57 |
reporting_end_month = st.sidebar.selectbox(
|
| 58 |
-
"Reporting Year End
|
| 59 |
options=range(1, 13),
|
| 60 |
index=10, # Default to November
|
| 61 |
format_func=lambda x: pd.Timestamp(2000, x, 1).strftime("%B"), # Show month names
|
|
@@ -68,18 +67,9 @@ raw_df["Reporting_Year"] = raw_df["Activity_Start_Date_Time"].apply(
|
|
| 68 |
lambda x: get_reporting_year(x, reporting_end_month)
|
| 69 |
)
|
| 70 |
# Add basemap selector
|
| 71 |
-
basemap_options = {
|
| 72 |
-
"OpenStreetMap": ctx.providers.OpenStreetMap.Mapnik, # type: ignore
|
| 73 |
-
"USGS Topo": ctx.providers.USGS.USTopo, # type: ignore
|
| 74 |
-
"CartoDB Light": ctx.providers.CartoDB.Positron, # type: ignore
|
| 75 |
-
"CartoDB Voyager": ctx.providers.CartoDB.Voyager, # type: ignore
|
| 76 |
-
"NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief": ctx.providers.NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief, # type: ignore
|
| 77 |
-
"OpenTopoMap": ctx.providers.OpenTopoMap, # type: ignore
|
| 78 |
-
}
|
| 79 |
-
|
| 80 |
selected_basemap = st.sidebar.selectbox(
|
| 81 |
"Basemap Provider:",
|
| 82 |
-
options=list(
|
| 83 |
index=0,
|
| 84 |
key="seasonal_basemap_select",
|
| 85 |
)
|
|
@@ -125,7 +115,7 @@ if not raw_df.empty:
|
|
| 125 |
areas=sectors,
|
| 126 |
area_type="sector",
|
| 127 |
reporting_end_month=reporting_end_month,
|
| 128 |
-
basemap_provider=
|
| 129 |
)
|
| 130 |
st.pyplot(fig)
|
| 131 |
|
|
|
|
| 1 |
import io
|
| 2 |
|
|
|
|
| 3 |
import pandas as pd
|
| 4 |
import streamlit as st
|
| 5 |
|
| 6 |
from components import render_filtered_data_preview
|
| 7 |
from dashboard_analytics import log_visit
|
| 8 |
+
from plots.map import BASEMAP_PROVIDERS, generate_seasonal_plot
|
| 9 |
from utils.data_loading import add_lat_long, get_stations_data
|
| 10 |
from utils.date_utils import get_reporting_year
|
| 11 |
|
|
|
|
| 23 |
# parameter selection
|
| 24 |
parameters = sorted(raw_df["Org_Analyte_Name"].unique())
|
| 25 |
selected_parameter = st.sidebar.selectbox(
|
| 26 |
+
"Parameter:",
|
| 27 |
parameters,
|
| 28 |
index=parameters.index("Salinity") if "Salinity" in parameters else 0,
|
| 29 |
key="seasonal_parameter_select",
|
|
|
|
| 37 |
# Show either single year selector or year range based on timeframe
|
| 38 |
if timeframe == "Single Year":
|
| 39 |
selected_year = st.sidebar.selectbox(
|
| 40 |
+
"Year:",
|
| 41 |
param_years,
|
| 42 |
index=len(param_years) - 1,
|
| 43 |
key="seasonal_year_select",
|
|
|
|
| 54 |
|
| 55 |
# Add reporting year end month selector
|
| 56 |
reporting_end_month = st.sidebar.selectbox(
|
| 57 |
+
"Reporting Year End-Month",
|
| 58 |
options=range(1, 13),
|
| 59 |
index=10, # Default to November
|
| 60 |
format_func=lambda x: pd.Timestamp(2000, x, 1).strftime("%B"), # Show month names
|
|
|
|
| 67 |
lambda x: get_reporting_year(x, reporting_end_month)
|
| 68 |
)
|
| 69 |
# Add basemap selector
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
selected_basemap = st.sidebar.selectbox(
|
| 71 |
"Basemap Provider:",
|
| 72 |
+
options=list(BASEMAP_PROVIDERS.keys()),
|
| 73 |
index=0,
|
| 74 |
key="seasonal_basemap_select",
|
| 75 |
)
|
|
|
|
| 115 |
areas=sectors,
|
| 116 |
area_type="sector",
|
| 117 |
reporting_end_month=reporting_end_month,
|
| 118 |
+
basemap_provider=BASEMAP_PROVIDERS[selected_basemap],
|
| 119 |
)
|
| 120 |
st.pyplot(fig)
|
| 121 |
|
ui/pages/tables.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import calendar
|
| 2 |
+
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import streamlit as st
|
| 5 |
+
|
| 6 |
+
from dashboard_analytics import log_visit
|
| 7 |
+
from utils.data_loading import create_summaries
|
| 8 |
+
from utils.summary import create_overall_summary_table
|
| 9 |
+
|
| 10 |
+
st.title("Summary Tables")
|
| 11 |
+
log_visit("Tables")
|
| 12 |
+
|
| 13 |
+
# Create summaries if they don't exist
|
| 14 |
+
if "overall_summary" not in st.session_state.data:
|
| 15 |
+
summary_by_station, overall_summary, multiindex_df = create_summaries(
|
| 16 |
+
st.session_state.data["raw_df"]
|
| 17 |
+
)
|
| 18 |
+
st.session_state.data.update(
|
| 19 |
+
{
|
| 20 |
+
"summary_by_station": summary_by_station,
|
| 21 |
+
"overall_summary": overall_summary,
|
| 22 |
+
"multiindex_df": multiindex_df,
|
| 23 |
+
}
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
# Create tabs for different summary views
|
| 27 |
+
tab1, tab2 = st.tabs(["Overall Summary", "Monthly Means"])
|
| 28 |
+
|
| 29 |
+
with tab1:
|
| 30 |
+
st.html(
|
| 31 |
+
create_overall_summary_table(
|
| 32 |
+
st.session_state.data["overall_summary"]
|
| 33 |
+
).as_raw_html()
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
with tab2:
|
| 37 |
+
# Get data from session state
|
| 38 |
+
raw_df = st.session_state.data["raw_df"].copy()
|
| 39 |
+
|
| 40 |
+
# Create datetime column if not exists
|
| 41 |
+
raw_df["Date"] = pd.to_datetime(raw_df["Activity_Start_Date_Time"])
|
| 42 |
+
|
| 43 |
+
# Add month and year columns
|
| 44 |
+
raw_df["Month"] = raw_df["Date"].dt.strftime("%b") # 3-letter month names
|
| 45 |
+
raw_df["Year"] = raw_df["Date"].dt.year
|
| 46 |
+
|
| 47 |
+
# Move parameter selection into tab2
|
| 48 |
+
selected_parameter = st.selectbox(
|
| 49 |
+
"Parameter:",
|
| 50 |
+
options=sorted(raw_df["Org_Analyte_Name"].unique()),
|
| 51 |
+
index=0,
|
| 52 |
+
help="Select a parameter to display monthly averages.",
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
# Filter data for selected parameter
|
| 56 |
+
param_df = raw_df[raw_df["Org_Analyte_Name"] == selected_parameter]
|
| 57 |
+
|
| 58 |
+
# Create pivot table
|
| 59 |
+
pivot_table = pd.pivot_table(
|
| 60 |
+
param_df,
|
| 61 |
+
values="Org_Result_Value",
|
| 62 |
+
index="Year",
|
| 63 |
+
columns="Month",
|
| 64 |
+
aggfunc="mean",
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
# Reorder columns to match calendar months
|
| 68 |
+
month_order = [calendar.month_abbr[i] for i in range(1, 13)]
|
| 69 |
+
pivot_table = pivot_table.reindex(columns=month_order)
|
| 70 |
+
pivot_table = pivot_table.round(2)
|
| 71 |
+
|
| 72 |
+
# Add yearly mean column
|
| 73 |
+
pivot_table_with_yearly_mean = pivot_table.copy()
|
| 74 |
+
pivot_table_with_yearly_mean["Annual Mean"] = pivot_table.mean(axis=1)
|
| 75 |
+
|
| 76 |
+
# Add grand mean row
|
| 77 |
+
grand_mean = pivot_table.mean()
|
| 78 |
+
pivot_table_with_yearly_mean.loc["Grand Mean"] = grand_mean
|
| 79 |
+
|
| 80 |
+
# Display the pivot table
|
| 81 |
+
st.markdown(f"### Monthly Means: {selected_parameter}")
|
| 82 |
+
st.dataframe(
|
| 83 |
+
pivot_table_with_yearly_mean.reset_index().style.format(
|
| 84 |
+
na_rep="", precision=2, thousands=""
|
| 85 |
+
),
|
| 86 |
+
use_container_width=True,
|
| 87 |
+
hide_index=True,
|
| 88 |
+
height=(len(pivot_table_with_yearly_mean) + 1) * 35
|
| 89 |
+
+ 2, # 35 pixels per row, +1 for header
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
# Add download button
|
| 93 |
+
csv_buffer = pivot_table_with_yearly_mean.to_csv().encode()
|
| 94 |
+
st.download_button(
|
| 95 |
+
label="Download Table (CSV)",
|
| 96 |
+
data=csv_buffer,
|
| 97 |
+
file_name=f"{selected_parameter}_monthly_means.csv",
|
| 98 |
+
mime="text/csv",
|
| 99 |
+
)
|