83f50882e2
- Created base template with navigation and layout structure - Implemented upload.html for patient data and file uploads - Developed edit.html for editing calculated metrics - Added preview.html for displaying generated report previews - Enhanced user experience with Tailwind CSS styling
198 lines
15 KiB
HTML
198 lines
15 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Edit Metrics - Report Generator{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="px-4 py-6 sm:px-0">
|
|
{% if not session.get('metrics') %}
|
|
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
|
|
<p class="text-yellow-800">No metrics found. Please <a href="/" class="underline">generate a report</a> first.</p>
|
|
</div>
|
|
{% else %}
|
|
{% if error %}
|
|
<div class="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
|
|
<p class="text-red-800">{{ error }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="bg-white shadow rounded-lg">
|
|
<div class="px-4 py-5 sm:p-6">
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h2 class="text-2xl font-bold text-gray-900">Edit Calculated Metrics</h2>
|
|
<a href="/preview" class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
|
|
Back to Preview
|
|
</a>
|
|
</div>
|
|
|
|
<form action="/edit" method="post" class="space-y-8">
|
|
<!-- Pnoe Metrics Section -->
|
|
<div class="border-b border-gray-200 pb-6">
|
|
<h3 class="text-lg font-medium text-gray-900 mb-4">Pnoe Metrics</h3>
|
|
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
|
|
<div>
|
|
<label for="vo2_max" class="block text-sm font-medium text-gray-700">VO2 Max (ml/min)</label>
|
|
<input type="number" step="0.01" name="vo2_max" id="vo2_max"
|
|
value="{{ '%.2f'|format(session.metrics.pnoe['vo2_max']) if session.metrics.pnoe.get('vo2_max') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="vo2_max_per_kg" class="block text-sm font-medium text-gray-700">VO2 Max per kg (ml/min/kg)</label>
|
|
<input type="number" step="0.01" name="vo2_max_per_kg" id="vo2_max_per_kg"
|
|
value="{{ '%.2f'|format(session.metrics.pnoe['vo2_max_per_kg']) if session.metrics.pnoe.get('vo2_max_per_kg') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="peak_vt" class="block text-sm font-medium text-gray-700">Peak VT (L)</label>
|
|
<input type="number" step="0.01" name="peak_vt" id="peak_vt"
|
|
value="{{ '%.2f'|format(session.metrics.pnoe['peak_vt']) if session.metrics.pnoe.get('peak_vt') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="peak_vt_hr" class="block text-sm font-medium text-gray-700">Peak VT HR (bpm)</label>
|
|
<input type="number" step="1" name="peak_vt_hr" id="peak_vt_hr"
|
|
value="{{ '%.0f'|format(session.metrics.pnoe['peak_vt_hr']) if session.metrics.pnoe.get('peak_vt_hr') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="fat_max_value" class="block text-sm font-medium text-gray-700">Fat Max Value (kcal/min)</label>
|
|
<input type="number" step="0.01" name="fat_max_value" id="fat_max_value"
|
|
value="{{ '%.2f'|format(session.metrics.pnoe['fat_max_value']) if session.metrics.pnoe.get('fat_max_value') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="fat_max_hr" class="block text-sm font-medium text-gray-700">Fat Max HR (bpm)</label>
|
|
<input type="number" step="1" name="fat_max_hr" id="fat_max_hr"
|
|
value="{{ '%.0f'|format(session.metrics.pnoe['fat_max_hr']) if session.metrics.pnoe.get('fat_max_hr') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- VT1 Section -->
|
|
<div class="border-b border-gray-200 pb-6">
|
|
<h3 class="text-lg font-medium text-gray-900 mb-4">VT1 Threshold</h3>
|
|
<div class="grid grid-cols-1 gap-6 sm:grid-cols-3">
|
|
<div>
|
|
<label for="vt1_hr" class="block text-sm font-medium text-gray-700">Heart Rate (bpm)</label>
|
|
<input type="number" step="1" name="vt1_hr" id="vt1_hr"
|
|
value="{{ '%.0f'|format(session.metrics.pnoe['vt1']['HeartRate']) if session.metrics.pnoe.get('vt1') and session.metrics.pnoe['vt1'].get('HeartRate') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="vt1_speed" class="block text-sm font-medium text-gray-700">Speed (mph)</label>
|
|
<input type="number" step="0.01" name="vt1_speed" id="vt1_speed"
|
|
value="{{ '%.2f'|format(session.metrics.pnoe['vt1']['Speed']) if session.metrics.pnoe.get('vt1') and session.metrics.pnoe['vt1'].get('Speed') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="vt1_time" class="block text-sm font-medium text-gray-700">Time (sec)</label>
|
|
<input type="number" step="1" name="vt1_time" id="vt1_time"
|
|
value="{{ '%.0f'|format(session.metrics.pnoe['vt1']['Time']) if session.metrics.pnoe.get('vt1') and session.metrics.pnoe['vt1'].get('Time') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- VT2 Section -->
|
|
<div class="border-b border-gray-200 pb-6">
|
|
<h3 class="text-lg font-medium text-gray-900 mb-4">VT2 Threshold</h3>
|
|
<div class="grid grid-cols-1 gap-6 sm:grid-cols-3">
|
|
<div>
|
|
<label for="vt2_hr" class="block text-sm font-medium text-gray-700">Heart Rate (bpm)</label>
|
|
<input type="number" step="1" name="vt2_hr" id="vt2_hr"
|
|
value="{{ '%.0f'|format(session.metrics.pnoe['vt2']['HeartRate']) if session.metrics.pnoe.get('vt2') and session.metrics.pnoe['vt2'].get('HeartRate') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="vt2_speed" class="block text-sm font-medium text-gray-700">Speed (mph)</label>
|
|
<input type="number" step="0.01" name="vt2_speed" id="vt2_speed"
|
|
value="{{ '%.2f'|format(session.metrics.pnoe['vt2']['Speed']) if session.metrics.pnoe.get('vt2') and session.metrics.pnoe['vt2'].get('Speed') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="vt2_time" class="block text-sm font-medium text-gray-700">Time (sec)</label>
|
|
<input type="number" step="1" name="vt2_time" id="vt2_time"
|
|
value="{{ '%.0f'|format(session.metrics.pnoe['vt2']['Time']) if session.metrics.pnoe.get('vt2') and session.metrics.pnoe['vt2'].get('Time') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Heart Rate Zones -->
|
|
<div class="border-b border-gray-200 pb-6">
|
|
<h3 class="text-lg font-medium text-gray-900 mb-4">Heart Rate Zones</h3>
|
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-5">
|
|
{% for i in range(1, 6) %}
|
|
{% set zone_key = "zone" + i|string + "_bpm" %}
|
|
<div>
|
|
<label for="{{ zone_key }}" class="block text-sm font-medium text-gray-700">Zone {{ i }} (e.g., 120-140bpm)</label>
|
|
<input type="text" name="{{ zone_key }}" id="{{ zone_key }}"
|
|
value="{{ session.metrics.pnoe[zone_key] if session.metrics.pnoe.get(zone_key) else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Spirometry Metrics -->
|
|
{% if session.metrics.spirometry %}
|
|
<div class="border-b border-gray-200 pb-6">
|
|
<h3 class="text-lg font-medium text-gray-900 mb-4">Spirometry Metrics</h3>
|
|
<div class="space-y-4">
|
|
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
<div>
|
|
<label for="fvc_best" class="block text-sm font-medium text-gray-700">FVC Best (L)</label>
|
|
<input type="number" step="0.01" name="fvc_best" id="fvc_best"
|
|
value="{{ '%.2f'|format(session.metrics.spirometry['fvc_best']) if session.metrics.spirometry.get('fvc_best') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="fvc_pred" class="block text-sm font-medium text-gray-700">FVC % Predicted</label>
|
|
<input type="number" step="0.1" name="fvc_pred" id="fvc_pred"
|
|
value="{{ '%.1f'|format(session.metrics.spirometry['fvc_pred']) if session.metrics.spirometry.get('fvc_pred') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="fev1_best" class="block text-sm font-medium text-gray-700">FEV1 Best (L)</label>
|
|
<input type="number" step="0.01" name="fev1_best" id="fev1_best"
|
|
value="{{ '%.2f'|format(session.metrics.spirometry['fev1_best']) if session.metrics.spirometry.get('fev1_best') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="fev1_pred" class="block text-sm font-medium text-gray-700">FEV1 % Predicted</label>
|
|
<input type="number" step="0.1" name="fev1_pred" id="fev1_pred"
|
|
value="{{ '%.1f'|format(session.metrics.spirometry['fev1_pred']) if session.metrics.spirometry.get('fev1_pred') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="fev1_fvc_pct_best" class="block text-sm font-medium text-gray-700">FEV1/FVC% Best</label>
|
|
<input type="number" step="0.01" name="fev1_fvc_pct_best" id="fev1_fvc_pct_best"
|
|
value="{{ '%.2f'|format(session.metrics.spirometry['fev1_fvc_pct_best']) if session.metrics.spirometry.get('fev1_fvc_pct_best') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
<div>
|
|
<label for="fev1_fvc_pct_pred" class="block text-sm font-medium text-gray-700">FEV1/FVC% % Predicted</label>
|
|
<input type="number" step="0.1" name="fev1_fvc_pct_pred" id="fev1_fvc_pct_pred"
|
|
value="{{ '%.1f'|format(session.metrics.spirometry['fev1_fvc_pct_pred']) if session.metrics.spirometry.get('fev1_fvc_pct_pred') else '' }}"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm px-3 py-2 border">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Submit Button -->
|
|
<div class="flex justify-end">
|
|
<button type="submit"
|
|
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
|
Regenerate Report
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endblock %}
|
|
|