import pdfkit from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader("report_gen")) # Define templates and their unique contexts # pages = [ # ("page_1.html", {"name": "John Doe", "surname": "Moran", "date": "July 29, 2025"}), # ("page_2.html", {"content": "This is page 2 content"}), # ( # "page_3.html", # { # "patient_name": "Keirstyn Moran", # "age": "34", # "height": "5'4\"", # "weight": "123lbs", # "focus": "Endurance", # "fat_mass": "27.6lbs", # "fat_percentage": "22.4%", # "lean_mass": "95.4lbs", # "lean_percentage": "77.6%", # "body_fat_percent": "22.4%", # "age_range": "20-39", # "gender": "F", # "contact_email": "info@ishplabs.com", # "website": "www.ishplabs.com", # "social": "@ishplabs", # "page_number": "4", # "body_composition_chart": "../graphs/page_1_body_composition.png", # "body_fat_chart": "../graphs/page_1_body_fat.png", # }, # ), # ( # "page_4.html", # { # "patient_name": "Keirstyn Moran", # "age": "34", # "height": "5'4\"", # "weight": "123lbs", # "focus": "Endurance", # "contact_email": "info@ishplabs.com", # "website": "www.ishplabs.com", # "social": "@ishplabs", # "page_number": "3", # }, # ), # ( # "page_5.html", # { # "patient_name": "Keirstyn Moran", # "age": "34", # "height": "5'4\"", # "weight": "123lbs", # "focus": "Endurance", # "resting_calories": "1386kCals", # "fat_percentage": "33%", # "carb_percentage": "67%", # "neat_calories": "762kCals", # "weight_loss_calories": "423kCals", # "weight_loss_rate": "1.1lbs", # "total_calories": "~1725kCals", # "contact_email": "info@ishplabs.com", # "website": "www.ishplabs.com", # "social": "@ishplabs", # "page_number": "5", # }, # ), # ( # "page_6.html", # { # "patient_name": "Keirstyn Moran", # "age": "34", # "height": "5'4\"", # "weight": "123lbs", # "focus": "Endurance", # "deficit_calories": "1725KCals", # "deficit_protein": "120g Protein", # "deficit_carbs": "155g Carbs", # "deficit_fat": "69g Fat", # "deficit_fiber": "25g Fibre", # "refeed_weekday_calories": "1615KCals", # "refeed_weekday_protein": "120g Protein", # "refeed_weekday_carbs": "142g Carbs", # "refeed_weekday_fat": "63g Fat", # "refeed_weekday_fiber": "24g Fibre", # "refeed_weekend_calories": "2000KCals", # "refeed_weekend_protein": "120g Protein", # "refeed_weekend_carbs": "190g Carbs", # "refeed_weekend_fat": "84g Fat", # "refeed_weekend_fiber": "30g Fibre", # "protein_percentage": "28%", # "carbs_percentage": "36%", # "fats_percentage": "36%", # "contact_email": "info@ishplabs.com", # "website": "www.ishplabs.com", # "social": "@ishplabs", # "page_number": "6", # }, # ), # ( # "page_7.html", # { # "patient_name": "Keirstyn Moran", # "age": "34", # "height": "5'4\"", # "weight": "123lbs", # "focus": "Endurance", # "fvc_value": "4.24L → 112.0%", # "fev1_value": "3.26L → 103.3%", # "fev1_fvc_ratio": "76.89% → 91.8%", # "indication": "No Respiratory Capacity Limitation", # "respiratory_graph": "../graphs/respiratory_chart.png", # "peak_vt_value": "2.38L/Breath which occurs at 172bpm (Zone 3)", # "peak_vt_percentage": "73% of FEV1", # "contact_email": "info@ishplabs.com", # "website": "www.ishplabs.com", # "social": "@ishplabs", # "page_number": "7", # }, # ), # ( # "page_8.html", # { # "patient_name": "Keirstyn Moran", # "age": "34", # "height": "5'4\"", # "weight": "123lbs", # "focus": "Endurance", # "vo2_max_value": "49.5", # "vo2_max_percentile": "100th percentile", # "age_range": "30-39", # "very_poor_range": "19.0-24.1", # "poor_range": "24.1-28.2", # "fair_range": "28.2-32.2", # "good_range": "32.2-35.7", # "excellent_range": "35.7-45.8", # "superior_range": "45.8+", # "zone1_percentage": "55-65% of Max Heart Rate", # "zone2_percentage": "65-75% of Max Heart Rate", # "zone3_percentage": "80-85% of Max Heart Rate", # "zone4_percentage": "85-88% of Max Heart Rate", # "zone5_percentage": "90% of Max Heart Rate", # "zone1_bpm": "81-96bpm", # "zone2_bpm": "96-100bpm", # "zone3_bpm": "100-178bpm", # "zone4_bpm": "178-188bpm", # "zone5_bpm": "188-198bpm", # "zone1_speed": "3.5mph", # "zone2_speed": "3.5-4.0mph", # "zone3_speed": "4.0-6.5mph", # "zone4_speed": "6.5-7.0mph", # "zone5_speed": "7.0-8.0mph", # "zone1_incline": "2% Incline", # "zone2_incline": "2% Incline", # "zone3_incline": "2% Incline", # "zone4_incline": "2% Incline", # "zone5_incline": "2% Incline", # "zone1_pace": "10:39min/km Pace", # "zone2_pace": "10:39-9:19min/km Pace", # "zone3_pace": "9:19-5:44min/km Pace", # "zone4_pace": "5:44-5:20min/km Pace", # "zone5_pace": "5:20-4:40min/km Pace", # "zone1_calories": "4.4kcals/minute", # "zone2_calories": "5.9kcals/minute", # "zone3_calories": "9.4kcals/minute", # "zone4_calories": "12.5kcals/minute", # "zone5_calories": "12.8kcals/minute", # "zone1_carb": "Avg: 0.4g/min Carb Utilization", # "zone2_carb": "Avg: 0.6g/min Carb Utilization", # "zone3_carb": "Avg: 1.9g/min Carb Utilization", # "zone4_carb": "Avg: 2.9g/min Carb Utilization", # "zone5_carb": "Avg: 3.1g/min Carb Utilization", # "zone1_breaths": "Avg: 27 breaths", # "zone2_breaths": "Avg: 28 breaths", # "zone3_breaths": "Avg: 31 breaths", # "zone4_breaths": "Avg: 42 breaths", # "zone5_breaths": "Avg: 51 breaths", # "zone1_breath_range": "Ideal Range: 15-20 breaths", # "zone2_breath_range": "Ideal Range: 20-25 breaths", # "zone3_breath_range": "Ideal Range: 25-30 breaths", # "zone4_breath_range": "Ideal Range: 30-35 breaths", # "zone5_breath_range": "Ideal Range: 40+ breaths", # "contact_email": "info@ishplabs.com", # "website": "www.ishplabs.com", # "social": "@ishplabs", # "page_number": "8", # }, # ), # ( # "page_9.html", # { # "patient_name": "Keirstyn Moran", # "age": "34", # "height": "5'4\"", # "weight": "123lbs", # "focus": "Endurance", # "fuel_utilization_chart": "../graphs/fuel_utilization_chart.png", # "client_name": "Keirstyn Moran", # "assessment_date": "July 29 2025", # "contact_email": "info@ishplabs.com", # "website": "www.ishplabs.com", # "social": "@ishplabs", # "page_number": "9", # }, # ), # ( # "page_10.html", # { # "patient_name": "Keirstyn Moran", # "age": "34", # "height": "5'4\"", # "weight": "123lbs", # "focus": "Endurance", # "vo2_pulse_drop_bpm": "180 bpm", # "vo2_pulse_drop_zone": "Zone 4", # "vo2_pulse_chart": "../graphs/vo2_pulse_chart.png", # "vo2_breath_drop_bpm": "173 bpm", # "vo2_breath_drop_zone": "Zone 3", # "vo2_breath_chart": "../graphs/vo2_breath_chart.png", # "contact_email": "info@ishplabs.com", # "website": "www.ishplabs.com", # "social": "@ishplabs", # "page_number": "9", # }, # ), # ( # "page_11.html", # { # "patient_name": "Keirstyn Moran", # "age": "34", # "height": "5'4\"", # "weight": "123lbs", # "focus": "Endurance", # "fat_max_optimal": "*Optimal 10-12Kcals/minute", # "fat_max_value": "3.8Kcals/min", # "fat_max_heart_rate": "49% of Max Heart Rate", # "fat_max_bpm": "97 bpm", # "crossover_bpm": "100bpm", # "crossover_heart_rate": "51% of Max Heart Rate", # "fat_metabolism_note": "100bpm at a speed of 4.0mph and incline of 2%", # "fat_metabolism_chart": "../graphs/fat_metabolism_chart.png", # "cardiac_recovery_time": "(1 minute)", # "cardiac_recovery_percentage": "33%", # "metabolic_recovery_time": "(2 minute)", # "metabolic_recovery_percentage": "65%", # "breath_recovery_time": "(2.5 minute)", # "breath_recovery_percentage": "76%", # "recovery_chart": "../graphs/recovery_chart.png", # "resting_heart_rate": "53bpm", # "hr_age_range": "26-35", # "hr_poor": "82bpm +", # "hr_below_avg": "75-81bpm", # "hr_average": "71-74bpm", # "hr_above_avg": "66-70bpm", # "hr_good": "62-65bpm", # "hr_excellent": "55-61bpm", # "hr_athlete": "44-54bpm", # "contact_email": "info@ishplabs.com", # "website": "www.ishplabs.com", # "social": "@ishplabs", # "page_number": "10", # }, # ), # ( # "page_13.html", # { # "patient_name": "Keirstyn Moran", # "age": "34", # "height": "5'4\"", # "weight": "123lbs", # "focus": "Endurance", # "zone2_frequency": "3-4x/week", # "zone2_duration": "40+ minutes", # "zone2_hr_range": "____", # "zone2_speed": "____ mph", # "zone2_incline": "2% Incline", # "zone3_frequency": "1-2x/week", # "zone3_duration": "10-20 minutes", # "zone3_hr_range": "____ bpm", # "zone3_speed": "____mph", # "zone3_incline": "2% Incline", # "zone3_target_hr": "___ bpm", # "zone3_recovery_speed": "____mph", # "zone3_recovery_incline": "2% Incline", # "zone1_hr_range": "____bpm", # "zone1_duration": "4-8 minutes", # "zone3_repeats": "2-3 times", # "short_sets": "8-10", # "short_duration": "10-30 seconds", # "short_zone": "5", # "short_rpe": "10", # "short_recovery": "20-60 seconds", # "medium_sets": "6-8", # "medium_duration": "30-90 seconds", # "medium_zone": "4", # "medium_rpe": "8-9", # "medium_recovery": "30-90 seconds", # "long_sets": "4-6", # "long_duration": "5-10 minutes", # "long_zone": "3/4", # "long_rpe": "7-8", # "long_recovery": "2.5-5 minutes", # "tempo_sets": "2-3", # "tempo_duration": "10-20 minutes", # "tempo_zone": "3", # "tempo_rpe": "6-7", # "tempo_recovery": "4-8 minutes", # "cardio_sets": "1", # "cardio_duration": ">40 minutes", # "cardio_zone": "2", # "cardio_rpe": "4-5", # "cardio_recovery": "N/A", # "week1_mon_zone": "Zone 2", # "week1_mon_duration": "45 mins", # "week1_tue_zone": "Zone 2", # "week1_tue_duration": "45 mins", # "week1_wed_zone": "Zone 3", # "week1_wed_duration1": "10mins On", # "week1_wed_duration2": "8mins Rest", # "week1_wed_sets": "x2", # "week1_thu_content": "", # "week1_fri_zone": "Zone 2", # "week1_fri_duration": "45 mins", # "week1_sat_content": "", # "week1_sun_content": "", # "week2_mon_zone": "Zone 2", # "week2_mon_duration": "50 mins", # "week2_tue_zone": "Zone 2", # "week2_tue_duration": "50 mins", # "week2_wed_zone": "Zone 3", # "week2_wed_duration1": "10mins On", # "week2_wed_duration2": "6mins Rest", # "week2_wed_sets": "x2", # "week2_thu_content": "", # "week2_fri_zone": "Zone 2", # "week2_fri_duration": "50 mins", # "week2_sat_content": "", # "week2_sun_content": "", # "contact_email": "info@ishplabs.com", # "website": "www.ishplabs.com", # "social": "@ishplabs", # "page_number": "12", # }, # ), # ("page_14.html", {}), # ("page_15.html", {}), # ("page_16.html", {}), # ("page_17.html", {}), # ("page_18.html", {}), # ("page_19.html", {}), # ] pages = [ (f"page_{i}.html", {}) for i in range(1, 20) ] # Render each template with its own context html_pages = [] for tpl, ctx in pages: template = env.get_template(tpl) html_pages.append(template.render(ctx)) # Combine with page breaks final_html = "
".join(html_pages) # Wrap in full HTML document html_doc = f""" {final_html} """ # Generate PDF options = { # "page-size": "A4", 'page-height': '297mm', 'page-width': '210mm', "encoding": "UTF-8", "margin-top": "0mm", "margin-bottom": "0mm", "margin-left": "0mm", "margin-right": "0mm", "no-outline": None, } pdfkit.from_string(html_doc, "truth_report.pdf", options=options) print("✅ PDF generated: multi_page_report.pdf")