Pavan Kumar Jonnakuti commited on
Commit
ab202d4
Β·
1 Parent(s): 2f597cc

Fix network port, CORS/XSRF, deprecation warnings, and add parquet cache fallback loading

Browse files
Code/argo_dashboard.py CHANGED
@@ -239,7 +239,7 @@ with col1:
239
  for _, r in totals.iterrows():
240
  fig_bar.add_annotation(x=r['Year'], y=r['Total_Floats'], text=str(int(r['Total_Floats'])), showarrow=False, yshift=10)
241
  fig_bar.update_layout(barmode='stack', xaxis=dict(dtick=1), height=520)
242
- st.plotly_chart(fig_bar, use_container_width=True)
243
 
244
  # Donut chart: age distribution of alive floats (bio dataset)
245
  with col2:
@@ -255,7 +255,7 @@ with col2:
255
  if not age_counts.empty:
256
  fig_donut = px.pie(age_counts, names='Age_Years', values='Count', hole=0.55, title='Age (years) distribution of alive floats')
257
  fig_donut.update_traces(textinfo='percent+label')
258
- st.plotly_chart(fig_donut, use_container_width=True)
259
  else:
260
  st.write("No alive float age groups available for selected filters.")
261
  else:
@@ -331,7 +331,7 @@ else:
331
  )
332
 
333
  fig_map.update_layout(height=650)
334
- st.plotly_chart(fig_map, use_container_width=True)
335
  # ------------------------------
336
  # DAC summary table (bio dataset) - bottom
337
  # Live = age_days >= 90, Dead = age_days < 90, Total = unique floats
@@ -352,7 +352,7 @@ summary_table = g.groupby('DAC').agg(
352
 
353
  # present in requested order: DAC | Live | Dead | Total
354
  summary_table = summary_table[['DAC','Live','Dead','Total']]
355
- st.dataframe(summary_table, use_container_width=True)
356
 
357
  # Compact text lines
358
  st.markdown("**Compact summary (DAC β€” Live / Dead / Total)**")
 
239
  for _, r in totals.iterrows():
240
  fig_bar.add_annotation(x=r['Year'], y=r['Total_Floats'], text=str(int(r['Total_Floats'])), showarrow=False, yshift=10)
241
  fig_bar.update_layout(barmode='stack', xaxis=dict(dtick=1), height=520)
242
+ st.plotly_chart(fig_bar, width="stretch")
243
 
244
  # Donut chart: age distribution of alive floats (bio dataset)
245
  with col2:
 
255
  if not age_counts.empty:
256
  fig_donut = px.pie(age_counts, names='Age_Years', values='Count', hole=0.55, title='Age (years) distribution of alive floats')
257
  fig_donut.update_traces(textinfo='percent+label')
258
+ st.plotly_chart(fig_donut, width="stretch")
259
  else:
260
  st.write("No alive float age groups available for selected filters.")
261
  else:
 
331
  )
332
 
333
  fig_map.update_layout(height=650)
334
+ st.plotly_chart(fig_map, width="stretch")
335
  # ------------------------------
336
  # DAC summary table (bio dataset) - bottom
337
  # Live = age_days >= 90, Dead = age_days < 90, Total = unique floats
 
352
 
353
  # present in requested order: DAC | Live | Dead | Total
354
  summary_table = summary_table[['DAC','Live','Dead','Total']]
355
+ st.dataframe(summary_table, width="stretch")
356
 
357
  # Compact text lines
358
  st.markdown("**Compact summary (DAC β€” Live / Dead / Total)**")
streamlit/.streamlit/config.toml CHANGED
@@ -7,5 +7,8 @@ font = "sans serif"
7
 
8
  [server]
9
  headless = true
10
- port = 8501
11
  maxUploadSize = 500
 
 
 
 
7
 
8
  [server]
9
  headless = true
10
+ port = 3001
11
  maxUploadSize = 500
12
+ address = "0.0.0.0"
13
+ enableCORS = false
14
+ enableXsrfProtection = false
streamlit/dashboard.py CHANGED
@@ -329,8 +329,11 @@ def load_profile_data():
329
  cache_path = CACHE_DIR / "profiles.parquet"
330
 
331
  if cache_path.exists():
332
- age_h = (datetime.now().timestamp() - cache_path.stat().st_mtime) / 3600
333
- if age_h < 24:
 
 
 
334
  df = pd.read_parquet(cache_path)
335
  # Ensure is_deep exists (handles stale caches from before this column was added)
336
  if "is_deep" not in df.columns:
@@ -342,7 +345,7 @@ def load_profile_data():
342
  df = pd.read_csv(PROF_FILE, comment="#")
343
  # Strip whitespace from column names (GDAC files sometimes have spaces)
344
  df.columns = df.columns.str.strip()
345
-
346
  # --- Land-mask filtering removed: caused discrepancies ---
347
  df = df.dropna(subset=["latitude", "longitude"])
348
 
@@ -351,7 +354,7 @@ def load_profile_data():
351
  (df["latitude"] >= -90) & (df["latitude"] <= 90) &
352
  (df["longitude"] >= -180) & (df["longitude"] <= 180)
353
  ]
354
-
355
  df["date"] = pd.to_datetime(df["date"], format="%Y%m%d%H%M%S", errors="coerce")
356
  if "date_update" in df.columns:
357
  df["date_update"] = pd.to_datetime(
@@ -361,52 +364,58 @@ def load_profile_data():
361
  df["dac"] = df["file"].str.extract(r"^([^/]+)/")
362
  df["year"] = df["date"].dt.year
363
  df["is_deep"] = df["profiler_type"].isin(DEEP_PROFILER_TYPES)
364
-
365
  df.to_parquet(cache_path, index=False)
366
  return df
367
-
368
-
369
  @st.cache_data(show_spinner="Loading BGC-profile index …")
370
  def load_bio_data():
371
  """Load argo_bio-profile_index.txt with Parquet cache (24-h TTL)."""
372
  CACHE_DIR.mkdir(exist_ok=True)
373
  cache_path = CACHE_DIR / "bgc_profiles.parquet"
374
-
375
  if cache_path.exists():
376
- age_h = (datetime.now().timestamp() - cache_path.stat().st_mtime) / 3600
377
- if age_h < 24:
 
 
 
378
  return pd.read_parquet(cache_path)
379
-
380
  df = pd.read_csv(BIO_FILE, comment="#")
381
  df.columns = df.columns.str.strip()
382
-
383
  df["date"] = pd.to_datetime(df["date"], format="%Y%m%d%H%M%S", errors="coerce")
384
  df["wmo_id"] = df["file"].str.extract(r"/(\d+)/")
385
  df["year"] = df["date"].dt.year
386
-
387
  params_upper = df["parameters"].fillna("").str.upper()
388
  df["has_doxy"] = params_upper.str.contains("DOXY")
389
  df["has_chla"] = params_upper.str.contains("CHLA")
390
  df["has_nitrate"] = params_upper.str.contains("NITRATE")
391
  df["has_ph"] = params_upper.str.contains("PH_IN_SITU")
392
-
393
  df.to_parquet(cache_path, index=False)
394
  return df
395
-
396
-
397
  @st.cache_data(show_spinner="Loading float metadata index …")
398
  def load_meta_data():
399
  """Load ar_index_global_meta.txt with Parquet cache (24-h TTL).
400
-
401
  Provides one row per float (WMO) with profiler_type, institution,
402
  dac, and a human-readable profiler_name from WMO R08.
403
  """
404
  CACHE_DIR.mkdir(exist_ok=True)
405
  cache_path = CACHE_DIR / "meta.parquet"
406
-
407
  if cache_path.exists():
408
- age_h = (datetime.now().timestamp() - cache_path.stat().st_mtime) / 3600
409
- if age_h < 24:
 
 
 
410
  return pd.read_parquet(cache_path)
411
 
412
  df = pd.read_csv(META_FILE, comment="#")
@@ -785,29 +794,29 @@ def show_float_details(wmo):
785
  c1, c2, c3 = st.columns(3)
786
  with c1:
787
  fig = plot_utils.create_ts_diagram(cycles, temp, psal, wmo, title=f"T/S Diagram<br><sup>{date_suffix}</sup>")
788
- st.plotly_chart(fig, use_container_width=True)
789
  with c2:
790
  fig = plot_utils.create_section_chart(dates, pres, temp, "Temperature (Β°C)", f"Section chart TEMP<br><sup>{date_suffix}</sup>", wmo)
791
- st.plotly_chart(fig, use_container_width=True)
792
  with c3:
793
  fig = plot_utils.create_section_chart(dates, pres, psal, "Salinity (PSU)", f"Section chart PSAL<br><sup>{date_suffix}</sup>", wmo)
794
- st.plotly_chart(fig, use_container_width=True)
795
 
796
  c4, c5, c6 = st.columns(3)
797
  with c4:
798
  fig = plot_utils.create_section_chart(dates, pres, rho, "Potential Density (kg/mΒ³)", f"Section chart RHO<br><sup>{date_suffix}</sup>", wmo)
799
- st.plotly_chart(fig, use_container_width=True)
800
  with c5:
801
  fig = plot_utils.create_overlaid_profiles(temp, pres, cycles, "Temperature (Β°C)", f"Overlaid profiles TEMP<br><sup>{date_suffix}</sup>", wmo)
802
- st.plotly_chart(fig, use_container_width=True)
803
  with c6:
804
  fig = plot_utils.create_overlaid_profiles(psal, pres, cycles, "Salinity (PSU)", f"Overlaid profiles PSAL<br><sup>{date_suffix}</sup>", wmo)
805
- st.plotly_chart(fig, use_container_width=True)
806
 
807
  c7, c8, c9 = st.columns(3)
808
  with c7:
809
  fig = plot_utils.create_overlaid_profiles(rho, pres, cycles, "Potential Density (kg/mΒ³)", f"Overlaid profiles RHO<br><sup>{date_suffix}</sup>", wmo)
810
- st.plotly_chart(fig, use_container_width=True)
811
  else:
812
  st.info("No valid profile data available for technical plots.")
813
  except Exception as e:
@@ -933,7 +942,7 @@ with st.sidebar:
933
  st.markdown("## πŸ” Filters")
934
 
935
  # ── Refresh ──
936
- if st.button("πŸ”„ Refresh Data", use_container_width=True, type="primary"):
937
  for f in CACHE_DIR.glob("*.parquet"):
938
  f.unlink()
939
  st.cache_data.clear()
@@ -1376,7 +1385,7 @@ with col_left:
1376
  )
1377
 
1378
  fig_map.update_layout(height=620)
1379
- st.plotly_chart(fig_map, use_container_width=True, key="main_map", on_select="rerun", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_float_map"}})
1380
 
1381
  if selected_wmo_from_map:
1382
  if st.button(f"πŸ“„ View Info for Float {selected_wmo_from_map}"):
@@ -1468,7 +1477,7 @@ with col_right:
1468
  margin=dict(l=50, r=20, t=80, b=40),
1469
  )
1470
  )
1471
- st.plotly_chart(fig_bar, use_container_width=True, key="bar_chart", on_select="rerun", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_annual_floats"}})
1472
  else:
1473
  st.info("No active float data for bar chart.")
1474
  else:
@@ -1571,7 +1580,7 @@ with col_tree:
1571
  **_dark_layout(margin=dict(l=0, r=0, t=10, b=0)),
1572
  coloraxis_showscale=False,
1573
  )
1574
- st.plotly_chart(fig_tree, use_container_width=True, key="treemap", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_last1day_treemap"}})
1575
  else:
1576
  st.info("No active floats in the last 1 day for current filters.")
1577
  else:
@@ -1653,7 +1662,7 @@ with col_donut:
1653
  bgcolor="rgba(0,0,0,0)",
1654
  ),
1655
  )
1656
- st.plotly_chart(fig_donut, use_container_width=True, key="donut", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_age_distribution"}})
1657
  else:
1658
  st.info("No age data available.")
1659
  else:
@@ -1718,7 +1727,7 @@ with col_profiler:
1718
  bgcolor="rgba(0,0,0,0)",
1719
  ),
1720
  )
1721
- st.plotly_chart(fig_ptype, use_container_width=True, key="profiler_donut", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_profiler_types"}})
1722
  st.caption(f"πŸ“‹ {len(df_meta):,} floats across {df_meta['profiler_name'].nunique()} instrument models (source: ar_index_global_meta.txt)")
1723
  else:
1724
  st.info("No metadata available.")
@@ -1768,7 +1777,7 @@ with col_fleet:
1768
  margin=dict(l=50, r=20, t=60, b=40),
1769
  ),
1770
  )
1771
- st.plotly_chart(fig_fleet, use_container_width=True, key="fleet_composition", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_fleet_composition"}})
1772
  st.caption("Shows how the fleet instrument mix has evolved per deployment year")
1773
  else:
1774
  st.info("No deployment data available.")
@@ -1868,7 +1877,7 @@ if len(df_prof) > 0:
1868
  height=160,
1869
  )
1870
  )
1871
- st.plotly_chart(fig_global, use_container_width=True, key="global_status_bar", config={"displayModeBar": False})
1872
 
1873
  with col_dac2:
1874
  st.markdown("### πŸ“‘ Float Status Summary")
@@ -2168,7 +2177,7 @@ with st.expander("πŸ“‹ View Raw Data", expanded=False):
2168
  tab1, tab2, tab3 = st.tabs(["Core Profiles", "BGC Profiles", "Float Metadata"])
2169
  with tab1:
2170
  st.dataframe(
2171
- filt_prof.head(200), use_container_width=True, hide_index=True
2172
  )
2173
  st.caption(
2174
  f"Showing {min(200, len(filt_prof)):,} of {len(filt_prof):,} records"
@@ -2182,7 +2191,7 @@ with st.expander("πŸ“‹ View Raw Data", expanded=False):
2182
  )
2183
  with tab2:
2184
  st.dataframe(
2185
- filt_bio.head(200), use_container_width=True, hide_index=True
2186
  )
2187
  st.caption(
2188
  f"Showing {min(200, len(filt_bio)):,} of {len(filt_bio):,} records"
@@ -2196,7 +2205,7 @@ with st.expander("πŸ“‹ View Raw Data", expanded=False):
2196
  )
2197
  with tab3:
2198
  st.dataframe(
2199
- df_meta.head(500), use_container_width=True, hide_index=True
2200
  )
2201
  st.caption(
2202
  f"Showing {min(500, len(df_meta)):,} of {len(df_meta):,} float metadata records (source: ar_index_global_meta.txt)"
 
329
  cache_path = CACHE_DIR / "profiles.parquet"
330
 
331
  if cache_path.exists():
332
+ use_cache = not PROF_FILE.exists()
333
+ if not use_cache:
334
+ age_h = (datetime.now().timestamp() - cache_path.stat().st_mtime) / 3600
335
+ use_cache = age_h < 24
336
+ if use_cache:
337
  df = pd.read_parquet(cache_path)
338
  # Ensure is_deep exists (handles stale caches from before this column was added)
339
  if "is_deep" not in df.columns:
 
345
  df = pd.read_csv(PROF_FILE, comment="#")
346
  # Strip whitespace from column names (GDAC files sometimes have spaces)
347
  df.columns = df.columns.str.strip()
348
+
349
  # --- Land-mask filtering removed: caused discrepancies ---
350
  df = df.dropna(subset=["latitude", "longitude"])
351
 
 
354
  (df["latitude"] >= -90) & (df["latitude"] <= 90) &
355
  (df["longitude"] >= -180) & (df["longitude"] <= 180)
356
  ]
357
+
358
  df["date"] = pd.to_datetime(df["date"], format="%Y%m%d%H%M%S", errors="coerce")
359
  if "date_update" in df.columns:
360
  df["date_update"] = pd.to_datetime(
 
364
  df["dac"] = df["file"].str.extract(r"^([^/]+)/")
365
  df["year"] = df["date"].dt.year
366
  df["is_deep"] = df["profiler_type"].isin(DEEP_PROFILER_TYPES)
367
+
368
  df.to_parquet(cache_path, index=False)
369
  return df
370
+
371
+
372
  @st.cache_data(show_spinner="Loading BGC-profile index …")
373
  def load_bio_data():
374
  """Load argo_bio-profile_index.txt with Parquet cache (24-h TTL)."""
375
  CACHE_DIR.mkdir(exist_ok=True)
376
  cache_path = CACHE_DIR / "bgc_profiles.parquet"
377
+
378
  if cache_path.exists():
379
+ use_cache = not BIO_FILE.exists()
380
+ if not use_cache:
381
+ age_h = (datetime.now().timestamp() - cache_path.stat().st_mtime) / 3600
382
+ use_cache = age_h < 24
383
+ if use_cache:
384
  return pd.read_parquet(cache_path)
385
+
386
  df = pd.read_csv(BIO_FILE, comment="#")
387
  df.columns = df.columns.str.strip()
388
+
389
  df["date"] = pd.to_datetime(df["date"], format="%Y%m%d%H%M%S", errors="coerce")
390
  df["wmo_id"] = df["file"].str.extract(r"/(\d+)/")
391
  df["year"] = df["date"].dt.year
392
+
393
  params_upper = df["parameters"].fillna("").str.upper()
394
  df["has_doxy"] = params_upper.str.contains("DOXY")
395
  df["has_chla"] = params_upper.str.contains("CHLA")
396
  df["has_nitrate"] = params_upper.str.contains("NITRATE")
397
  df["has_ph"] = params_upper.str.contains("PH_IN_SITU")
398
+
399
  df.to_parquet(cache_path, index=False)
400
  return df
401
+
402
+
403
  @st.cache_data(show_spinner="Loading float metadata index …")
404
  def load_meta_data():
405
  """Load ar_index_global_meta.txt with Parquet cache (24-h TTL).
406
+
407
  Provides one row per float (WMO) with profiler_type, institution,
408
  dac, and a human-readable profiler_name from WMO R08.
409
  """
410
  CACHE_DIR.mkdir(exist_ok=True)
411
  cache_path = CACHE_DIR / "meta.parquet"
412
+
413
  if cache_path.exists():
414
+ use_cache = not META_FILE.exists()
415
+ if not use_cache:
416
+ age_h = (datetime.now().timestamp() - cache_path.stat().st_mtime) / 3600
417
+ use_cache = age_h < 24
418
+ if use_cache:
419
  return pd.read_parquet(cache_path)
420
 
421
  df = pd.read_csv(META_FILE, comment="#")
 
794
  c1, c2, c3 = st.columns(3)
795
  with c1:
796
  fig = plot_utils.create_ts_diagram(cycles, temp, psal, wmo, title=f"T/S Diagram<br><sup>{date_suffix}</sup>")
797
+ st.plotly_chart(fig, width="stretch")
798
  with c2:
799
  fig = plot_utils.create_section_chart(dates, pres, temp, "Temperature (Β°C)", f"Section chart TEMP<br><sup>{date_suffix}</sup>", wmo)
800
+ st.plotly_chart(fig, width="stretch")
801
  with c3:
802
  fig = plot_utils.create_section_chart(dates, pres, psal, "Salinity (PSU)", f"Section chart PSAL<br><sup>{date_suffix}</sup>", wmo)
803
+ st.plotly_chart(fig, width="stretch")
804
 
805
  c4, c5, c6 = st.columns(3)
806
  with c4:
807
  fig = plot_utils.create_section_chart(dates, pres, rho, "Potential Density (kg/mΒ³)", f"Section chart RHO<br><sup>{date_suffix}</sup>", wmo)
808
+ st.plotly_chart(fig, width="stretch")
809
  with c5:
810
  fig = plot_utils.create_overlaid_profiles(temp, pres, cycles, "Temperature (Β°C)", f"Overlaid profiles TEMP<br><sup>{date_suffix}</sup>", wmo)
811
+ st.plotly_chart(fig, width="stretch")
812
  with c6:
813
  fig = plot_utils.create_overlaid_profiles(psal, pres, cycles, "Salinity (PSU)", f"Overlaid profiles PSAL<br><sup>{date_suffix}</sup>", wmo)
814
+ st.plotly_chart(fig, width="stretch")
815
 
816
  c7, c8, c9 = st.columns(3)
817
  with c7:
818
  fig = plot_utils.create_overlaid_profiles(rho, pres, cycles, "Potential Density (kg/mΒ³)", f"Overlaid profiles RHO<br><sup>{date_suffix}</sup>", wmo)
819
+ st.plotly_chart(fig, width="stretch")
820
  else:
821
  st.info("No valid profile data available for technical plots.")
822
  except Exception as e:
 
942
  st.markdown("## πŸ” Filters")
943
 
944
  # ── Refresh ──
945
+ if st.button("πŸ”„ Refresh Data", width="stretch", type="primary"):
946
  for f in CACHE_DIR.glob("*.parquet"):
947
  f.unlink()
948
  st.cache_data.clear()
 
1385
  )
1386
 
1387
  fig_map.update_layout(height=620)
1388
+ st.plotly_chart(fig_map, width="stretch", key="main_map", on_select="rerun", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_float_map"}})
1389
 
1390
  if selected_wmo_from_map:
1391
  if st.button(f"πŸ“„ View Info for Float {selected_wmo_from_map}"):
 
1477
  margin=dict(l=50, r=20, t=80, b=40),
1478
  )
1479
  )
1480
+ st.plotly_chart(fig_bar, width="stretch", key="bar_chart", on_select="rerun", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_annual_floats"}})
1481
  else:
1482
  st.info("No active float data for bar chart.")
1483
  else:
 
1580
  **_dark_layout(margin=dict(l=0, r=0, t=10, b=0)),
1581
  coloraxis_showscale=False,
1582
  )
1583
+ st.plotly_chart(fig_tree, width="stretch", key="treemap", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_last1day_treemap"}})
1584
  else:
1585
  st.info("No active floats in the last 1 day for current filters.")
1586
  else:
 
1662
  bgcolor="rgba(0,0,0,0)",
1663
  ),
1664
  )
1665
+ st.plotly_chart(fig_donut, width="stretch", key="donut", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_age_distribution"}})
1666
  else:
1667
  st.info("No age data available.")
1668
  else:
 
1727
  bgcolor="rgba(0,0,0,0)",
1728
  ),
1729
  )
1730
+ st.plotly_chart(fig_ptype, width="stretch", key="profiler_donut", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_profiler_types"}})
1731
  st.caption(f"πŸ“‹ {len(df_meta):,} floats across {df_meta['profiler_name'].nunique()} instrument models (source: ar_index_global_meta.txt)")
1732
  else:
1733
  st.info("No metadata available.")
 
1777
  margin=dict(l=50, r=20, t=60, b=40),
1778
  ),
1779
  )
1780
+ st.plotly_chart(fig_fleet, width="stretch", key="fleet_composition", config={"toImageButtonOptions": {"format": "png", "scale": 2, "filename": "argo_fleet_composition"}})
1781
  st.caption("Shows how the fleet instrument mix has evolved per deployment year")
1782
  else:
1783
  st.info("No deployment data available.")
 
1877
  height=160,
1878
  )
1879
  )
1880
+ st.plotly_chart(fig_global, width="stretch", key="global_status_bar", config={"displayModeBar": False})
1881
 
1882
  with col_dac2:
1883
  st.markdown("### πŸ“‘ Float Status Summary")
 
2177
  tab1, tab2, tab3 = st.tabs(["Core Profiles", "BGC Profiles", "Float Metadata"])
2178
  with tab1:
2179
  st.dataframe(
2180
+ filt_prof.head(200), width="stretch", hide_index=True
2181
  )
2182
  st.caption(
2183
  f"Showing {min(200, len(filt_prof)):,} of {len(filt_prof):,} records"
 
2191
  )
2192
  with tab2:
2193
  st.dataframe(
2194
+ filt_bio.head(200), width="stretch", hide_index=True
2195
  )
2196
  st.caption(
2197
  f"Showing {min(200, len(filt_bio)):,} of {len(filt_bio):,} records"
 
2205
  )
2206
  with tab3:
2207
  st.dataframe(
2208
+ df_meta.head(500), width="stretch", hide_index=True
2209
  )
2210
  st.caption(
2211
  f"Showing {min(500, len(df_meta)):,} of {len(df_meta):,} float metadata records (source: ar_index_global_meta.txt)"
streamlit/dashboard_example.py CHANGED
@@ -239,7 +239,7 @@ with col1:
239
  for _, r in totals.iterrows():
240
  fig_bar.add_annotation(x=r['Year'], y=r['Total_Floats'], text=str(int(r['Total_Floats'])), showarrow=False, yshift=10)
241
  fig_bar.update_layout(barmode='stack', xaxis=dict(dtick=1), height=520)
242
- st.plotly_chart(fig_bar, use_container_width=True)
243
 
244
  # Donut chart: age distribution of alive floats (bio dataset)
245
  with col2:
@@ -255,7 +255,7 @@ with col2:
255
  if not age_counts.empty:
256
  fig_donut = px.pie(age_counts, names='Age_Years', values='Count', hole=0.55, title='Age (years) distribution of alive floats')
257
  fig_donut.update_traces(textinfo='percent+label')
258
- st.plotly_chart(fig_donut, use_container_width=True)
259
  else:
260
  st.write("No alive float age groups available for selected filters.")
261
  else:
@@ -331,7 +331,7 @@ else:
331
  )
332
 
333
  fig_map.update_layout(height=650)
334
- st.plotly_chart(fig_map, use_container_width=True)
335
  # ------------------------------
336
  # DAC summary table (bio dataset) - bottom
337
  # Live = age_days >= 90, Dead = age_days < 90, Total = unique floats
@@ -352,7 +352,7 @@ summary_table = g.groupby('DAC').agg(
352
 
353
  # present in requested order: DAC | Live | Dead | Total
354
  summary_table = summary_table[['DAC','Live','Dead','Total']]
355
- st.dataframe(summary_table, use_container_width=True)
356
 
357
  # Compact text lines
358
  st.markdown("**Compact summary (DAC β€” Live / Dead / Total)**")
 
239
  for _, r in totals.iterrows():
240
  fig_bar.add_annotation(x=r['Year'], y=r['Total_Floats'], text=str(int(r['Total_Floats'])), showarrow=False, yshift=10)
241
  fig_bar.update_layout(barmode='stack', xaxis=dict(dtick=1), height=520)
242
+ st.plotly_chart(fig_bar, width="stretch")
243
 
244
  # Donut chart: age distribution of alive floats (bio dataset)
245
  with col2:
 
255
  if not age_counts.empty:
256
  fig_donut = px.pie(age_counts, names='Age_Years', values='Count', hole=0.55, title='Age (years) distribution of alive floats')
257
  fig_donut.update_traces(textinfo='percent+label')
258
+ st.plotly_chart(fig_donut, width="stretch")
259
  else:
260
  st.write("No alive float age groups available for selected filters.")
261
  else:
 
331
  )
332
 
333
  fig_map.update_layout(height=650)
334
+ st.plotly_chart(fig_map, width="stretch")
335
  # ------------------------------
336
  # DAC summary table (bio dataset) - bottom
337
  # Live = age_days >= 90, Dead = age_days < 90, Total = unique floats
 
352
 
353
  # present in requested order: DAC | Live | Dead | Total
354
  summary_table = summary_table[['DAC','Live','Dead','Total']]
355
+ st.dataframe(summary_table, width="stretch")
356
 
357
  # Compact text lines
358
  st.markdown("**Compact summary (DAC β€” Live / Dead / Total)**")
streamlit/hello.py CHANGED
@@ -115,7 +115,7 @@ with col1:
115
  mapbox=dict(center=dict(lat=-10, lon=80), zoom=3)
116
  )
117
 
118
- st.plotly_chart(fig_map, use_container_width=True)
119
 
120
  # ---------------- KPI ----------------
121
  with col2:
@@ -148,4 +148,4 @@ fig_bar = px.bar(
148
  y="count"
149
  )
150
 
151
- st.plotly_chart(fig_bar, use_container_width=True)
 
115
  mapbox=dict(center=dict(lat=-10, lon=80), zoom=3)
116
  )
117
 
118
+ st.plotly_chart(fig_map, width="stretch")
119
 
120
  # ---------------- KPI ----------------
121
  with col2:
 
148
  y="count"
149
  )
150
 
151
+ st.plotly_chart(fig_bar, width="stretch")