feat: Enhance context generation with new table images for VO2 Max and Heart Rate Zones

- Added functionality to generate VO2 Max and Heart Rate Zones tables in the context_generator.py.
- Integrated graph_generator to create table images with specified data and styles.
- Updated report_generator.py to pass graph_generator to context generation.
- Introduced a new method in graph_generator.py to generate table images with customizable options.
- Created test scripts for Page 5 (RMR and NEAT calculations) and Page 6 (Meal Plan calculations) using actual patient data.
- Updated Jupyter notebook metadata for better environment identification.
This commit is contained in:
bolade
2025-11-21 11:38:43 +01:00
parent 9d51b006c0
commit 47f0c6f3fb
8 changed files with 825 additions and 294 deletions
+83
View File
@@ -1305,3 +1305,86 @@ class GraphGenerator:
plt.close()
return self._image_to_base64(chart_path) if save_as_base64 else str(chart_path)
def generate_table_image(
self,
data: list[list],
columns: list[str],
title: str = None,
col_widths: list[float] = None,
cell_colors: list[list[str]] = None,
header_color: str = "#4dd0e1",
save_as_base64: bool = True,
) -> str:
"""
Generate a table as an image.
Args:
data: List of rows (each row is a list of values)
columns: List of column headers
title: Optional title for the table
col_widths: Optional list of column widths
cell_colors: Optional matrix of cell colors (same shape as data)
header_color: Color for the header row
save_as_base64: If True, return base64 string
Returns:
Base64 string or file path
"""
# Calculate figure size based on rows and columns
# Approximate height: header + rows
height = (len(data) + 1) * 0.5 + (0.5 if title else 0)
width = len(columns) * 2.5 if not col_widths else sum(col_widths) * 10
fig, ax = plt.subplots(figsize=(width, height))
ax.axis("off")
if title:
plt.title(title, pad=20, fontsize=14, fontweight="bold")
# Create table
table = ax.table(
cellText=data,
colLabels=columns,
cellLoc="center",
loc="center",
colColours=[header_color] * len(columns),
)
# Style the table
table.auto_set_font_size(False)
table.set_fontsize(10)
table.scale(1, 1.5) # Increase row height
# Apply cell colors if provided
if cell_colors:
for i, row_colors in enumerate(cell_colors):
for j, color in enumerate(row_colors):
if color:
# (row_idx, col_idx) - row_idx starts at 1 for data (0 is header)
cell = table[(i + 1, j)]
cell.set_facecolor(color)
# Bold headers
for (row, col), cell in table.get_celld().items():
if row == 0:
cell.set_text_props(weight="bold")
cell.set_height(0.1)
plt.tight_layout()
if save_as_base64:
import io
buf = io.BytesIO()
plt.savefig(buf, format="png", bbox_inches="tight", dpi=300)
plt.close(fig)
buf.seek(0)
return base64.b64encode(buf.read()).decode("utf-8")
else:
output_path = (
self.charts_dir / f"table_{pd.Timestamp.now().timestamp()}.png"
)
plt.savefig(output_path, bbox_inches="tight", dpi=300)
plt.close(fig)
return str(output_path)