Add requirements.txt, tailwind configuration, and initial truth report HTML
- Created requirements.txt with a comprehensive list of dependencies. - Added tailwindconfig.js for Tailwind CSS configuration. - Introduced truth_report.html with structured content and Tailwind CSS styling for a visually appealing layout.
This commit is contained in:
Binary file not shown.
+807
@@ -0,0 +1,807 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 6,
|
||||||
|
"id": "b18c1027",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"{'id': 'gen-1758708788-9UUhU8KfktBmyteT4BUC', 'provider': 'Google', 'model': 'google/gemini-2.5-flash-lite', 'object': 'chat.completion', 'created': 1758708788, 'choices': [{'logprobs': None, 'finish_reason': 'stop', 'native_finish_reason': 'STOP', 'index': 0, 'message': {'role': 'assistant', 'content': 'Parameters,Best,LLN,Pred.,%Pred.,ZScore,PRE#1,PRE#2,PRE#3\\nFVC,4.24,3.03,3.79,112.0,0.95,4.24,4.17,4.15\\nFEV1,3.26,2.53,3.16,103.3,0.28,3.26,3.21,3.14\\nFEV1/FVC%,76.89,72.47,83.78,91.8,-1.05,76.9,77.0,75.7\\nPEF,684,222,384,178.7,-,444,438,684\\nFEF2575,2.74,2.15,3.42,80.2,-0.84,2.74,2.68,2.48\\nFEF25,6.08,,,0.0,-,6.08,6.0,5.53\\nFEF50,3.06,,,0.0,-,3.06,3.1,2.77\\nFEF75,1.06,0.71,1.41,75.1,-0.72,1.06,1.12,0.94\\nPEFTime,79,,,49,-,79,40,39\\nEVol,78.0,,,77.0,-,78.0,77.0,197.0\\nFEV6,4.22,3.03,3.79,111.4,-,4.22,4.17,4.13', 'refusal': None, 'reasoning': None}}], 'usage': {'prompt_tokens': 1348, 'completion_tokens': 434, 'total_tokens': 1782, 'prompt_tokens_details': {'cached_tokens': 0}, 'completion_tokens_details': {'reasoning_tokens': 0, 'image_tokens': 0}}}\n",
|
||||||
|
"Content saved to extracted_table.csv\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"\n",
|
||||||
|
"import requests\n",
|
||||||
|
"import json\n",
|
||||||
|
"import base64\n",
|
||||||
|
"from pathlib import Path\n",
|
||||||
|
"\n",
|
||||||
|
"API_KEY_REF = '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'\n",
|
||||||
|
"def encode_pdf_to_base64(pdf_path):\n",
|
||||||
|
" with open(pdf_path, \"rb\") as pdf_file:\n",
|
||||||
|
" return base64.b64encode(pdf_file.read()).decode('utf-8')\n",
|
||||||
|
"\n",
|
||||||
|
"url = \"https://openrouter.ai/api/v1/chat/completions\"\n",
|
||||||
|
"headers = {\n",
|
||||||
|
" \"Authorization\": f\"Bearer {API_KEY_REF}\",\n",
|
||||||
|
" \"Content-Type\": \"application/json\"\n",
|
||||||
|
"}\n",
|
||||||
|
"\n",
|
||||||
|
"# Read and encode the PDF\n",
|
||||||
|
"pdf_path = \"data/~Moran~K~19910201~Spirometry Exam~20250729~20250729032843.pdf\"\n",
|
||||||
|
"base64_pdf = encode_pdf_to_base64(pdf_path)\n",
|
||||||
|
"data_url = f\"data:application/pdf;base64,{base64_pdf}\"\n",
|
||||||
|
"\n",
|
||||||
|
"messages = [\n",
|
||||||
|
" {\n",
|
||||||
|
" \"role\": \"user\",\n",
|
||||||
|
" \"content\": [\n",
|
||||||
|
" {\n",
|
||||||
|
" \"type\": \"text\",\n",
|
||||||
|
" \"text\": \"Please extract the table from the pdf and return the values in csv format, \"\n",
|
||||||
|
" \"note that it is the unit of parameter that is beside it and it should not be a column. \"\n",
|
||||||
|
" \"The '-' Should be treated as empty values.\"\n",
|
||||||
|
" \"do not add 'csv' at the start or end of the response\"\n",
|
||||||
|
" },\n",
|
||||||
|
" {\n",
|
||||||
|
" \"type\": \"file\",\n",
|
||||||
|
" \"file\": {\n",
|
||||||
|
" \"filename\": \"document.pdf\",\n",
|
||||||
|
" \"file_data\": data_url\n",
|
||||||
|
" }\n",
|
||||||
|
" },\n",
|
||||||
|
" ]\n",
|
||||||
|
" }\n",
|
||||||
|
"]\n",
|
||||||
|
"\n",
|
||||||
|
"# Optional: Configure PDF processing engine\n",
|
||||||
|
"# PDF parsing will still work even if the plugin is not explicitly set\n",
|
||||||
|
"plugins = [\n",
|
||||||
|
" {\n",
|
||||||
|
" \"id\": \"file-parser\",\n",
|
||||||
|
" \"pdf\": {\n",
|
||||||
|
" \"engine\": \"pdf-text\" # defaults to \"mistral-ocr\". See Pricing above\n",
|
||||||
|
" }\n",
|
||||||
|
" }\n",
|
||||||
|
"]\n",
|
||||||
|
"\n",
|
||||||
|
"payload = {\n",
|
||||||
|
" \"model\": \"google/gemini-2.5-flash-lite\",\n",
|
||||||
|
" \"messages\": messages,\n",
|
||||||
|
"}\n",
|
||||||
|
"\n",
|
||||||
|
"response = requests.post(url, headers=headers, json=payload)\n",
|
||||||
|
"# Get the response content\n",
|
||||||
|
"response_data = response.json()\n",
|
||||||
|
"print(response_data)\n",
|
||||||
|
"\n",
|
||||||
|
"# Extract the content from the response\n",
|
||||||
|
"if 'choices' in response_data and len(response_data['choices']) > 0:\n",
|
||||||
|
" content = response_data['choices'][0]['message']['content']\n",
|
||||||
|
" \n",
|
||||||
|
" # Save to a CSV file\n",
|
||||||
|
" output_file = \"extracted_table.csv\"\n",
|
||||||
|
" with open(output_file, 'w', encoding='utf-8') as f:\n",
|
||||||
|
" f.write(content)\n",
|
||||||
|
" \n",
|
||||||
|
" print(f\"Content saved to {output_file}\")\n",
|
||||||
|
"else:\n",
|
||||||
|
" print(\"No content found in response\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 12,
|
||||||
|
"id": "56a9d655",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"FVC Best: 4.24, FVC Pred: 112.0\n",
|
||||||
|
"FEV1 Best: 3.26, FEV1 Pred: 103.3\n",
|
||||||
|
"FEV1/FVC% Best: 76.89, FEV1/FVC% Pred: 91.8\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"import pandas as pd\n",
|
||||||
|
"spirometry_df = pd.read_csv(\"extracted_table.csv\")\n",
|
||||||
|
"\n",
|
||||||
|
"fvc_best = spirometry_df.loc[spirometry_df['Parameters'] == 'FVC', 'Best'].values[0]\n",
|
||||||
|
"fvc_pred = spirometry_df.loc[spirometry_df['Parameters'] == 'FVC', '%Pred.'].values[0]\n",
|
||||||
|
"\n",
|
||||||
|
"fev1_best = spirometry_df.loc[spirometry_df['Parameters'] == 'FEV1', 'Best'].values[0]\n",
|
||||||
|
"fev1_pred = spirometry_df.loc[spirometry_df['Parameters'] == 'FEV1', '%Pred.'].values[0]\n",
|
||||||
|
"\n",
|
||||||
|
"fev1_fevc_best = spirometry_df.loc[spirometry_df['Parameters'] == 'FEV1/FVC%', 'Best'].values[0]\n",
|
||||||
|
"fev1_fevc_pred = spirometry_df.loc[spirometry_df['Parameters'] == 'FEV1/FVC%', '%Pred.'].values[0]\n",
|
||||||
|
"\n",
|
||||||
|
"print(f\"FVC Best: {fvc_best}, FVC Pred: {fvc_pred}\")\n",
|
||||||
|
"print(f\"FEV1 Best: {fev1_best}, FEV1 Pred: {fev1_pred}\")\n",
|
||||||
|
"print(f\"FEV1/FVC% Best: {fev1_fevc_best}, FEV1/FVC% Pred: {fev1_fevc_pred}\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 16,
|
||||||
|
"id": "990f4b4f",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Peak VT: 2.75\n",
|
||||||
|
"HR at Peak VT: 155.0\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"df = pd.read_csv('data/Pnoe_20250729_1550-Moran_Keirstyn.csv', delimiter=';')\n",
|
||||||
|
"peak_vt = df['VT(l)'].max()\n",
|
||||||
|
"max_vt_row = df.loc[df['VT(l)'].idxmax()]\n",
|
||||||
|
"print(f\"Peak VT: {peak_vt}\")\n",
|
||||||
|
"hr = max_vt_row['HR(bpm)']\n",
|
||||||
|
"print(f\"HR at Peak VT: {hr}\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 18,
|
||||||
|
"id": "041cbc3d",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Peak VT: 2.3770000000000002\n",
|
||||||
|
"HR at Peak VT: 171.525\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stderr",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"/tmp/ipykernel_301535/4157056299.py:3: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
|
||||||
|
" df = df.apply(pd.to_numeric, errors='ignore')\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"df = pd.read_csv('data/Pnoe_20250729_1550-Moran_Keirstyn.csv', delimiter=';')\n",
|
||||||
|
"# Convert all columns to numeric where possible, coercing errors to NaN\n",
|
||||||
|
"df = df.apply(pd.to_numeric, errors='ignore')\n",
|
||||||
|
"df['VO2 Pulse'] = df['VO2(ml/min)'] / df['HR(bpm)'] # VO2 Pulse in mL/beat\n",
|
||||||
|
"df['VO2 Breath'] = df['VO2(ml/min)'] / df['BF(bpm)'] # VO2 per Breath in mL/breath\n",
|
||||||
|
"df['CHO'] = df['EE(kcal/min)'] * df['CARBS(%)']/100\n",
|
||||||
|
"df['FAT'] = df['EE(kcal/min)'] * df['FAT(%)']/100\n",
|
||||||
|
"# Smooth key columns using rolling window\n",
|
||||||
|
"window_size = 10\n",
|
||||||
|
"\n",
|
||||||
|
"# List of columns to smooth\n",
|
||||||
|
"columns_to_smooth = ['VO2(ml/min)', 'VCO2(ml/min)', 'HR(bpm)', 'VT(l)', 'BF(bpm)', 'VE(l/min)', 'VO2 Pulse', 'VO2 Breath', 'CHO', 'FAT']\n",
|
||||||
|
"\n",
|
||||||
|
"# Apply smoothing to each column\n",
|
||||||
|
"for col in columns_to_smooth:\n",
|
||||||
|
" if col in df.columns:\n",
|
||||||
|
" df[f'{col}_smoothed'] = df[col].rolling(window=window_size).mean()\n",
|
||||||
|
" \n",
|
||||||
|
"peak_vt = df['VT(l)_smoothed'].max()\n",
|
||||||
|
"max_vt_row = df.loc[df['VT(l)_smoothed'].idxmax()]\n",
|
||||||
|
"print(f\"Peak VT: {peak_vt}\")\n",
|
||||||
|
"hr = max_vt_row['HR(bpm)_smoothed']\n",
|
||||||
|
"print(f\"HR at Peak VT: {hr}\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 20,
|
||||||
|
"id": "de7cadd1",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Percent FEV: 72.91411042944786\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"percent_fev = (peak_vt / fev1_best) * 100\n",
|
||||||
|
"print(f\"Percent FEV: {percent_fev}\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 24,
|
||||||
|
"id": "cb972ed3",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/html": [
|
||||||
|
"<div>\n",
|
||||||
|
"<style scoped>\n",
|
||||||
|
" .dataframe tbody tr th:only-of-type {\n",
|
||||||
|
" vertical-align: middle;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe tbody tr th {\n",
|
||||||
|
" vertical-align: top;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe thead th {\n",
|
||||||
|
" text-align: right;\n",
|
||||||
|
" }\n",
|
||||||
|
"</style>\n",
|
||||||
|
"<table border=\"1\" class=\"dataframe\">\n",
|
||||||
|
" <thead>\n",
|
||||||
|
" <tr style=\"text-align: right;\">\n",
|
||||||
|
" <th></th>\n",
|
||||||
|
" <th>MeasurementDate</th>\n",
|
||||||
|
" <th>Comment</th>\n",
|
||||||
|
" <th>ExternalDeviceId</th>\n",
|
||||||
|
" <th>ExternalPatientId</th>\n",
|
||||||
|
" <th>FirstName</th>\n",
|
||||||
|
" <th>LastName</th>\n",
|
||||||
|
" <th>BirthDate</th>\n",
|
||||||
|
" <th>Age</th>\n",
|
||||||
|
" <th>Ethnicity</th>\n",
|
||||||
|
" <th>Gender</th>\n",
|
||||||
|
" <th>...</th>\n",
|
||||||
|
" <th>Child_XC</th>\n",
|
||||||
|
" <th>Child_XC_Unit</th>\n",
|
||||||
|
" <th>Child_BIVA_ZRh</th>\n",
|
||||||
|
" <th>Child_BIVA_ZXcH</th>\n",
|
||||||
|
" <th>Child_PhA</th>\n",
|
||||||
|
" <th>Child_PhA_Unit</th>\n",
|
||||||
|
" <th>Child_REE_Kcal</th>\n",
|
||||||
|
" <th>Child_REE_MJ</th>\n",
|
||||||
|
" <th>Child_TEE_Kcal</th>\n",
|
||||||
|
" <th>Child_TEE_MJ</th>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </thead>\n",
|
||||||
|
" <tbody>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>13</th>\n",
|
||||||
|
" <td>2025-07-29T18:58:54.0000000Z</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" <td>10000001583275_0055003f5631501320313557</td>\n",
|
||||||
|
" <td>KM6479696509</td>\n",
|
||||||
|
" <td>Keirstyn</td>\n",
|
||||||
|
" <td>Moran</td>\n",
|
||||||
|
" <td>1991-02-01T00:00:00.0000000Z</td>\n",
|
||||||
|
" <td>34</td>\n",
|
||||||
|
" <td>Caucasian</td>\n",
|
||||||
|
" <td>Female</td>\n",
|
||||||
|
" <td>...</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" <td>NaN</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </tbody>\n",
|
||||||
|
"</table>\n",
|
||||||
|
"<p>1 rows × 147 columns</p>\n",
|
||||||
|
"</div>"
|
||||||
|
],
|
||||||
|
"text/plain": [
|
||||||
|
" MeasurementDate Comment \\\n",
|
||||||
|
"13 2025-07-29T18:58:54.0000000Z NaN \n",
|
||||||
|
"\n",
|
||||||
|
" ExternalDeviceId ExternalPatientId FirstName \\\n",
|
||||||
|
"13 10000001583275_0055003f5631501320313557 KM6479696509 Keirstyn \n",
|
||||||
|
"\n",
|
||||||
|
" LastName BirthDate Age Ethnicity Gender ... \\\n",
|
||||||
|
"13 Moran 1991-02-01T00:00:00.0000000Z 34 Caucasian Female ... \n",
|
||||||
|
"\n",
|
||||||
|
" Child_XC Child_XC_Unit Child_BIVA_ZRh Child_BIVA_ZXcH Child_PhA \\\n",
|
||||||
|
"13 NaN NaN NaN NaN NaN \n",
|
||||||
|
"\n",
|
||||||
|
" Child_PhA_Unit Child_REE_Kcal Child_REE_MJ Child_TEE_Kcal Child_TEE_MJ \n",
|
||||||
|
"13 NaN NaN NaN NaN NaN \n",
|
||||||
|
"\n",
|
||||||
|
"[1 rows x 147 columns]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 24,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"personal_df = pd.read_excel('data/SECA body comp for all patients.xlsx')\n",
|
||||||
|
"\n",
|
||||||
|
"keirstyn_data = personal_df[personal_df['LastName'].str.contains('Moran', case=False, na=False)]\n",
|
||||||
|
"keirstyn_data"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 26,
|
||||||
|
"id": "98d9295a",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"VO2 Max: 47.906290322580645\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"v02_max = df['VO2(ml/min)_smoothed'].max()\n",
|
||||||
|
"weight = keirstyn_data['Weight'].iloc[0]\n",
|
||||||
|
"print(f\"VO2 Max: {v02_max/weight}\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 32,
|
||||||
|
"id": "cdfeb309",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"\n",
|
||||||
|
"==================================================\n",
|
||||||
|
"Optimal Fat Burning Zone (highest fat:carb ratio):\n",
|
||||||
|
"Time: 164.0 seconds\n",
|
||||||
|
"Fat burn rate: 3.894 kcal/min\n",
|
||||||
|
"Carb burn rate: 1.575 kcal/min\n",
|
||||||
|
"Fat:Carb ratio: 2.47\n",
|
||||||
|
"Heart Rate: 96.7 bpm\n",
|
||||||
|
"VO2: 1147.9 ml/min\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"# Find the point where fat burning is highest and carb burning is lowest\n",
|
||||||
|
"# Using the smoothed data for more stable results\n",
|
||||||
|
"fat_burn_max_idx = df['FAT_smoothed'].idxmax()\n",
|
||||||
|
"carb_burn_min_idx = df['CHO_smoothed'].idxmin()\n",
|
||||||
|
"\n",
|
||||||
|
"# # Get the data at maximum fat burning point\n",
|
||||||
|
"# max_fat_row = df.loc[fat_burn_max_idx]\n",
|
||||||
|
"# print(f\"Maximum Fat Burning Point:\")\n",
|
||||||
|
"# print(f\"Time: {max_fat_row['T(sec)']} seconds\")\n",
|
||||||
|
"# print(f\"Fat burn rate: {max_fat_row['FAT_smoothed']:.3f} kcal/min\")\n",
|
||||||
|
"# print(f\"Carb burn rate: {max_fat_row['CHO_smoothed']:.3f} kcal/min\")\n",
|
||||||
|
"# print(f\"Heart Rate: {max_fat_row['HR(bpm)_smoothed']:.1f} bpm\")\n",
|
||||||
|
"# print(f\"VO2: {max_fat_row['VO2(ml/min)_smoothed']:.1f} ml/min\")\n",
|
||||||
|
"\n",
|
||||||
|
"# print(\"\\n\" + \"=\"*50)\n",
|
||||||
|
"\n",
|
||||||
|
"# # Get the data at minimum carb burning point\n",
|
||||||
|
"# min_carb_row = df.loc[carb_burn_min_idx]\n",
|
||||||
|
"# print(f\"Minimum Carbohydrate Burning Point:\")\n",
|
||||||
|
"# print(f\"Time: {min_carb_row['T(sec)']} seconds\")\n",
|
||||||
|
"# print(f\"Fat burn rate: {min_carb_row['FAT_smoothed']:.3f} kcal/min\")\n",
|
||||||
|
"# print(f\"Carb burn rate: {min_carb_row['CHO_smoothed']:.3f} kcal/min\")\n",
|
||||||
|
"# print(f\"Heart Rate: {min_carb_row['HR(bpm)_smoothed']:.1f} bpm\")\n",
|
||||||
|
"# print(f\"VO2: {min_carb_row['VO2(ml/min)_smoothed']:.1f} ml/min\")\n",
|
||||||
|
"\n",
|
||||||
|
"print(\"\\n\" + \"=\"*50)\n",
|
||||||
|
"\n",
|
||||||
|
"# Find the optimal fat burning zone (highest fat:carb ratio)\n",
|
||||||
|
"df['fat_carb_ratio'] = df['FAT_smoothed'] / (df['CHO_smoothed'] + 0.00000001) # Add small value to avoid division by zero\n",
|
||||||
|
"optimal_fat_idx = df['fat_carb_ratio'].idxmax()\n",
|
||||||
|
"optimal_row = df.loc[optimal_fat_idx]\n",
|
||||||
|
"\n",
|
||||||
|
"print(f\"Optimal Fat Burning Zone (highest fat:carb ratio):\")\n",
|
||||||
|
"print(f\"Time: {optimal_row['T(sec)']} seconds\")\n",
|
||||||
|
"print(f\"Fat burn rate: {optimal_row['FAT_smoothed']:.3f} kcal/min\")\n",
|
||||||
|
"print(f\"Carb burn rate: {optimal_row['CHO_smoothed']:.3f} kcal/min\")\n",
|
||||||
|
"print(f\"Fat:Carb ratio: {optimal_row['fat_carb_ratio']:.2f}\")\n",
|
||||||
|
"print(f\"Heart Rate: {optimal_row['HR(bpm)_smoothed']:.1f} bpm\")\n",
|
||||||
|
"print(f\"VO2: {optimal_row['VO2(ml/min)_smoothed']:.1f} ml/min\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 33,
|
||||||
|
"id": "4420cfea",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Found 2 intersections at indices: [18, 47]\n",
|
||||||
|
"\n",
|
||||||
|
"Last intersection at index 47:\n",
|
||||||
|
"Time: 251.0 seconds\n",
|
||||||
|
"Fat burn rate: 3.040 kcal/min\n",
|
||||||
|
"Carb burn rate: 3.166 kcal/min\n",
|
||||||
|
"Heart Rate: 100.5 bpm\n",
|
||||||
|
"VO2: 1283.0 ml/min\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"# Find intersections where FAT_smoothed and CHO_smoothed cross each other\n",
|
||||||
|
"intersections = []\n",
|
||||||
|
"\n",
|
||||||
|
"for i in range(1, len(df)):\n",
|
||||||
|
" # Check if there's a crossover between consecutive points\n",
|
||||||
|
" prev_fat = df.iloc[i-1]['FAT_smoothed']\n",
|
||||||
|
" prev_cho = df.iloc[i-1]['CHO_smoothed']\n",
|
||||||
|
" curr_fat = df.iloc[i]['FAT_smoothed']\n",
|
||||||
|
" curr_cho = df.iloc[i]['CHO_smoothed']\n",
|
||||||
|
" \n",
|
||||||
|
" # Skip if any values are NaN\n",
|
||||||
|
" if pd.isna(prev_fat) or pd.isna(prev_cho) or pd.isna(curr_fat) or pd.isna(curr_cho):\n",
|
||||||
|
" continue\n",
|
||||||
|
" \n",
|
||||||
|
" # Check if lines cross (fat was above/below cho and now it's below/above)\n",
|
||||||
|
" if ((prev_fat > prev_cho and curr_fat < curr_cho) or \n",
|
||||||
|
" (prev_fat < prev_cho and curr_fat > curr_cho)):\n",
|
||||||
|
" intersections.append(i)\n",
|
||||||
|
"\n",
|
||||||
|
"print(f\"Found {len(intersections)} intersections at indices: {intersections}\")\n",
|
||||||
|
"\n",
|
||||||
|
"if intersections:\n",
|
||||||
|
" # Get the last intersection\n",
|
||||||
|
" last_intersection_idx = intersections[-1]\n",
|
||||||
|
" last_intersection_row = df.iloc[last_intersection_idx]\n",
|
||||||
|
" \n",
|
||||||
|
" print(f\"\\nLast intersection at index {last_intersection_idx}:\")\n",
|
||||||
|
" print(f\"Time: {last_intersection_row['T(sec)']} seconds\")\n",
|
||||||
|
" print(f\"Fat burn rate: {last_intersection_row['FAT_smoothed']:.3f} kcal/min\")\n",
|
||||||
|
" print(f\"Carb burn rate: {last_intersection_row['CHO_smoothed']:.3f} kcal/min\")\n",
|
||||||
|
" print(f\"Heart Rate: {last_intersection_row['HR(bpm)_smoothed']:.1f} bpm\")\n",
|
||||||
|
" print(f\"VO2: {last_intersection_row['VO2(ml/min)_smoothed']:.1f} ml/min\")\n",
|
||||||
|
"else:\n",
|
||||||
|
" print(\"No intersections found between FAT and CHO curves\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 37,
|
||||||
|
"id": "62803668",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"VT1: {'HeartRate': 100.5, 'Speed': 4.0, 'Time': 251.0}\n",
|
||||||
|
"VT2: {'HeartRate': 189.71300000000002, 'Speed': 7.5, 'Time': 1524.0}\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"def detect_vt1(df, fat_col=\"FAT_smoothed\", carb_col=\"CHO_smoothed\"):\n",
|
||||||
|
" \"\"\"\n",
|
||||||
|
" Detect VT1 as the first index where carb burn > fat burn and remains higher.\n",
|
||||||
|
" \"\"\"\n",
|
||||||
|
" condition = df[carb_col] > df[fat_col]\n",
|
||||||
|
" crossover_indices = condition[condition].index\n",
|
||||||
|
"\n",
|
||||||
|
" if len(crossover_indices) == 0:\n",
|
||||||
|
" return None # No crossover found\n",
|
||||||
|
" \n",
|
||||||
|
" # Find first crossover where carbs remain higher for the rest\n",
|
||||||
|
" for idx in crossover_indices:\n",
|
||||||
|
" if all(df.loc[idx:][carb_col] > df.loc[idx:][fat_col]):\n",
|
||||||
|
" return idx\n",
|
||||||
|
" return None\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def detect_vt2(df, vent_col=\"VE(l/min)_smoothed\", bf_col=\"BF(bpm)_smoothed\", smooth_window=5):\n",
|
||||||
|
" \"\"\"\n",
|
||||||
|
" Detect VT2 using slope/inflection method.\n",
|
||||||
|
" Works with either Ventilation (VE) or Breathing Frequency (Bf).\n",
|
||||||
|
" \"\"\"\n",
|
||||||
|
" col = vent_col if vent_col in df.columns else bf_col\n",
|
||||||
|
" \n",
|
||||||
|
" # Use already smoothed data\n",
|
||||||
|
" smoothed_col = col\n",
|
||||||
|
" \n",
|
||||||
|
" # Compute slope (first derivative)\n",
|
||||||
|
" df[\"slope\"] = df[smoothed_col].diff()\n",
|
||||||
|
" \n",
|
||||||
|
" # Detect inflection: largest change in slope (second derivative peak)\n",
|
||||||
|
" df[\"second_derivative\"] = df[\"slope\"].diff()\n",
|
||||||
|
" inflection_idx = df[\"second_derivative\"].idxmax()\n",
|
||||||
|
" \n",
|
||||||
|
" return inflection_idx\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def analyze_thresholds(df_input):\n",
|
||||||
|
" # Use the existing dataframe\n",
|
||||||
|
" df_copy = df_input.copy()\n",
|
||||||
|
" \n",
|
||||||
|
" # --- Detect VT1 ---\n",
|
||||||
|
" vt1_idx = detect_vt1(df_copy)\n",
|
||||||
|
" vt1 = None\n",
|
||||||
|
" if vt1_idx is not None:\n",
|
||||||
|
" vt1 = {\n",
|
||||||
|
" \"HeartRate\": df_copy.loc[vt1_idx, \"HR(bpm)_smoothed\"],\n",
|
||||||
|
" \"Speed\": df_copy.loc[vt1_idx, \"Speed\"],\n",
|
||||||
|
" \"Time\": df_copy.loc[vt1_idx, \"T(sec)\"]\n",
|
||||||
|
" }\n",
|
||||||
|
" \n",
|
||||||
|
" # --- Detect VT2 ---\n",
|
||||||
|
" vt2_idx = detect_vt2(df_copy)\n",
|
||||||
|
" vt2 = None\n",
|
||||||
|
" if vt2_idx is not None:\n",
|
||||||
|
" vt2 = {\n",
|
||||||
|
" \"HeartRate\": df_copy.loc[vt2_idx, \"HR(bpm)_smoothed\"],\n",
|
||||||
|
" \"Speed\": df_copy.loc[vt2_idx, \"Speed\"],\n",
|
||||||
|
" \"Time\": df_copy.loc[vt2_idx, \"T(sec)\"]\n",
|
||||||
|
" }\n",
|
||||||
|
" \n",
|
||||||
|
" return vt1, vt2\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"vt1, vt2 = analyze_thresholds(df)\n",
|
||||||
|
"print(\"VT1:\", vt1)\n",
|
||||||
|
"print(\"VT2:\", vt2)\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 40,
|
||||||
|
"id": "07593b56",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Zone 1 (Active Recovery): 81.7 - 96.7 bpm\n",
|
||||||
|
"Zone 2 (Aerobic Base): 96.7 - 100.5 bpm\n",
|
||||||
|
"Zone 3 (Aerobic): 100.5 - 179.7 bpm\n",
|
||||||
|
"Zone 4 (Lactate Threshold): 179.7 - 199.7 bpm\n",
|
||||||
|
"Zone 5 (VO2 Max): 199.7+ bpm\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"zone_1_start = optimal_row['HR(bpm)_smoothed'] - 15\n",
|
||||||
|
"zone_2_start = optimal_row['HR(bpm)_smoothed']\n",
|
||||||
|
"zone_3_start = vt1\n",
|
||||||
|
"zone_4_start = vt2['HeartRate'] - 10\n",
|
||||||
|
"zone_5_start = vt2['HeartRate'] + 10\n",
|
||||||
|
"\n",
|
||||||
|
"zone_1_end = zone_2_start\n",
|
||||||
|
"zone_2_end = vt1['HeartRate']\n",
|
||||||
|
"zone_3_end = zone_4_start\n",
|
||||||
|
"zone_4_end = zone_5_start\n",
|
||||||
|
"\n",
|
||||||
|
"print(f\"Zone 1 (Active Recovery): {zone_1_start:.1f} - {zone_1_end:.1f} bpm\")\n",
|
||||||
|
"print(f\"Zone 2 (Aerobic Base): {zone_2_start:.1f} - {zone_2_end:.1f} bpm\")\n",
|
||||||
|
"print(f\"Zone 3 (Aerobic): {zone_3_start['HeartRate']:.1f} - {zone_3_end:.1f} bpm\")\n",
|
||||||
|
"print(f\"Zone 4 (Lactate Threshold): {zone_4_start:.1f} - {zone_4_end:.1f} bpm\")\n",
|
||||||
|
"print(f\"Zone 5 (VO2 Max): {zone_5_start:.1f}+ bpm\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 60,
|
||||||
|
"id": "c90415b2",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"VO2 Max detected at index 202:\n",
|
||||||
|
"Time: 985.0 seconds\n",
|
||||||
|
"VO2 Breath: 58.2 ml/breath\n",
|
||||||
|
"VO2: 2167.8 ml/min\n",
|
||||||
|
"VO2 per kg: 38.8 ml/kg/min\n",
|
||||||
|
"Heart Rate: 170.5 bpm\n",
|
||||||
|
"Speed: 6.0 km/h\n",
|
||||||
|
"VO2 Breath Slope: -0.02\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"# Calculate the slope of VO2 Breath (first derivative)\n",
|
||||||
|
"df['vo2_breath_slope'] = df['VO2 Breath_smoothed'].diff()\n",
|
||||||
|
"\n",
|
||||||
|
"# Find points where slope is consistently zero or negative\n",
|
||||||
|
"# We'll use a rolling window to check for consistent negative/zero slope\n",
|
||||||
|
"window = len(df) // 3 # Number of consecutive points to check\n",
|
||||||
|
"\n",
|
||||||
|
"# Calculate rolling mean of slope to smooth out noise\n",
|
||||||
|
"df['vo2_breath_slope_smoothed'] = df['vo2_breath_slope'].rolling(window=window).mean()\n",
|
||||||
|
"\n",
|
||||||
|
"# Find where slope becomes consistently zero or negative\n",
|
||||||
|
"mask = df['vo2_breath_slope_smoothed'] <= 0\n",
|
||||||
|
"consistent_negative_indices = mask[mask].index\n",
|
||||||
|
"\n",
|
||||||
|
"if len(consistent_negative_indices) > 0:\n",
|
||||||
|
" # Find the first point where slope becomes consistently negative/zero\n",
|
||||||
|
" vo2_max_idx = consistent_negative_indices[0]\n",
|
||||||
|
" vo2_max_row = df.loc[vo2_max_idx]\n",
|
||||||
|
" \n",
|
||||||
|
" print(f\"VO2 Max detected at index {vo2_max_idx}:\")\n",
|
||||||
|
" print(f\"Time: {vo2_max_row['T(sec)']} seconds\")\n",
|
||||||
|
" print(f\"VO2 Breath: {vo2_max_row['VO2 Breath_smoothed']:.1f} ml/breath\")\n",
|
||||||
|
" print(f\"VO2: {vo2_max_row['VO2(ml/min)_smoothed']:.1f} ml/min\")\n",
|
||||||
|
" print(f\"VO2 per kg: {vo2_max_row['VO2(ml/min)_smoothed']/weight:.1f} ml/kg/min\")\n",
|
||||||
|
" print(f\"Heart Rate: {vo2_max_row['HR(bpm)_smoothed']:.1f} bpm\")\n",
|
||||||
|
" print(f\"Speed: {vo2_max_row['Speed']} km/h\")\n",
|
||||||
|
" print(f\"VO2 Breath Slope: {vo2_max_row['vo2_breath_slope_smoothed']:.2f}\")\n",
|
||||||
|
"else:\n",
|
||||||
|
" # If no consistent negative slope found, use the maximum VO2 Breath value\n",
|
||||||
|
" vo2_max_idx = df['VO2 Breath_smoothed'].idxmax()\n",
|
||||||
|
" vo2_max_row = df.loc[vo2_max_idx]\n",
|
||||||
|
" \n",
|
||||||
|
" print(f\"No consistent negative slope found. Using peak VO2 Breath at index {vo2_max_idx}:\")\n",
|
||||||
|
" print(f\"Time: {vo2_max_row['T(sec)']} seconds\")\n",
|
||||||
|
" print(f\"VO2 Breath: {vo2_max_row['VO2 Breath_smoothed']:.1f} ml/breath\")\n",
|
||||||
|
" print(f\"VO2: {vo2_max_row['VO2(ml/min)_smoothed']:.1f} ml/min\")\n",
|
||||||
|
" print(f\"VO2 per kg: {vo2_max_row['VO2(ml/min)_smoothed']/weight:.1f} ml/kg/min\")\n",
|
||||||
|
" print(f\"Heart Rate: {vo2_max_row['HR(bpm)_smoothed']:.1f} bpm\")\n",
|
||||||
|
" print(f\"Speed: {vo2_max_row['Speed']} km/h\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 66,
|
||||||
|
"id": "c3b2cc59",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"VO2 Pulse and HR slopes diverge consistently starting at index 89:\n",
|
||||||
|
"Time: 485.0 seconds\n",
|
||||||
|
"VO2 Pulse (smoothed): 13.91\n",
|
||||||
|
"Heart Rate (smoothed): 136.2 bpm\n",
|
||||||
|
"VO2 Pulse Slope: 0.672\n",
|
||||||
|
"HR Slope: 1.000\n",
|
||||||
|
"Slope Difference: 1.006\n",
|
||||||
|
"VO2: 1897.8 ml/min\n",
|
||||||
|
"Speed: 4.5 km/h\n",
|
||||||
|
"Threshold used: 0.615\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"# Calculate slopes for both VO2 Pulse and HR\n",
|
||||||
|
"df['vo2_pulse_slope'] = df['VO2 Pulse_smoothed'].diff()\n",
|
||||||
|
"df['hr_slope'] = df['HR(bpm)_smoothed'].diff()\n",
|
||||||
|
"\n",
|
||||||
|
"# Calculate the difference between the slopes\n",
|
||||||
|
"df['slope_difference'] = abs(df['vo2_pulse_slope'] - df['hr_slope'])\n",
|
||||||
|
"\n",
|
||||||
|
"# Find where the slope difference becomes consistently large (slopes diverge)\n",
|
||||||
|
"# Use a rolling window to smooth out noise\n",
|
||||||
|
"window_size = len(df) // 5 # Adjust window size as needed\n",
|
||||||
|
"df['slope_difference_smoothed'] = df['slope_difference'].rolling(window=window_size).mean()\n",
|
||||||
|
"\n",
|
||||||
|
"# Find the threshold - we'll use the 75th percentile of slope differences as threshold\n",
|
||||||
|
"threshold = df['slope_difference_smoothed'].quantile(0.75)\n",
|
||||||
|
"\n",
|
||||||
|
"# Find points where slope difference exceeds threshold\n",
|
||||||
|
"divergence_mask = df['slope_difference_smoothed'] > threshold\n",
|
||||||
|
"divergence_indices = divergence_mask[divergence_mask].index\n",
|
||||||
|
"\n",
|
||||||
|
"if len(divergence_indices) > 0:\n",
|
||||||
|
" # Find the first sustained divergence point\n",
|
||||||
|
" min_consecutive_points = 5\n",
|
||||||
|
" consistent_divergence_idx = None\n",
|
||||||
|
" \n",
|
||||||
|
" for start_idx in divergence_indices:\n",
|
||||||
|
" # Check if divergence is sustained for consecutive points\n",
|
||||||
|
" consecutive_count = 0\n",
|
||||||
|
" for j in range(start_idx, min(start_idx + min_consecutive_points, len(df))):\n",
|
||||||
|
" if j in divergence_indices:\n",
|
||||||
|
" consecutive_count += 1\n",
|
||||||
|
" else:\n",
|
||||||
|
" break\n",
|
||||||
|
" \n",
|
||||||
|
" if consecutive_count >= min_consecutive_points:\n",
|
||||||
|
" consistent_divergence_idx = start_idx\n",
|
||||||
|
" break\n",
|
||||||
|
" \n",
|
||||||
|
" if consistent_divergence_idx is not None:\n",
|
||||||
|
" divergence_row = df.iloc[consistent_divergence_idx]\n",
|
||||||
|
" \n",
|
||||||
|
" print(f\"VO2 Pulse and HR slopes diverge consistently starting at index {consistent_divergence_idx}:\")\n",
|
||||||
|
" print(f\"Time: {divergence_row['T(sec)']} seconds\")\n",
|
||||||
|
" print(f\"VO2 Pulse (smoothed): {divergence_row['VO2 Pulse_smoothed']:.2f}\")\n",
|
||||||
|
" print(f\"Heart Rate (smoothed): {divergence_row['HR(bpm)_smoothed']:.1f} bpm\")\n",
|
||||||
|
" print(f\"VO2 Pulse Slope: {divergence_row['vo2_pulse_slope']:.3f}\")\n",
|
||||||
|
" print(f\"HR Slope: {divergence_row['hr_slope']:.3f}\")\n",
|
||||||
|
" print(f\"Slope Difference: {divergence_row['slope_difference_smoothed']:.3f}\")\n",
|
||||||
|
" print(f\"VO2: {divergence_row['VO2(ml/min)_smoothed']:.1f} ml/min\")\n",
|
||||||
|
" print(f\"Speed: {divergence_row['Speed']} km/h\")\n",
|
||||||
|
" print(f\"Threshold used: {threshold:.3f}\")\n",
|
||||||
|
" else:\n",
|
||||||
|
" print(f\"No sustained divergence found. Threshold: {threshold:.3f}\")\n",
|
||||||
|
" # Show the point with maximum slope difference instead\n",
|
||||||
|
" max_diff_idx = df['slope_difference_smoothed'].idxmax()\n",
|
||||||
|
" max_diff_row = df.iloc[max_diff_idx]\n",
|
||||||
|
" \n",
|
||||||
|
" print(f\"\\nPoint with maximum slope difference at index {max_diff_idx}:\")\n",
|
||||||
|
" print(f\"Time: {max_diff_row['T(sec)']} seconds\")\n",
|
||||||
|
" print(f\"VO2 Pulse (smoothed): {max_diff_row['VO2 Pulse_smoothed']:.2f}\")\n",
|
||||||
|
" print(f\"Heart Rate (smoothed): {max_diff_row['HR(bpm)_smoothed']:.1f} bpm\")\n",
|
||||||
|
" print(f\"Slope Difference: {max_diff_row['slope_difference_smoothed']:.3f}\")\n",
|
||||||
|
"else:\n",
|
||||||
|
" print(\"No significant slope divergence found between VO2 Pulse and HR\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "672d68f3",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Maximum FAT_smoothed occurs at index 30:\n",
|
||||||
|
"Heart Rate (smoothed): 96.7 bpm\n",
|
||||||
|
"FAT (smoothed): 3.894 kcal/min\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"max_fat_smoothed_idx = df['FAT_smoothed'].idxmax()\n",
|
||||||
|
"max_fat_smoothed_row = df.loc[max_fat_smoothed_idx]\n",
|
||||||
|
"max_heart_rate = 220 - keirstyn_data['Age'].iloc[0]\n",
|
||||||
|
"\n",
|
||||||
|
"print(f\"Maximum FAT_smoothed occurs at index {max_fat_smoothed_idx}:\")\n",
|
||||||
|
"print(f\"Heart Rate (smoothed): {max_fat_smoothed_row['HR(bpm)_smoothed']:.1f} bpm\")\n",
|
||||||
|
"print(f\"FAT (smoothed): {max_fat_smoothed_row['FAT_smoothed']:.3f} kcal/min\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "fe3b7605",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "report_generation",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.12.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
+407
@@ -0,0 +1,407 @@
|
|||||||
|
import base64
|
||||||
|
|
||||||
|
|
||||||
|
def image_to_base64(image_path):
|
||||||
|
try:
|
||||||
|
with open(image_path, "rb") as image_file:
|
||||||
|
return base64.b64encode(image_file.read()).decode("utf-8")
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"Warning: Image not found at {image_path}")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
### Defining Page Contexts ###
|
||||||
|
page_1_context = {
|
||||||
|
"name": "John Doe",
|
||||||
|
"surname": "Moran",
|
||||||
|
"date": "July 29, 2025",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_2_context = {
|
||||||
|
"content": "This is page 2 content",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_3_context = {
|
||||||
|
"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": image_to_base64(
|
||||||
|
"/home/oluwasanmi/Documents/Work/MKD/report_generation/graphs/page_1_body_composition.png"
|
||||||
|
),
|
||||||
|
"body_fat_chart": image_to_base64(
|
||||||
|
"/home/oluwasanmi/Documents/Work/MKD/report_generation/graphs/page_1_body_fat.png"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
page_4_context = {
|
||||||
|
"patient_name": "Keirstyn Moran",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_5_context = {
|
||||||
|
"patient_name": "Keirstyn Moran",
|
||||||
|
}
|
||||||
|
page_6_context = {
|
||||||
|
"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_context = {
|
||||||
|
"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",
|
||||||
|
"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",
|
||||||
|
"respiratory_graph": image_to_base64(
|
||||||
|
"/home/oluwasanmi/Documents/Work/MKD/report_generation/graphs/respiratory.png"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
page_8_context = {
|
||||||
|
"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_context = {
|
||||||
|
"patient_name": "Keirstyn Moran",
|
||||||
|
"age": "34",
|
||||||
|
"height": "5'4\"",
|
||||||
|
"weight": "123lbs",
|
||||||
|
"focus": "Endurance",
|
||||||
|
"fuel_utilization_chart": image_to_base64(
|
||||||
|
"/home/oluwasanmi/Documents/Work/MKD/report_generation/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_context = {
|
||||||
|
"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": image_to_base64(
|
||||||
|
"/home/oluwasanmi/Documents/Work/MKD/report_generation/graphs/vo2_pulse_chart.png"
|
||||||
|
),
|
||||||
|
"vo2_breath_drop_bpm": "173 bpm",
|
||||||
|
"vo2_breath_drop_zone": "Zone 3",
|
||||||
|
"vo2_breath_chart": image_to_base64(
|
||||||
|
"/home/oluwasanmi/Documents/Work/MKD/report_generation/graphs/vo2_breath_chart.png"
|
||||||
|
),
|
||||||
|
"contact_email": "info@ishplabs.com",
|
||||||
|
"website": "www.ishplabs.com",
|
||||||
|
"social": "@ishplabs",
|
||||||
|
"page_number": "10",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_11_context = {
|
||||||
|
"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": image_to_base64(
|
||||||
|
"/home/oluwasanmi/Documents/Work/MKD/report_generation/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": image_to_base64(
|
||||||
|
"/home/oluwasanmi/Documents/Work/MKD/report_generation/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": "11",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_12_context = {
|
||||||
|
"patient_name": "Keirstyn Moran",
|
||||||
|
"contact_email": "info@ishplabs.com",
|
||||||
|
"website": "www.ishplabs.com",
|
||||||
|
"social": "@ishplabs",
|
||||||
|
"page_number": "12",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_13_context = {
|
||||||
|
"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": "96-110bpm",
|
||||||
|
"zone2_speed": "3.5-4.0mph",
|
||||||
|
"zone2_incline": "2% Incline",
|
||||||
|
"zone3_frequency": "1-2x/week",
|
||||||
|
"zone3_duration": "10-20 minutes",
|
||||||
|
"zone3_hr_range": "100-178bpm",
|
||||||
|
"zone3_speed": "4.0-6.5mph",
|
||||||
|
"zone3_incline": "2% Incline",
|
||||||
|
"zone3_target_hr": "140bpm",
|
||||||
|
"zone3_recovery_speed": "3.5mph",
|
||||||
|
"zone3_recovery_incline": "2% Incline",
|
||||||
|
"zone1_hr_range": "81-96bpm",
|
||||||
|
"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": "13",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_14_context = {
|
||||||
|
"patient_name": "Keirstyn Moran",
|
||||||
|
"contact_email": "info@ishplabs.com",
|
||||||
|
"website": "www.ishplabs.com",
|
||||||
|
"social": "@ishplabs",
|
||||||
|
"page_number": "14",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_15_context = {
|
||||||
|
"patient_name": "Keirstyn Moran",
|
||||||
|
"contact_email": "info@ishplabs.com",
|
||||||
|
"website": "www.ishplabs.com",
|
||||||
|
"social": "@ishplabs",
|
||||||
|
"page_number": "15",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_16_context = {
|
||||||
|
"patient_name": "Keirstyn Moran",
|
||||||
|
"contact_email": "info@ishplabs.com",
|
||||||
|
"website": "www.ishplabs.com",
|
||||||
|
"social": "@ishplabs",
|
||||||
|
"page_number": "16",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_17_context = {
|
||||||
|
"patient_name": "Keirstyn Moran",
|
||||||
|
"contact_email": "info@ishplabs.com",
|
||||||
|
"website": "www.ishplabs.com",
|
||||||
|
"social": "@ishplabs",
|
||||||
|
"page_number": "17",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_18_context = {
|
||||||
|
"patient_name": "Keirstyn Moran",
|
||||||
|
"contact_email": "info@ishplabs.com",
|
||||||
|
"website": "www.ishplabs.com",
|
||||||
|
"social": "@ishplabs",
|
||||||
|
"page_number": "18",
|
||||||
|
}
|
||||||
|
|
||||||
|
page_19_context = {
|
||||||
|
"patient_name": "Keirstyn Moran",
|
||||||
|
"contact_email": "info@ishplabs.com",
|
||||||
|
"website": "www.ishplabs.com",
|
||||||
|
"social": "@ishplabs",
|
||||||
|
"page_number": "19",
|
||||||
|
}
|
||||||
|
|
||||||
|
context_list = [
|
||||||
|
page_1_context,
|
||||||
|
page_2_context,
|
||||||
|
page_3_context,
|
||||||
|
# page_4_context,
|
||||||
|
# page_5_context,
|
||||||
|
# page_6_context,
|
||||||
|
# page_7_context,
|
||||||
|
# page_8_context,
|
||||||
|
# page_9_context,
|
||||||
|
# page_10_context,
|
||||||
|
# page_11_context,
|
||||||
|
# page_12_context,
|
||||||
|
# page_13_context,
|
||||||
|
# page_14_context,
|
||||||
|
# page_15_context,
|
||||||
|
page_16_context,
|
||||||
|
page_17_context,
|
||||||
|
page_18_context,
|
||||||
|
page_19_context,
|
||||||
|
]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Parameters,Best,LLN,Pred.,%Pred.,ZScore,PRE#1,PRE#2,PRE#3
|
||||||
|
FVC,4.24,3.03,3.79,112.0,0.95,4.24,4.17,4.15
|
||||||
|
FEV1,3.26,2.53,3.16,103.3,0.28,3.26,3.21,3.14
|
||||||
|
FEV1/FVC%,76.89,72.47,83.78,91.8,-1.05,76.9,77.0,75.7
|
||||||
|
PEF,684,222,384,178.7,-,444,438,684
|
||||||
|
FEF2575,2.74,2.15,3.42,80.2,-0.84,2.74,2.68,2.48
|
||||||
|
FEF25,6.08,,,0.0,-,6.08,6.0,5.53
|
||||||
|
FEF50,3.06,,,0.0,-,3.06,3.1,2.77
|
||||||
|
FEF75,1.06,0.71,1.41,75.1,-0.72,1.06,1.12,0.94
|
||||||
|
PEFTime,79,,,49,-,79,40,39
|
||||||
|
EVol,78.0,,,77.0,-,78.0,77.0,197.0
|
||||||
|
FEV6,4.22,3.03,3.79,111.4,-,4.22,4.17,4.13
|
||||||
|
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
@@ -1,367 +1,18 @@
|
|||||||
import pdfkit
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
from context import context_list
|
||||||
|
|
||||||
env = Environment(loader=FileSystemLoader("report_gen"))
|
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 = []
|
html_pages = []
|
||||||
for tpl, ctx in pages:
|
|
||||||
template = env.get_template(tpl)
|
for i, context in enumerate(context_list):
|
||||||
html_pages.append(template.render(ctx))
|
template = env.get_template(f"page_{i + 1}.html")
|
||||||
|
html_pages.append(template.render(context))
|
||||||
|
|
||||||
# Combine with page breaks
|
# Combine with page breaks
|
||||||
final_html = "<div class='page-break'></div>".join(html_pages)
|
final_html = "<div class='page-break'></div>".join(html_pages)
|
||||||
|
|
||||||
# Wrap in full HTML document
|
# Wrap in full HTML document
|
||||||
html_doc = f"""
|
html_doc = f"""
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -379,6 +30,13 @@ html_doc = f"""
|
|||||||
.page {{
|
.page {{
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}}
|
}}
|
||||||
|
/* Reset margins and padding everywhere */
|
||||||
|
* {{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="m-0 p-0">
|
<body class="m-0 p-0">
|
||||||
@@ -387,18 +45,25 @@ html_doc = f"""
|
|||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# Generate PDF
|
# Generate PDF
|
||||||
options = {
|
|
||||||
# "page-size": "A4",
|
|
||||||
'page-height': '297mm',
|
def html_string_to_pdf(html_content, pdf_path):
|
||||||
'page-width': '210mm',
|
with sync_playwright() as p:
|
||||||
"encoding": "UTF-8",
|
browser = p.chromium.launch()
|
||||||
"margin-top": "0mm",
|
page = browser.new_page()
|
||||||
"margin-bottom": "0mm",
|
|
||||||
"margin-left": "0mm",
|
# Set the HTML directly
|
||||||
"margin-right": "0mm",
|
page.set_content(html_content)
|
||||||
"no-outline": None,
|
|
||||||
}
|
# Export to PDF
|
||||||
pdfkit.from_string(html_doc, "truth_report.pdf", options=options)
|
page.pdf(path=pdf_path, format="A4", print_background=True)
|
||||||
|
|
||||||
|
browser.close()
|
||||||
|
|
||||||
|
|
||||||
|
html_string_to_pdf(html_doc, "multi_page_report.pdf")
|
||||||
|
# pdfkit.from_string(html_doc, "truth_report.pdf", options=options)
|
||||||
|
|
||||||
print("✅ PDF generated: multi_page_report.pdf")
|
print("✅ PDF generated: multi_page_report.pdf")
|
||||||
|
|||||||
Binary file not shown.
+7
-6
@@ -2,7 +2,7 @@
|
|||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 1,
|
"execution_count": 2,
|
||||||
"id": "63f43af5",
|
"id": "63f43af5",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 2,
|
"execution_count": 3,
|
||||||
"id": "b0ee2af1",
|
"id": "b0ee2af1",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"name": "stderr",
|
"name": "stderr",
|
||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"/tmp/ipykernel_225163/3054964805.py:3: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
|
"/tmp/ipykernel_393367/3054964805.py:3: FutureWarning: errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead\n",
|
||||||
" df = df.apply(pd.to_numeric, errors='ignore')\n"
|
" df = df.apply(pd.to_numeric, errors='ignore')\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": 4,
|
||||||
"id": "fbd292c3",
|
"id": "fbd292c3",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 4,
|
"execution_count": 5,
|
||||||
"id": "ef8bc7ac",
|
"id": "ef8bc7ac",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -133,6 +133,7 @@
|
|||||||
"ax1.axvspan(phase_times[2], phase_times[3], alpha=0.2, color='lightgreen')\n",
|
"ax1.axvspan(phase_times[2], phase_times[3], alpha=0.2, color='lightgreen')\n",
|
||||||
"ax1.axvspan(phase_times[3], df['T(sec)'].max(), alpha=0.2, color='blue')\n",
|
"ax1.axvspan(phase_times[3], df['T(sec)'].max(), alpha=0.2, color='blue')\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
"plt.savefig('graphs/respiratory.png')\n",
|
||||||
"plt.show()"
|
"plt.show()"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -1036,7 +1037,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 37,
|
"execution_count": null,
|
||||||
"id": "bf55717b",
|
"id": "bf55717b",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
|
def html_to_pdf(html_path, pdf_path):
|
||||||
|
with sync_playwright() as p:
|
||||||
|
browser = p.chromium.launch()
|
||||||
|
page = browser.new_page()
|
||||||
|
|
||||||
|
# Load local HTML file
|
||||||
|
page.goto(f"file://{html_path}")
|
||||||
|
|
||||||
|
# Export to PDF with A4 size
|
||||||
|
page.pdf(
|
||||||
|
path=pdf_path,
|
||||||
|
format="A4", # <-- built-in A4 support
|
||||||
|
margin={"top": "20mm", "bottom": "20mm", "left": "15mm", "right": "15mm"},
|
||||||
|
print_background=True # include Tailwind background colors/images
|
||||||
|
)
|
||||||
|
|
||||||
|
browser.close()
|
||||||
|
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
html_to_pdf("table_of_contents.html", "report.pdf")
|
||||||
@@ -36,64 +36,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dotted Pattern at Bottom -->
|
<!-- Dotted Pattern at Bottom -->
|
||||||
<div class="absolute bottom-0 left-0 w-full h-32 z-10">
|
|
||||||
<div class="grid grid-cols-20 gap-2 h-full items-end pb-8 pl-8">
|
|
||||||
<!-- First row of dots -->
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-20 gap-2 h-full items-end pb-4 pl-8">
|
|
||||||
<!-- Second row of dots -->
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-20 gap-2 h-full items-end pl-8">
|
|
||||||
<!-- Third row of dots -->
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
+46
-38
@@ -1,62 +1,62 @@
|
|||||||
<div class="bg-white w-full page m-0 p-0">
|
<div class="bg-white w-full page m-0 px-10">
|
||||||
<div class="px-16 py-20">
|
<div class="px-16 py-10">
|
||||||
<!-- Table of Contents Header -->
|
<!-- Table of Contents Header -->
|
||||||
<div class="mb-12">
|
<div class="mb-8">
|
||||||
<h1 class="text-4xl font-bold text-black mb-4 tracking-wide">
|
<h1
|
||||||
|
class="text-5xl font-bold text-black mb-6 tracking-wide border-b-4 border-blue-500 pb-2 text-center"
|
||||||
|
>
|
||||||
TABLE OF CONTENTS
|
TABLE OF CONTENTS
|
||||||
</h1>
|
</h1>
|
||||||
<div class="w-full h-1 bg-cyan-400"></div>
|
<div class="w-full h-1 bg-cyan-400"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Table of Contents Items -->
|
<!-- Table of Contents Items -->
|
||||||
<div class="space-y-6">
|
<div class="flex flex-col justify-between space-y-6 py-6">
|
||||||
<!-- Lung Analysis -->
|
<!-- Lung Analysis -->
|
||||||
<div class="flex items-start bg-gray-200 rounded-lg">
|
<div class="flex items-start bg-gray-200 h-24">
|
||||||
<div
|
<div
|
||||||
class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-6"
|
class="bg-black text-white text-2xl font-bold w-16 h-full flex items-center justify-center mr-8 flex-shrink-0"
|
||||||
>
|
>
|
||||||
3
|
3
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 py-3">
|
<div class="flex flex-col flex-1 py-1 justify-center h-full">
|
||||||
<h2 class="text-2xl font-semibold text-black mb-1">
|
<h2 class="text-2xl font-semibold text-black">
|
||||||
Lung Analysis
|
Lung Analysis
|
||||||
</h2>
|
</h2>
|
||||||
<div class="space-y-1">
|
<p class="text-gray-600 text-base">
|
||||||
<p class="text-gray-600 text-sm">
|
Pulse Oximetry Assessment
|
||||||
Pulse Oximetry Assessment
|
</p>
|
||||||
</p>
|
<p class="text-gray-600 text-base">
|
||||||
<p class="text-gray-600 text-sm">
|
Spirometry Assessment
|
||||||
Spirometry Assessment
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Cardio Metrics -->
|
<!-- Cardio Metrics -->
|
||||||
<div class="flex items-start bg-gray-200 rounded-lg">
|
<div class="flex items-start bg-gray-200 h-24">
|
||||||
<div
|
<div
|
||||||
class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-6"
|
class="bg-black text-white text-2xl font-bold w-16 h-full flex items-center justify-center mr-8 flex-shrink-0"
|
||||||
>
|
>
|
||||||
4
|
4
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 py-3">
|
<div class="flex flex-col py-1 flex-1 justify-center h-full">
|
||||||
<h2 class="text-2xl font-semibold text-black mb-1">
|
<h2 class="text-2xl font-semibold text-black mb-3">
|
||||||
Cardio Metrics
|
Cardio Metrics
|
||||||
</h2>
|
</h2>
|
||||||
<p class="text-gray-600 text-sm">
|
<p class="text-gray-600 text-base">
|
||||||
Active Metabolic Rate Assessment
|
Active Metabolic Rate Assessment
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fuel Utilization -->
|
<!-- Fuel Utilization -->
|
||||||
<div class="flex items-start bg-gray-200 rounded-lg">
|
<div class="flex items-start bg-gray-200 h-24">
|
||||||
<div
|
<div
|
||||||
class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-6"
|
class="bg-black text-white text-2xl font-bold w-16 h-full flex items-center justify-center mr-8 flex-shrink-0"
|
||||||
>
|
>
|
||||||
5
|
5
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 py-3">
|
<div class="flex flex-col py-1 flex-1 justify-center flex-1 h-full">
|
||||||
<h2 class="text-2xl font-semibold text-black">
|
<h2 class="text-2xl font-semibold text-black">
|
||||||
Fuel Utilization
|
Fuel Utilization
|
||||||
</h2>
|
</h2>
|
||||||
@@ -64,13 +64,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Local Muscle Activity -->
|
<!-- Local Muscle Activity -->
|
||||||
<div class="flex items-start bg-gray-200 rounded-lg">
|
<div class="flex items-start bg-gray-200 h-24">
|
||||||
<div
|
<div
|
||||||
class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-6"
|
class="bg-black text-white text-2xl font-bold w-16 h-full flex items-center justify-center mr-8 flex-shrink-0"
|
||||||
>
|
>
|
||||||
9
|
9
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 py-3">
|
<div class="flex flex-col justify-center h-full flex-1">
|
||||||
<h2 class="text-2xl font-semibold text-black">
|
<h2 class="text-2xl font-semibold text-black">
|
||||||
Local Muscle Activity
|
Local Muscle Activity
|
||||||
</h2>
|
</h2>
|
||||||
@@ -78,13 +78,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Training Recommendations -->
|
<!-- Training Recommendations -->
|
||||||
<div class="flex items-start bg-gray-200 rounded-lg">
|
<div class="flex items-start bg-gray-200 h-24">
|
||||||
<div
|
<div
|
||||||
class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-6"
|
class="bg-black text-white text-2xl font-bold w-16 h-full flex items-center justify-center mr-8 flex-shrink-0"
|
||||||
>
|
>
|
||||||
10
|
10
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 py-3">
|
<div class="flex flex-col h-full justify-center flex-1">
|
||||||
<h2 class="text-2xl font-semibold text-black">
|
<h2 class="text-2xl font-semibold text-black">
|
||||||
Training Recommendations
|
Training Recommendations
|
||||||
</h2>
|
</h2>
|
||||||
@@ -92,28 +92,36 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Next Steps -->
|
<!-- Next Steps -->
|
||||||
<div class="flex items-start bg-gray-200 rounded-lg">
|
<div class="flex items-start bg-gray-200 h-24">
|
||||||
<div
|
<div
|
||||||
class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-6"
|
class="bg-black text-white text-2xl font-bold w-16 h-full flex items-center justify-center mr-8 flex-shrink-0"
|
||||||
>
|
>
|
||||||
12
|
12
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 py-3">
|
<div class="flex flex-col h-full justify-center flex-1">
|
||||||
<h2 class="text-2xl font-semibold text-black">
|
<h2 class="text-2xl font-semibold text-black">
|
||||||
Next Steps
|
Next Steps
|
||||||
</h2>
|
</h2>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<!-- No sub-items -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Glossary -->
|
<!-- Glossary -->
|
||||||
<div class="flex items-start bg-gray-200 rounded-lg">
|
<div class="flex items-start bg-gray-200 h-24">
|
||||||
<div
|
<div
|
||||||
class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-6"
|
class="bg-black text-white text-2xl font-bold w-16 h-full flex items-center justify-center mr-8 flex-shrink-0"
|
||||||
>
|
>
|
||||||
13
|
13
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 py-3">
|
<div class="flex flex-col h-full justify-center flex-1">
|
||||||
<h2 class="text-2xl font-semibold text-black">Glossary</h2>
|
<h2 class="text-2xl font-semibold text-black">
|
||||||
|
Glossary
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<!-- No sub-items -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+3
-19
@@ -30,43 +30,27 @@
|
|||||||
<!-- Body Composition Chart -->
|
<!-- Body Composition Chart -->
|
||||||
<div class="flex justify-center mb-8">
|
<div class="flex justify-center mb-8">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<img src="{{ body_composition_chart}}"
|
<img src="data:image/png;base64, {{ body_composition_chart}}"
|
||||||
alt="Body Composition Chart"
|
alt="Body Composition Chart"
|
||||||
class="w-80 h-80 object-contain">
|
class="w-80 h-80 object-contain">
|
||||||
|
|
||||||
<!-- Chart Labels -->
|
<!-- Chart Labels -->
|
||||||
<div class="absolute top-4 left-1/2 transform -translate-x-1/2 text-center">
|
|
||||||
<div class="text-lg font-semibold text-gray-700">Fat Mass ({{ fat_mass | default('27.6lbs') }})</div>
|
|
||||||
<div class="text-lg font-semibold text-gray-700">{{ fat_percentage | default('22.4%') }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="absolute bottom-4 right-8 text-center">
|
|
||||||
<div class="text-lg font-semibold text-gray-700">Lean Mass ({{ lean_mass | default('95.4lbs') }})</div>
|
|
||||||
<div class="text-lg font-semibold text-gray-700">{{ lean_percentage | default('77.6%') }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Body Fat Percentage Section -->
|
<!-- Body Fat Percentage Section -->
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<h4 class="text-xl font-bold text-center text-black mb-4">Body Fat Percent - {{ body_fat_percent | default('22.4%') }}</h4>
|
|
||||||
|
|
||||||
<!-- Body Fat Chart -->
|
<!-- Body Fat Chart -->
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<img src="{{ body_fat_chart}}"
|
<img src="data:image/png;base64, {{ body_fat_chart }}"
|
||||||
alt="Body Fat Percentage Chart"
|
alt="Body Fat Percentage Chart" >
|
||||||
class="w-full max-w-2xl h-32 object-contain">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Age Range Label -->
|
<!-- Age Range Label -->
|
||||||
<div class="flex justify-start mt-2 ml-8">
|
|
||||||
<span class="text-sm font-semibold text-gray-700">{{ age_range | default('20-39') }}</span>
|
|
||||||
<span class="text-sm text-gray-700 ml-1">({{ gender | default('F') }})</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Footer Section -->
|
<!-- Footer Section -->
|
||||||
<div class="absolute bottom-0 left-0 right-0 bg-black text-white px-6 py-3">
|
<div class="absolute bottom-0 left-0 right-0 bg-black text-white px-6 py-3">
|
||||||
<div class="flex justify-between items-center text-sm">
|
<div class="flex justify-between items-center text-sm">
|
||||||
|
|||||||
@@ -150,7 +150,7 @@
|
|||||||
|
|
||||||
<!-- Respiratory Graph -->
|
<!-- Respiratory Graph -->
|
||||||
<div class="flex justify-center mb-4">
|
<div class="flex justify-center mb-4">
|
||||||
<img src="{{ respiratory_graph }}"
|
<img src="data:image/png;base64, {{ respiratory_graph }}"
|
||||||
alt="Respiratory Analysis Chart"
|
alt="Respiratory Analysis Chart"
|
||||||
class="w-full max-w-4xl h-64 object-contain">
|
class="w-full max-w-4xl h-64 object-contain">
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,67 @@
|
|||||||
|
asttokens==3.0.0
|
||||||
|
brotli==1.1.0
|
||||||
|
cffi==2.0.0
|
||||||
|
chardet==5.2.0
|
||||||
|
charset-normalizer==3.4.3
|
||||||
|
click==8.3.0
|
||||||
|
comm==0.2.3
|
||||||
|
contourpy==1.3.3
|
||||||
|
cryptography==46.0.1
|
||||||
|
cssselect2==0.8.0
|
||||||
|
cycler==0.12.1
|
||||||
|
debugpy==1.8.17
|
||||||
|
decorator==5.2.1
|
||||||
|
et-xmlfile==2.0.0
|
||||||
|
executing==2.2.1
|
||||||
|
fonttools==4.60.0
|
||||||
|
ipykernel==6.30.1
|
||||||
|
ipython==9.5.0
|
||||||
|
ipython-pygments-lexers==1.1.1
|
||||||
|
jedi==0.19.2
|
||||||
|
jinja2==3.1.6
|
||||||
|
jupyter-client==8.6.3
|
||||||
|
jupyter-core==5.8.1
|
||||||
|
kiwisolver==1.4.9
|
||||||
|
markupsafe==3.0.2
|
||||||
|
matplotlib==3.10.6
|
||||||
|
matplotlib-inline==0.1.7
|
||||||
|
nest-asyncio==1.6.0
|
||||||
|
numpy==2.3.3
|
||||||
|
opencv-python-headless==4.11.0.86
|
||||||
|
openpyxl==3.1.5
|
||||||
|
packaging==25.0
|
||||||
|
pandas==2.3.2
|
||||||
|
pango==0.0.1
|
||||||
|
parso==0.8.5
|
||||||
|
pdfkit==1.0.0
|
||||||
|
pdfminer-six==20250506
|
||||||
|
pexpect==4.9.0
|
||||||
|
pillow==11.3.0
|
||||||
|
platformdirs==4.4.0
|
||||||
|
prompt-toolkit==3.0.52
|
||||||
|
psutil==7.1.0
|
||||||
|
ptyprocess==0.7.0
|
||||||
|
pure-eval==0.2.3
|
||||||
|
pycparser==2.23
|
||||||
|
pydyf==0.11.0
|
||||||
|
pygments==2.19.2
|
||||||
|
pymupdf==1.26.4
|
||||||
|
pyparsing==3.2.5
|
||||||
|
pypdf==5.9.0
|
||||||
|
pypdfium2==4.30.0
|
||||||
|
pyphen==0.17.2
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
pytz==2025.2
|
||||||
|
pyzmq==27.1.0
|
||||||
|
seaborn==0.13.2
|
||||||
|
six==1.17.0
|
||||||
|
stack-data==0.6.3
|
||||||
|
tabulate==0.9.0
|
||||||
|
tinycss2==1.4.0
|
||||||
|
tinyhtml5==2.0.0
|
||||||
|
tornado==6.5.2
|
||||||
|
traitlets==5.14.3
|
||||||
|
tzdata==2025.2
|
||||||
|
wcwidth==0.2.14
|
||||||
|
webencodings==0.5.1
|
||||||
|
zopfli==0.2.3.post1
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Truth Report</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-100">
|
||||||
|
<div class="bg-white w-full page m-0 px-10">
|
||||||
|
<div class="px-16 py-10">
|
||||||
|
<!-- Table of Contents Header -->
|
||||||
|
<div class="mb-8">
|
||||||
|
<h1 class="text-4xl font-bold text-black mb-6 tracking-wide"></h1>
|
||||||
|
TABLE OF CONTENTS
|
||||||
|
</h1>
|
||||||
|
<div class="w-full h-1 bg-cyan-400"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-start bg-gray-200 h-24">
|
||||||
|
<div
|
||||||
|
class="bg-black text-white text-2xl font-bold w-16 h-full flex items-center justify-center mr-8 flex-shrink-0"
|
||||||
|
>
|
||||||
|
5
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col py-1 bg-green-200 flex-1 justify-center h-full">
|
||||||
|
<h2 class="text-2xl font-semibold text-black">
|
||||||
|
Fuel Utilizationd
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Table of Contents Items -->
|
||||||
|
<div class="flex flex-col justify-between space-y-6 pt-6">
|
||||||
|
<!-- Lung Analysis -->
|
||||||
|
<div class="flex items-start bg-gray-200 rounded-lg mb-2">
|
||||||
|
<div class="bg-black text-white text-2xl font-bold w-16 h-full flex items-center justify-center rounded-lg mr-8 flex-shrink-0">
|
||||||
|
3
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h2 class="text-2xl font-semibold text-black mb-3">
|
||||||
|
Lung Analysis
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<p class="text-gray-600 text-base">
|
||||||
|
Pulse Oximetry Assessment
|
||||||
|
</p>
|
||||||
|
<p class="text-gray-600 text-base">
|
||||||
|
Spirometry Assessment
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Cardio Metrics -->
|
||||||
|
<div class="flex items-start bg-gray-200 rounded-lg p-6">
|
||||||
|
<div class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-8 flex-shrink-0">
|
||||||
|
4
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h2 class="text-2xl font-semibold text-black mb-3">
|
||||||
|
Cardio Metrics
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<p class="text-gray-600 text-base">
|
||||||
|
Active Metabolic Rate Assessment
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center bg-gray-200 p-6">
|
||||||
|
<div class="flex items-center justify-center w-20 h-20 bg-black text-white text-4xl font-semibold mr-6">
|
||||||
|
4
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h1 class="text-4xl font-normal">Nutrition Guidelines</h1>
|
||||||
|
<p class="text-lg">Ultrasound & Body Composition Assessment</p>
|
||||||
|
<p class="text-lg">Resting Metabolic Rate Assessment</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Fuel Utilization -->
|
||||||
|
<div class="flex items-start bg-gray-200 rounded-lg p-6">
|
||||||
|
<div class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-8 flex-shrink-0">
|
||||||
|
5
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h2 class="text-2xl font-semibold text-black mb-3">
|
||||||
|
Fuel Utilization
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<!-- No sub-items -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Local Muscle Activity -->
|
||||||
|
<div class="flex items-start bg-gray-200 rounded-lg p-6">
|
||||||
|
<div class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-8 flex-shrink-0">
|
||||||
|
9
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h2 class="text-2xl font-semibold text-black mb-3">
|
||||||
|
Local Muscle Activity
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<!-- No sub-items -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Training Recommendations -->
|
||||||
|
<div class="flex items-start bg-gray-200 rounded-lg p-6">
|
||||||
|
<div class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-8 flex-shrink-0">
|
||||||
|
10
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h2 class="text-2xl font-semibold text-black mb-3">
|
||||||
|
Training Recommendations
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<!-- No sub-items -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Next Steps -->
|
||||||
|
<div class="flex items-start bg-gray-200 rounded-lg p-6">
|
||||||
|
<div class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-8 flex-shrink-0">
|
||||||
|
12
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h2 class="text-2xl font-semibold text-black mb-3">
|
||||||
|
Next Steps
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<!-- No sub-items -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Glossary -->
|
||||||
|
<div class="flex items-start bg-gray-200 rounded-lg p-6">
|
||||||
|
<div class="bg-black text-white text-2xl font-bold w-16 h-16 flex items-center justify-center rounded-lg mr-8 flex-shrink-0">
|
||||||
|
13
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<h2 class="text-2xl font-semibold text-black mb-3">
|
||||||
|
Glossary
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<!-- No sub-items -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
Reference in New Issue
Block a user