malcolmSQ commited on
Commit ·
bc8f256
1
Parent(s): 7b8bb25
Fix download functionality and import issues - Replace broken File components with working DownloadButton - Fix import paths to work without PYTHONPATH - Store raw data for proper CSV downloads - Update README with correct instructions - Improve UI layout by removing weird row layouts
Browse files- README.md +1 -3
- dashboard/app.py +97 -58
README.md
CHANGED
|
@@ -30,12 +30,10 @@ This project implements a flexible, modular dashboard for mangrove carbon seques
|
|
| 30 |
```
|
| 31 |
2. **Run the dashboard:**
|
| 32 |
```bash
|
| 33 |
-
|
| 34 |
```
|
| 35 |
The terminal will display a public share link.
|
| 36 |
|
| 37 |
-
**Note:** The `PYTHONPATH=.` is required to ensure the `er_model_core` module can be found.
|
| 38 |
-
|
| 39 |
## Growth Models
|
| 40 |
- **Declining Increment (ACTIVE):**
|
| 41 |
- Annual increment declines linearly to zero; total size is the sum of non-negative increments (never decreases or goes negative).
|
|
|
|
| 30 |
```
|
| 31 |
2. **Run the dashboard:**
|
| 32 |
```bash
|
| 33 |
+
python dashboard/app.py
|
| 34 |
```
|
| 35 |
The terminal will display a public share link.
|
| 36 |
|
|
|
|
|
|
|
| 37 |
## Growth Models
|
| 38 |
- **Declining Increment (ACTIVE):**
|
| 39 |
- Annual increment declines linearly to zero; total size is the sum of non-negative increments (never decreases or goes negative).
|
dashboard/app.py
CHANGED
|
@@ -8,6 +8,12 @@ Mangrove ER Model Dashboard
|
|
| 8 |
|
| 9 |
import gradio as gr
|
| 10 |
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
from er_model_core.er_model import ERModel
|
| 12 |
import pandas as pd
|
| 13 |
import numpy as np
|
|
@@ -434,21 +440,17 @@ This dashboard visualizes these values annually over the project duration, provi
|
|
| 434 |
# --- Tabbed tables section ---
|
| 435 |
with gr.Tabs():
|
| 436 |
with gr.Tab("Project Results (Annual)"):
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
download_results_btn = gr.Button("📥 Download CSV", variant="secondary", scale=1)
|
| 440 |
with gr.Tab("Species Results (Annual)"):
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
download_species_btn = gr.Button("📥 Download CSV", variant="secondary", scale=1)
|
| 444 |
with gr.Tab("Surviving Trees Table"):
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
download_survival_btn = gr.Button("📥 Download CSV", variant="secondary", scale=1)
|
| 448 |
with gr.Tab("Biomass Table"):
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
download_biomass_btn = gr.Button("📥 Download CSV", variant="secondary", scale=1)
|
| 452 |
# --- End tabbed tables ---
|
| 453 |
# Update the update_declining_increment callback to use these new inputs
|
| 454 |
def update_declining_increment(y1, y2, y3, y4, y5,
|
|
@@ -488,6 +490,13 @@ This dashboard visualizes these values annually over the project duration, provi
|
|
| 488 |
species_results_fmt = to_native(species_results_fmt)
|
| 489 |
survival_table = to_native(survival_table)
|
| 490 |
biomass_debug_df = to_native(biomass_debug_df)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
# --- Ensure all other values are native types ---
|
| 492 |
if hasattr(summary, 'item'):
|
| 493 |
summary = summary.item()
|
|
@@ -501,70 +510,100 @@ This dashboard visualizes these values annually over the project duration, provi
|
|
| 501 |
)
|
| 502 |
|
| 503 |
# Download functionality for tables
|
| 504 |
-
def
|
| 505 |
-
if
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
return None
|
| 522 |
-
csv_data = df.to_csv(index=False)
|
| 523 |
-
return (csv_data, "project_results.csv")
|
| 524 |
|
| 525 |
-
def download_species_csv(
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 531 |
|
| 532 |
-
def download_survival_csv(
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
|
| 539 |
-
def download_biomass_csv(
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
|
| 544 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 545 |
|
| 546 |
download_results_btn.click(
|
| 547 |
download_results_csv,
|
| 548 |
-
inputs=[
|
| 549 |
-
outputs=[
|
| 550 |
)
|
| 551 |
|
| 552 |
download_species_btn.click(
|
| 553 |
download_species_csv,
|
| 554 |
-
inputs=[
|
| 555 |
-
outputs=[
|
| 556 |
)
|
| 557 |
|
| 558 |
download_survival_btn.click(
|
| 559 |
download_survival_csv,
|
| 560 |
-
inputs=[
|
| 561 |
-
outputs=[
|
| 562 |
)
|
| 563 |
|
| 564 |
download_biomass_btn.click(
|
| 565 |
download_biomass_csv,
|
| 566 |
-
inputs=[
|
| 567 |
-
outputs=[
|
| 568 |
)
|
| 569 |
|
| 570 |
# Show initial results
|
|
|
|
| 8 |
|
| 9 |
import gradio as gr
|
| 10 |
from pathlib import Path
|
| 11 |
+
import sys
|
| 12 |
+
import os
|
| 13 |
+
|
| 14 |
+
# Add the parent directory to Python path so we can import er_model_core
|
| 15 |
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
| 16 |
+
|
| 17 |
from er_model_core.er_model import ERModel
|
| 18 |
import pandas as pd
|
| 19 |
import numpy as np
|
|
|
|
| 440 |
# --- Tabbed tables section ---
|
| 441 |
with gr.Tabs():
|
| 442 |
with gr.Tab("Project Results (Annual)"):
|
| 443 |
+
results_box = gr.Dataframe(label="Project Results (Annual)")
|
| 444 |
+
download_results_btn = gr.DownloadButton("📥 Download CSV", variant="secondary")
|
|
|
|
| 445 |
with gr.Tab("Species Results (Annual)"):
|
| 446 |
+
species_box = gr.Dataframe(label="Species Results (Annual)")
|
| 447 |
+
download_species_btn = gr.DownloadButton("📥 Download CSV", variant="secondary")
|
|
|
|
| 448 |
with gr.Tab("Surviving Trees Table"):
|
| 449 |
+
survival_box = gr.Dataframe(label="Surviving Trees Table")
|
| 450 |
+
download_survival_btn = gr.DownloadButton("📥 Download CSV", variant="secondary")
|
|
|
|
| 451 |
with gr.Tab("Biomass Table"):
|
| 452 |
+
biomass_debug_table = gr.Dataframe(label="Biomass Table (inputs & outputs per year)")
|
| 453 |
+
download_biomass_btn = gr.DownloadButton("📥 Download CSV", variant="secondary")
|
|
|
|
| 454 |
# --- End tabbed tables ---
|
| 455 |
# Update the update_declining_increment callback to use these new inputs
|
| 456 |
def update_declining_increment(y1, y2, y3, y4, y5,
|
|
|
|
| 490 |
species_results_fmt = to_native(species_results_fmt)
|
| 491 |
survival_table = to_native(survival_table)
|
| 492 |
biomass_debug_df = to_native(biomass_debug_df)
|
| 493 |
+
|
| 494 |
+
# Store raw data for downloads (before formatting)
|
| 495 |
+
update_declining_increment._last_results = results
|
| 496 |
+
update_declining_increment._last_species_results = species_results
|
| 497 |
+
update_declining_increment._last_survival_table = model.species_metrics.pivot(index="Year", columns="Species", values="Surviving Trees").reset_index()
|
| 498 |
+
update_declining_increment._last_biomass_table = model.species_metrics
|
| 499 |
+
|
| 500 |
# --- Ensure all other values are native types ---
|
| 501 |
if hasattr(summary, 'item'):
|
| 502 |
summary = summary.item()
|
|
|
|
| 510 |
)
|
| 511 |
|
| 512 |
# Download functionality for tables
|
| 513 |
+
def download_results_csv():
|
| 514 |
+
if hasattr(update_declining_increment, '_last_results'):
|
| 515 |
+
df = update_declining_increment._last_results.copy()
|
| 516 |
+
# Remove formatting for CSV export
|
| 517 |
+
for col in df.columns:
|
| 518 |
+
if df[col].dtype == 'object':
|
| 519 |
+
try:
|
| 520 |
+
# Try to convert formatted strings back to numbers
|
| 521 |
+
df[col] = df[col].str.replace(',', '').astype(float)
|
| 522 |
+
except:
|
| 523 |
+
pass # Keep as string if conversion fails
|
| 524 |
+
|
| 525 |
+
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False)
|
| 526 |
+
df.to_csv(temp_file.name, index=False)
|
| 527 |
+
temp_file.close()
|
| 528 |
+
return temp_file.name
|
| 529 |
+
return None
|
|
|
|
|
|
|
|
|
|
| 530 |
|
| 531 |
+
def download_species_csv():
|
| 532 |
+
if hasattr(update_declining_increment, '_last_species_results'):
|
| 533 |
+
df = update_declining_increment._last_species_results.copy()
|
| 534 |
+
# Remove formatting for CSV export
|
| 535 |
+
for col in df.columns:
|
| 536 |
+
if df[col].dtype == 'object':
|
| 537 |
+
try:
|
| 538 |
+
# Try to convert formatted strings back to numbers
|
| 539 |
+
df[col] = df[col].str.replace(',', '').astype(float)
|
| 540 |
+
except:
|
| 541 |
+
pass # Keep as string if conversion fails
|
| 542 |
+
|
| 543 |
+
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False)
|
| 544 |
+
df.to_csv(temp_file.name, index=False)
|
| 545 |
+
temp_file.close()
|
| 546 |
+
return temp_file.name
|
| 547 |
+
return None
|
| 548 |
|
| 549 |
+
def download_survival_csv():
|
| 550 |
+
if hasattr(update_declining_increment, '_last_survival_table'):
|
| 551 |
+
df = update_declining_increment._last_survival_table.copy()
|
| 552 |
+
# Remove formatting for CSV export
|
| 553 |
+
for col in df.columns:
|
| 554 |
+
if df[col].dtype == 'object' and col != 'Year':
|
| 555 |
+
try:
|
| 556 |
+
# Try to convert formatted strings back to numbers
|
| 557 |
+
df[col] = df[col].str.replace(',', '').astype(float)
|
| 558 |
+
except:
|
| 559 |
+
pass # Keep as string if conversion fails
|
| 560 |
+
|
| 561 |
+
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False)
|
| 562 |
+
df.to_csv(temp_file.name, index=False)
|
| 563 |
+
temp_file.close()
|
| 564 |
+
return temp_file.name
|
| 565 |
+
return None
|
| 566 |
|
| 567 |
+
def download_biomass_csv():
|
| 568 |
+
if hasattr(update_declining_increment, '_last_biomass_table'):
|
| 569 |
+
df = update_declining_increment._last_biomass_table.copy()
|
| 570 |
+
# Remove formatting for CSV export - this table has complex column names
|
| 571 |
+
for col in df.columns:
|
| 572 |
+
if df[col].dtype == 'object':
|
| 573 |
+
try:
|
| 574 |
+
# Try to convert formatted strings back to numbers
|
| 575 |
+
df[col] = df[col].str.replace(',', '').astype(float)
|
| 576 |
+
except:
|
| 577 |
+
pass # Keep as string if conversion fails
|
| 578 |
+
|
| 579 |
+
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False)
|
| 580 |
+
df.to_csv(temp_file.name, index=False)
|
| 581 |
+
temp_file.close()
|
| 582 |
+
return temp_file.name
|
| 583 |
+
return None
|
| 584 |
|
| 585 |
download_results_btn.click(
|
| 586 |
download_results_csv,
|
| 587 |
+
inputs=[],
|
| 588 |
+
outputs=[]
|
| 589 |
)
|
| 590 |
|
| 591 |
download_species_btn.click(
|
| 592 |
download_species_csv,
|
| 593 |
+
inputs=[],
|
| 594 |
+
outputs=[]
|
| 595 |
)
|
| 596 |
|
| 597 |
download_survival_btn.click(
|
| 598 |
download_survival_csv,
|
| 599 |
+
inputs=[],
|
| 600 |
+
outputs=[]
|
| 601 |
)
|
| 602 |
|
| 603 |
download_biomass_btn.click(
|
| 604 |
download_biomass_csv,
|
| 605 |
+
inputs=[],
|
| 606 |
+
outputs=[]
|
| 607 |
)
|
| 608 |
|
| 609 |
# Show initial results
|