malcolmSQ commited on
Commit
efb9a96
·
1 Parent(s): bc8f256

Fix download functionality with working CSV generation - Implement proper download functions that generate fresh CSV files - Use current parameter values to regenerate data for download - Create properly formatted CSV files with clean numeric data - Fix button click handlers to actually work - Update environment with correct Gradio version

Browse files
Files changed (4) hide show
  1. README.md +5 -0
  2. dashboard/app.py +145 -77
  3. environment.yml +16 -0
  4. requirements.txt +1 -1
README.md CHANGED
@@ -25,11 +25,16 @@ This project implements a flexible, modular dashboard for mangrove carbon seques
25
 
26
  1. **Install dependencies:**
27
  ```bash
 
28
  conda env create -f environment.yml
29
  conda activate er-model
 
 
 
30
  ```
31
  2. **Run the dashboard:**
32
  ```bash
 
33
  python dashboard/app.py
34
  ```
35
  The terminal will display a public share link.
 
25
 
26
  1. **Install dependencies:**
27
  ```bash
28
+ # Method 1: Using conda (recommended)
29
  conda env create -f environment.yml
30
  conda activate er-model
31
+
32
+ # Method 2: Using pip in existing environment
33
+ pip install -r requirements.txt
34
  ```
35
  2. **Run the dashboard:**
36
  ```bash
37
+ conda activate er-model # if using conda
38
  python dashboard/app.py
39
  ```
40
  The terminal will display a public share link.
dashboard/app.py CHANGED
@@ -441,16 +441,20 @@ This dashboard visualizes these values annually over the project duration, provi
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,
@@ -491,12 +495,6 @@ This dashboard visualizes these values annually over the project duration, provi
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,100 +508,170 @@ This dashboard visualizes these values annually over the project duration, provi
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
 
441
  with gr.Tabs():
442
  with gr.Tab("Project Results (Annual)"):
443
  results_box = gr.Dataframe(label="Project Results (Annual)")
444
+ download_results_file = gr.File(label="Download CSV", visible=False)
445
+ download_results_btn = gr.Button("📥 Download CSV", variant="secondary")
446
  with gr.Tab("Species Results (Annual)"):
447
  species_box = gr.Dataframe(label="Species Results (Annual)")
448
+ download_species_file = gr.File(label="Download CSV", visible=False)
449
+ download_species_btn = gr.Button("📥 Download CSV", variant="secondary")
450
  with gr.Tab("Surviving Trees Table"):
451
  survival_box = gr.Dataframe(label="Surviving Trees Table")
452
+ download_survival_file = gr.File(label="Download CSV", visible=False)
453
+ download_survival_btn = gr.Button("📥 Download CSV", variant="secondary")
454
  with gr.Tab("Biomass Table"):
455
  biomass_debug_table = gr.Dataframe(label="Biomass Table (inputs & outputs per year)")
456
+ download_biomass_file = gr.File(label="Download CSV", visible=False)
457
+ download_biomass_btn = gr.Button("📥 Download CSV", variant="secondary")
458
  # --- End tabbed tables ---
459
  # Update the update_declining_increment callback to use these new inputs
460
  def update_declining_increment(y1, y2, y3, y4, y5,
 
495
  survival_table = to_native(survival_table)
496
  biomass_debug_df = to_native(biomass_debug_df)
497
 
 
 
 
 
 
 
498
  # --- Ensure all other values are native types ---
499
  if hasattr(summary, 'item'):
500
  summary = summary.item()
 
508
  )
509
 
510
  # Download functionality for tables
511
+ def create_download_file(df, filename):
512
+ """Create a temporary CSV file for download"""
513
+ import tempfile
514
+ import os
515
+
516
+ # Create a temporary file
517
+ temp_dir = tempfile.gettempdir()
518
+ filepath = os.path.join(temp_dir, filename)
519
+
520
+ # Convert formatted strings back to numbers where possible
521
+ if df is not None and not df.empty:
522
+ df_clean = df.copy()
523
+ for col in df_clean.columns:
524
+ if df_clean[col].dtype == 'object' and col.lower() != 'year':
525
  try:
526
  # Try to convert formatted strings back to numbers
527
+ df_clean[col] = df_clean[col].astype(str).str.replace(',', '').astype(float)
528
  except:
529
  pass # Keep as string if conversion fails
530
 
531
+ df_clean.to_csv(filepath, index=False)
532
+ return filepath
 
 
533
  return None
534
+
535
+ def download_results_csv(y1, y2, y3, y4, y5, planting_density_A, planting_density_B, *mortality_inputs_and_soil_carbon):
536
+ """Generate and download project results CSV"""
537
+ try:
538
+ # Recreate the results using current parameters
539
+ *mortality_inputs, soil_carbon = mortality_inputs_and_soil_carbon
540
+ config = update_planting_schedule(MODEL_CONFIGS["Declining Increment"], [y1, y2, y3, y4, y5])
541
+ config['species'][0]['planting_density'] = planting_density_A
542
+ config['species'][1]['planting_density'] = planting_density_B
543
+ for i in [0, 1]:
544
+ for yr_idx, yr in enumerate(range(1, 11)):
545
+ config['species'][i]['mortality_rates'][f'year_{yr}'] = mortality_inputs[yr_idx]
546
+ config['species'][i]['mortality_rates']['subsequent'] = mortality_inputs[10]
547
+ config['carbon']['soil_carbon_per_ha_per_year'] = soil_carbon
548
+
549
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as tmp:
550
+ yaml.dump(config, tmp)
551
+ tmp_path = tmp.name
552
+ model = ERModel(Path(tmp_path))
553
+ results, _ = model.run()
554
+
555
+ filepath = create_download_file(results, "project_results.csv")
556
+ return gr.File(value=filepath, visible=True) if filepath else gr.File(visible=False)
557
+ except Exception as e:
558
+ print(f"Error creating download: {e}")
559
+ return gr.File(visible=False)
560
 
561
+ def download_species_csv(y1, y2, y3, y4, y5, planting_density_A, planting_density_B, *mortality_inputs_and_soil_carbon):
562
+ """Generate and download species results CSV"""
563
+ try:
564
+ *mortality_inputs, soil_carbon = mortality_inputs_and_soil_carbon
565
+ config = update_planting_schedule(MODEL_CONFIGS["Declining Increment"], [y1, y2, y3, y4, y5])
566
+ config['species'][0]['planting_density'] = planting_density_A
567
+ config['species'][1]['planting_density'] = planting_density_B
568
+ for i in [0, 1]:
569
+ for yr_idx, yr in enumerate(range(1, 11)):
570
+ config['species'][i]['mortality_rates'][f'year_{yr}'] = mortality_inputs[yr_idx]
571
+ config['species'][i]['mortality_rates']['subsequent'] = mortality_inputs[10]
572
+ config['carbon']['soil_carbon_per_ha_per_year'] = soil_carbon
573
 
574
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as tmp:
575
+ yaml.dump(config, tmp)
576
+ tmp_path = tmp.name
577
+ model = ERModel(Path(tmp_path))
578
+ _, species_results = model.run()
579
+
580
+ filepath = create_download_file(species_results, "species_results.csv")
581
+ return gr.File(value=filepath, visible=True) if filepath else gr.File(visible=False)
582
+ except Exception as e:
583
+ print(f"Error creating download: {e}")
584
+ return gr.File(visible=False)
585
 
586
+ def download_survival_csv(y1, y2, y3, y4, y5, planting_density_A, planting_density_B, *mortality_inputs_and_soil_carbon):
587
+ """Generate and download survival table CSV"""
588
+ try:
589
+ *mortality_inputs, soil_carbon = mortality_inputs_and_soil_carbon
590
+ config = update_planting_schedule(MODEL_CONFIGS["Declining Increment"], [y1, y2, y3, y4, y5])
591
+ config['species'][0]['planting_density'] = planting_density_A
592
+ config['species'][1]['planting_density'] = planting_density_B
593
+ for i in [0, 1]:
594
+ for yr_idx, yr in enumerate(range(1, 11)):
595
+ config['species'][i]['mortality_rates'][f'year_{yr}'] = mortality_inputs[yr_idx]
596
+ config['species'][i]['mortality_rates']['subsequent'] = mortality_inputs[10]
597
+ config['carbon']['soil_carbon_per_ha_per_year'] = soil_carbon
598
 
599
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as tmp:
600
+ yaml.dump(config, tmp)
601
+ tmp_path = tmp.name
602
+ model = ERModel(Path(tmp_path))
603
+ model.run()
604
+ survival_table = create_survival_table(model)
605
+
606
+ # Convert survival table to raw numbers
607
+ df_raw = model.species_metrics.copy()
608
+ surv_raw = df_raw.pivot(index="Year", columns="Species", values="Surviving Trees")
609
+ surv_raw.columns = [SPECIES_DISPLAY_NAMES.get(sp, sp) for sp in surv_raw.columns]
610
+ surv_raw["Total Surviving Trees"] = surv_raw.sum(axis=1)
611
+ surv_raw = surv_raw.reset_index()
612
+
613
+ filepath = create_download_file(surv_raw, "surviving_trees.csv")
614
+ return gr.File(value=filepath, visible=True) if filepath else gr.File(visible=False)
615
+ except Exception as e:
616
+ print(f"Error creating download: {e}")
617
+ return gr.File(visible=False)
618
 
619
+ def download_biomass_csv(y1, y2, y3, y4, y5, planting_density_A, planting_density_B, *mortality_inputs_and_soil_carbon):
620
+ """Generate and download biomass table CSV"""
621
+ try:
622
+ *mortality_inputs, soil_carbon = mortality_inputs_and_soil_carbon
623
+ config = update_planting_schedule(MODEL_CONFIGS["Declining Increment"], [y1, y2, y3, y4, y5])
624
+ config['species'][0]['planting_density'] = planting_density_A
625
+ config['species'][1]['planting_density'] = planting_density_B
626
+ for i in [0, 1]:
627
+ for yr_idx, yr in enumerate(range(1, 11)):
628
+ config['species'][i]['mortality_rates'][f'year_{yr}'] = mortality_inputs[yr_idx]
629
+ config['species'][i]['mortality_rates']['subsequent'] = mortality_inputs[10]
630
+ config['carbon']['soil_carbon_per_ha_per_year'] = soil_carbon
631
 
632
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as tmp:
633
+ yaml.dump(config, tmp)
634
+ tmp_path = tmp.name
635
+ model = ERModel(Path(tmp_path))
636
+ model.run()
637
+
638
+ filepath = create_download_file(model.species_metrics, "biomass_table.csv")
639
+ return gr.File(value=filepath, visible=True) if filepath else gr.File(visible=False)
640
+ except Exception as e:
641
+ print(f"Error creating download: {e}")
642
+ return gr.File(visible=False)
643
 
644
+ # Connect download buttons to functions
645
  download_results_btn.click(
646
  download_results_csv,
647
+ inputs=[year_1, year_2, year_3, year_4, year_5,
648
+ planting_density_A, planting_density_B,
649
+ *mort_vars, mort_sub, soil_carbon],
650
+ outputs=[download_results_file]
651
  )
652
 
653
  download_species_btn.click(
654
  download_species_csv,
655
+ inputs=[year_1, year_2, year_3, year_4, year_5,
656
+ planting_density_A, planting_density_B,
657
+ *mort_vars, mort_sub, soil_carbon],
658
+ outputs=[download_species_file]
659
  )
660
 
661
  download_survival_btn.click(
662
  download_survival_csv,
663
+ inputs=[year_1, year_2, year_3, year_4, year_5,
664
+ planting_density_A, planting_density_B,
665
+ *mort_vars, mort_sub, soil_carbon],
666
+ outputs=[download_survival_file]
667
  )
668
 
669
  download_biomass_btn.click(
670
  download_biomass_csv,
671
+ inputs=[year_1, year_2, year_3, year_4, year_5,
672
+ planting_density_A, planting_density_B,
673
+ *mort_vars, mort_sub, soil_carbon],
674
+ outputs=[download_biomass_file]
675
  )
676
 
677
  # Show initial results
environment.yml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: er-model
2
+ channels:
3
+ - conda-forge
4
+ - defaults
5
+ dependencies:
6
+ - python=3.10
7
+ - pip
8
+ - pip:
9
+ - click>=8.1.7
10
+ - gradio>=4.0.0
11
+ - matplotlib>=3.8.2
12
+ - numpy>=1.26.3
13
+ - pandas>=2.2.0
14
+ - plotly>=5.0.0
15
+ - pyyaml>=6.0.1
16
+ - scipy>=1.12.0
requirements.txt CHANGED
@@ -1,5 +1,5 @@
1
  click>=8.1.7
2
- gradio>=3.41.2
3
  matplotlib>=3.8.2
4
  numpy>=1.26.3
5
  pandas>=2.2.0
 
1
  click>=8.1.7
2
+ gradio>=4.0.0
3
  matplotlib>=3.8.2
4
  numpy>=1.26.3
5
  pandas>=2.2.0