Add report generation with PDF output and respiratory chart
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
from weasyprint import HTML
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 1. Generate a chart with matplotlib
|
||||||
|
os.makedirs("static/charts", exist_ok=True)
|
||||||
|
chart_path = "static/charts/resp_chart.png"
|
||||||
|
|
||||||
|
plt.plot([1, 2, 3, 4], [1, 4, 2, 5], label="Breath Volume")
|
||||||
|
plt.legend()
|
||||||
|
plt.title("Respiratory Chart")
|
||||||
|
plt.savefig(chart_path)
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
# 2. Patient data (this would usually come from your DB)
|
||||||
|
patient_data = {
|
||||||
|
"name": "Keirstyn Moran",
|
||||||
|
"age": 34,
|
||||||
|
"height": 163,
|
||||||
|
"weight": 56
|
||||||
|
}
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"patient": patient_data,
|
||||||
|
"indications": "No Respiratory Capacity Limitation",
|
||||||
|
"chart_path": chart_path
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. Render Jinja2 template
|
||||||
|
env = Environment(loader=FileSystemLoader("templates"))
|
||||||
|
template = env.get_template("report.html")
|
||||||
|
html_out = template.render(context)
|
||||||
|
|
||||||
|
# 4. Generate PDF
|
||||||
|
HTML(string=html_out, base_url=".").write_pdf("lung_report.pdf")
|
||||||
|
|
||||||
|
print("✅ Report generated: lung_report.pdf")
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 8,
|
||||||
|
"id": "6eee3ddd",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import camelot\n",
|
||||||
|
"import pandas as pd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 9,
|
||||||
|
"id": "d95cd8b1",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"tables = camelot.read_pdf('data/KM6479696509 _ Keirstyn_Moran_SingleViewWithExplanation_2025-07-29.pdf')"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 11,
|
||||||
|
"id": "759fd46e",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"all_dfs = []\n",
|
||||||
|
"\n",
|
||||||
|
"# Append each table's dataframe to the list\n",
|
||||||
|
"for table in tables:\n",
|
||||||
|
" all_dfs.append(table.df)\n",
|
||||||
|
"\n",
|
||||||
|
"# Concatenate all dataframes into one\n",
|
||||||
|
"combined_df = pd.concat(all_dfs, ignore_index=True)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 14,
|
||||||
|
"id": "e9c3b9f0",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
" 0 1 2 3 \\\n",
|
||||||
|
"0 ← FM →\\nLow Fat\\nHigh Fat\\nBody Composition Ch... \n",
|
||||||
|
"1 \n",
|
||||||
|
"2 \n",
|
||||||
|
"3 \n",
|
||||||
|
"4 \n",
|
||||||
|
"5 \n",
|
||||||
|
"6 \n",
|
||||||
|
"\n",
|
||||||
|
" 4 5 \n",
|
||||||
|
"0 muscle mass in a coordinate system. A d... \n",
|
||||||
|
"1 \n",
|
||||||
|
"2 \n",
|
||||||
|
"3 \n",
|
||||||
|
"4 \n",
|
||||||
|
"5 \n",
|
||||||
|
"6 \n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"print(combined_df)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "6bbc907f",
|
||||||
|
"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
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Lung Report</title>
|
||||||
|
<!-- TailwindCSS via CDN -->
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
</head>
|
||||||
|
<body class="p-10 font-sans">
|
||||||
|
<div class="bg-black text-white p-4 rounded-lg mb-6">
|
||||||
|
<h1 class="text-2xl font-bold">Lung Analysis Report</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-4 mb-6">
|
||||||
|
<div><b>Name:</b> {{ patient.name }}</div>
|
||||||
|
<div><b>Age:</b> {{ patient.age }}</div>
|
||||||
|
<div><b>Height:</b> {{ patient.height }} cm</div>
|
||||||
|
<div><b>Weight:</b> {{ patient.weight }} kg</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<h2 class="text-xl font-semibold mb-2">Lung Function</h2>
|
||||||
|
<div class="w-full h-6 bg-gradient-to-r from-red-500 via-yellow-300 to-green-400 relative">
|
||||||
|
<div class="absolute top-[-5px] left-1/2 transform -translate-x-1/2
|
||||||
|
w-0 h-0 border-l-[7px] border-r-[7px] border-b-[10px] border-black"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-gray-100 p-4 rounded-lg mb-6">
|
||||||
|
<b>Indications:</b> {{ indications }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 class="text-xl font-semibold mb-2">Respiratory Chart</h2>
|
||||||
|
<img src="{{ chart_path }}" class="border rounded-lg shadow">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user