added minimal report

This commit is contained in:
bolade
2025-11-26 22:17:30 +01:00
parent 9d61ebb533
commit 4406b2013d
12 changed files with 23639 additions and 80 deletions
+201 -69
View File
@@ -1112,9 +1112,17 @@ class ContextGenerator:
graphs: Dict[str, str],
metric_overrides: Optional[Dict] = None,
graph_generator: Optional[Any] = None,
report_type: str = "full",
) -> Dict[str, Dict]:
"""Main method to generate all page contexts
Args:
patient_name: Patient name
graphs: Dictionary of graph data
metric_overrides: Optional metric overrides
graph_generator: Optional graph generator instance
report_type: Type of report ("full" or "minimal")
Returns:
Dictionary with keys 'page_1', 'page_2', etc., each containing context data for that page
"""
@@ -1133,60 +1141,156 @@ class ContextGenerator:
contexts = {}
# Define which pages to generate based on report type
if report_type == "minimal":
# Minimal report only needs pages: 1, 2, 4, 5, 6, 16, 17, 19, 20
# But we'll generate contexts for all needed pages and combine 19+20
pages_to_generate = [1, 2, 4, 5, 6, 16, 17, 19, 20]
else:
# Full report needs all pages 1-20
pages_to_generate = list(range(1, 21))
# Page 1
contexts["page_1"] = {
"name": self.patient_info["name"],
"surname": self.patient_info["last_name"],
"date": datetime.now().strftime("%B %d, %Y"),
}
# Page 2
contexts["page_2"] = {
"patient_name": self.patient_info["name"],
"test_date": datetime.now().strftime("%B %d, %Y"),
}
# Pages 3, 6 (pages 4 and 5 are handled separately)
for i in [0, 3]: # Skip indices 1 and 2 which are pages 4 and 5
contexts[f"page_{i + 3}"] = {
"patient_name": self.patient_info["name"],
"page_number": i + 3,
if 1 in pages_to_generate:
contexts["page_1"] = {
"name": self.patient_info["name"],
"surname": self.patient_info["last_name"],
"date": datetime.now().strftime("%B %d, %Y"),
}
# Page 2
if 2 in pages_to_generate:
contexts["page_2"] = {
"patient_name": self.patient_info["name"],
"test_date": datetime.now().strftime("%B %d, %Y"),
}
# Pages 3, 6 (pages 4 and 5 are handled separately)
if report_type == "full":
for i in [0, 3]: # Skip indices 1 and 2 which are pages 4 and 5
contexts[f"page_{i + 3}"] = {
"patient_name": self.patient_info["name"],
"page_number": i + 3,
}
# Page 4 - Nutrition Guidelines with Body Composition
contexts["page_4"] = {
"patient_name": self.patient_info["name"],
"page_number": 4,
"fat_percentage": f"{self.patient_info['fat_percentage']:.1f}",
"body_composition_chart": graphs.get("body_composition", ""),
"body_fat_chart": graphs.get("body_fat_percent", ""), # Alias for template
"body_fat_percent_chart": graphs.get(
"body_fat_percent", ""
), # Keep for consistency
}
if 4 in pages_to_generate:
contexts["page_4"] = {
"patient_name": self.patient_info["name"],
"page_number": 4,
"fat_percentage": f"{self.patient_info['fat_percentage']:.1f}",
"body_composition_chart": graphs.get("body_composition", ""),
"body_fat_chart": graphs.get("body_fat_percent", ""), # Alias for template
"body_fat_percent_chart": graphs.get(
"body_fat_percent", ""
), # Keep for consistency
}
# Page 5 - Resting Metabolic Rate Assessment
contexts["page_5"] = {
"patient_name": self.patient_info["name"],
"page_number": 5,
"metabolism_chart": graphs.get("metabolism_chart", ""),
"fuel_source_chart": graphs.get("fuel_source_chart", ""),
"resting_calories": rmr_metrics.get("resting_calories", 1500),
"neat_calories": rmr_metrics.get("neat_calories", 375),
"weight_loss_calories": rmr_metrics.get("weight_loss_calories", 500),
"weight_loss_rate": rmr_metrics.get("weight_loss_rate", 1.0),
"total_calories": rmr_metrics.get("total_calories", 1375),
}
if 5 in pages_to_generate:
contexts["page_5"] = {
"patient_name": self.patient_info["name"],
"page_number": 5,
"metabolism_chart": graphs.get("metabolism_chart", ""),
"fuel_source_chart": graphs.get("fuel_source_chart", ""),
"resting_calories": rmr_metrics.get("resting_calories", 1500),
"neat_calories": rmr_metrics.get("neat_calories", 375),
"weight_loss_calories": rmr_metrics.get("weight_loss_calories", 500),
"weight_loss_rate": rmr_metrics.get("weight_loss_rate", 1.0),
"total_calories": rmr_metrics.get("total_calories", 1375),
}
# For minimal reports, also generate resting heart rate table for page_5
if report_type == "minimal" and graph_generator:
resting_hr_metrics = self._calculate_resting_heart_rate_metrics()
rhr_table_info = self._calculate_rhr_table_data(
self.patient_info["age"], self.patient_info["gender"]
)
# Get resting heart rate value and determine category
rhr_value_str = resting_hr_metrics.get("resting_heart_rate", "0bpm")
rhr_value = float(rhr_value_str.replace("bpm", "").strip())
category = self._determine_rhr_category(
rhr_value,
self.patient_info["age"],
self.patient_info["gender"],
)
gender_label = (
"F" if self.patient_info["gender"].lower().startswith("f") else "M"
)
age_range_label = f"{rhr_table_info['age_range']} ({gender_label})"
rhr_columns = [
"Age",
"Poor",
"Below Average",
"Average",
"Above Average",
"Good",
"Excellent",
"Athlete",
]
rhr_data = [
[
age_range_label,
rhr_table_info["ranges"]["Poor"],
rhr_table_info["ranges"]["Below Average"],
rhr_table_info["ranges"]["Average"],
rhr_table_info["ranges"]["Above Average"],
rhr_table_info["ranges"]["Good"],
rhr_table_info["ranges"]["Excellent"],
rhr_table_info["ranges"]["Athlete"],
]
]
contexts["page_5"]["rhr_table"] = (
graph_generator.generate_resting_heart_rate_table(
data=rhr_data,
columns=rhr_columns,
rhr_value=rhr_value,
category=category,
save_as_base64=True,
)
)
# Calculate FEV1 percentage for page 7
fev1_percentage = 0
if spirometry_metrics.get("fvc_best"):
fev1_percentage = (
pnoe_metrics["peak_vt"] / spirometry_metrics["fvc_best"]
) * 100
# Page 6 - Meal Plan (needed for both full and minimal)
if 6 in pages_to_generate:
contexts["page_6"] = {
"patient_name": self.patient_info["name"],
"page_number": 6,
"deficit_calories": rmr_metrics.get("total_calories", 1600),
"deficit_protein": f"{int(rmr_metrics.get('total_calories', 1600) * 0.22 / 4)}g Protein",
"deficit_carbs": f"{int(rmr_metrics.get('total_calories', 1600) * 0.39 / 4)}g Carbs",
"deficit_fat": f"{int(rmr_metrics.get('total_calories', 1600) * 0.39 / 9)}g Fat",
"deficit_fiber": "24g Fibre",
"refeed_weekday_calories": int(rmr_metrics.get("total_calories", 1600) * 0.85),
"refeed_weekday_protein": f"{int(rmr_metrics.get('total_calories', 1600) * 0.85 * 0.22 / 4)}g Protein",
"refeed_weekday_carbs": f"{int(rmr_metrics.get('total_calories', 1600) * 0.85 * 0.39 / 4)}g Carbs",
"refeed_weekday_fat": f"{int(rmr_metrics.get('total_calories', 1600) * 0.85 * 0.39 / 9)}g Fat",
"refeed_weekday_fiber": "20g Fibre",
"refeed_weekend_calories": int(rmr_metrics.get("total_calories", 1600) * 1.375),
"refeed_weekend_protein": f"{int(rmr_metrics.get('total_calories', 1600) * 1.375 * 0.22 / 4)}g Protein",
"refeed_weekend_carbs": f"{int(rmr_metrics.get('total_calories', 1600) * 1.375 * 0.39 / 4)}g Carbs",
"refeed_weekend_fat": f"{int(rmr_metrics.get('total_calories', 1600) * 1.375 * 0.39 / 9)}g Fat",
"refeed_weekend_fiber": "33g Fibre",
"protein_percentage": "22%",
"carbs_percentage": "39%",
"fats_percentage": "39%",
}
# Page 7
contexts["page_7"] = {
# Only generate pages 7-15 and 18 for full reports
if report_type == "full":
# Calculate FEV1 percentage for page 7
fev1_percentage = 0
if spirometry_metrics.get("fvc_best"):
fev1_percentage = (
pnoe_metrics["peak_vt"] / spirometry_metrics["fvc_best"]
) * 100
# Page 7
contexts["page_7"] = {
"peak_vt": f"{pnoe_metrics['peak_vt']:.2f}",
"peak_vt_bpm": f"{int(pnoe_metrics['peak_vt_hr'])}",
"fev1_percentage": f"{fev1_percentage:.1f}",
@@ -1410,32 +1514,60 @@ class ContextGenerator:
except Exception as e:
print(f"Warning: Could not generate muscle oxygenation chart: {e}")
# Pages 14-18 (previously 13-17)
for i in range(1, 6):
page_num = i + 13
contexts[f"page_{page_num}"] = {
# Pages 14-18 (previously 13-17)
for i in range(1, 6):
page_num = i + 13
contexts[f"page_{page_num}"] = {
"patient_name": self.patient_info["name"],
"page_number": page_num,
}
# Add next_testing_date to page 16
if page_num == 16:
contexts["page_16"]["next_testing_date"] = self.patient_info.get(
"next_testing_date", "Contact us for scheduling"
)
# Page 16 - Next Steps (needed for both full and minimal)
if 16 in pages_to_generate:
contexts["page_16"] = {
"patient_name": self.patient_info["name"],
"page_number": page_num,
}
# Add next_testing_date to page 16
if page_num == 16:
contexts["page_16"]["next_testing_date"] = self.patient_info.get(
"page_number": 16,
"next_testing_date": self.patient_info.get(
"next_testing_date", "Contact us for scheduling"
)
),
}
# Page 19 - Glossary with Body Fat Percentage Master Chart (previously page 18)
contexts["page_19"] = {
"patient_name": self.patient_info["name"],
"page_number": 19,
"body_fat_percentage_chart": graphs.get(
"body_fat_percentage_master_chart", ""
),
}
# Page 17 - Glossary (needed for both full and minimal, but minimal uses different template)
if 17 in pages_to_generate:
contexts["page_17"] = {
"patient_name": self.patient_info["name"],
"page_number": 17,
}
# Page 20 (previously page 19)
contexts["page_20"] = {
"patient_name": self.patient_info["name"],
"page_number": 20,
}
# Page 19 - Glossary with Body Fat Percentage Master Chart
if 19 in pages_to_generate:
contexts["page_19"] = {
"patient_name": self.patient_info["name"],
"page_number": 19,
"body_fat_percentage_chart": graphs.get(
"body_fat_percentage_master_chart", ""
),
}
# Page 20 - Resting Heart Rate Table
if 20 in pages_to_generate:
contexts["page_20"] = {
"patient_name": self.patient_info["name"],
"page_number": 20,
}
# For minimal reports, create combined context for page_19_20_minimal
if report_type == "minimal" and 19 in pages_to_generate and 20 in pages_to_generate:
contexts["page_19_20_minimal"] = {
"patient_name": self.patient_info["name"],
"body_fat_percentage_chart": graphs.get(
"body_fat_percentage_master_chart", ""
),
}
return contexts