136 lines
4.8 KiB
Python
136 lines
4.8 KiB
Python
"""This module provides functions for generating mass balance reports."""
|
|
|
|
from pathlib import Path
|
|
|
|
import plotly.graph_objects as go
|
|
from jinja2 import Template
|
|
from plotly.io import to_html
|
|
from datetime import datetime, timedelta
|
|
|
|
import yaml
|
|
|
|
import logging
|
|
from . import plotting
|
|
|
|
|
|
import json
|
|
import numpy as np
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def mass_balance_report(
|
|
krig_params: dict,
|
|
wind_fig: go.Figure,
|
|
background_fig: go.Figure,
|
|
threed_fig: go.Figure,
|
|
krig_fig: go.Figure,
|
|
windrose_fig: go.Figure,
|
|
) -> str:
|
|
"""Generate a mass balance report."""
|
|
template_path = Path(__file__).parent / "templates" / "mass_balance_template.html"
|
|
|
|
# Convert the figures to HTML
|
|
plot_htmls = {}
|
|
for name, fig in zip(
|
|
["3D", "krig", "windrose", "wind", "background"],
|
|
[threed_fig, krig_fig, windrose_fig, wind_fig, background_fig],
|
|
strict=False,
|
|
):
|
|
if fig:
|
|
plot_htmls[name] = to_html(fig, full_html=False)
|
|
else:
|
|
plot_htmls[name] = plotting.blank_figure()
|
|
|
|
summary_data = {
|
|
"Estimated flux": f"{krig_params.get('volume', 0):.3f} kgh⁻¹",
|
|
}
|
|
|
|
with Path.open(template_path) as f:
|
|
template_content = f.read()
|
|
|
|
template = Template(template_content)
|
|
return template.render(
|
|
title="Mass Balance Report",
|
|
summary_data=summary_data,
|
|
threeD=plot_htmls["3D"],
|
|
krig=plot_htmls["krig"],
|
|
windrose=plot_htmls["windrose"],
|
|
wind=plot_htmls["wind"],
|
|
background=plot_htmls["background"],
|
|
)
|
|
|
|
|
|
def generate_reports(name: str, processor, config: dict, output_dir: Path):
|
|
"""
|
|
Generates reports, configuration files, and processed output variables for gasflux processing runs.
|
|
|
|
Parameters:
|
|
name (str): The name identifier for the current processing run (task_id).
|
|
processor (object): The processing object containing report data and output variables.
|
|
config (dict): Configuration dictionary used for processing.
|
|
output_dir (Path): Output directory path (already includes task_id) from INI configuration.
|
|
"""
|
|
processing_time = datetime.now() + timedelta(hours=8) # Beijing time (UTC+8)
|
|
# Save directly to the output directory (already includes task_id)
|
|
output_path = output_dir
|
|
output_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Save reports
|
|
for gas, report in processor.reports.items():
|
|
timestamp_str = processing_time.strftime("%Y%m%d_%H%M%S")
|
|
report_path = output_path / f"{gas}_report_{timestamp_str}.html"
|
|
with open(report_path, "w", encoding="utf-8") as file:
|
|
file.write(report)
|
|
|
|
# Save config
|
|
header = f"# Gasflux output config for task {name} from processing run at {processing_time}\n"
|
|
timestamp_str = processing_time.strftime("%Y%m%d_%H%M%S")
|
|
config_path = output_path / f"config_{timestamp_str}.yaml"
|
|
with open(config_path, "w") as file:
|
|
file.write(header)
|
|
yaml.safe_dump(config, file)
|
|
|
|
# Save DataFrame to Excel
|
|
if hasattr(processor, 'df') and processor.df is not None:
|
|
timestamp_str = processing_time.strftime("%Y%m%d_%H%M%S")
|
|
excel_path = output_path / f"processed_data_{timestamp_str}.xlsx"
|
|
processor.df.to_excel(excel_path, index=False, engine='openpyxl')
|
|
logger.info(f"DataFrame saved to {excel_path}")
|
|
|
|
# Save output variables
|
|
output_vars = processor.output_vars
|
|
# output_vars = delete_large_arrays(output_vars, threshold_size=50)
|
|
header = (
|
|
f"# Gasflux output variables for task {name} from processing run at {processing_time}\n"
|
|
)
|
|
timestamp_str = processing_time.strftime("%Y%m%d_%H%M%S")
|
|
filename = output_path / f"output_vars_{timestamp_str}.json"
|
|
with open(filename, "w") as file:
|
|
file.write(header)
|
|
json.dump(
|
|
output_vars, file, default=lambda item: item.tolist() if isinstance(item, np.ndarray) else item, indent=4
|
|
)
|
|
logger.info(f"Task {name} results saved to {output_path}")
|
|
|
|
|
|
def delete_large_arrays(output_vars: dict, threshold_size: int) -> dict:
|
|
"""
|
|
Iterate through the output_vars dictionary and replace large numpy arrays
|
|
with their metadata (e.g., shape and data type).
|
|
|
|
Parameters:
|
|
output_vars (dict): The dictionary containing output data including potential numpy arrays.
|
|
threshold_size (int): The number of elements above which an array is considered large.
|
|
"""
|
|
del_keys = []
|
|
for key, value in output_vars.items():
|
|
if isinstance(value, dict):
|
|
output_vars[key] = delete_large_arrays(value, threshold_size) # recursive
|
|
elif isinstance(value, np.ndarray):
|
|
if value.size > threshold_size:
|
|
del_keys.append(key)
|
|
for key in del_keys:
|
|
del output_vars[key]
|
|
return output_vars
|