|
|
import os |
|
|
import io |
|
|
import gradio as gr |
|
|
import pandas as pd |
|
|
import matplotlib.pyplot as plt |
|
|
import matplotlib.dates as mdates |
|
|
from datetime import datetime |
|
|
|
|
|
def plot_ketones(file): |
|
|
""" |
|
|
Process uploaded file and create a ketone levels plot over time. |
|
|
|
|
|
Args: |
|
|
file: Uploaded file object (CSV or Excel) |
|
|
|
|
|
Returns: |
|
|
matplotlib figure as PIL Image |
|
|
""" |
|
|
try: |
|
|
|
|
|
if file is None: |
|
|
return None, "Please upload a file" |
|
|
|
|
|
|
|
|
filename = file.name if hasattr(file, 'name') else 'uploaded_file' |
|
|
|
|
|
|
|
|
if filename.endswith('.xlsx') or filename.endswith('.xls'): |
|
|
df = pd.read_excel(file, engine='openpyxl') |
|
|
elif filename.endswith('.csv'): |
|
|
df = pd.read_csv(file) |
|
|
else: |
|
|
return None, "Please upload a CSV or Excel file" |
|
|
|
|
|
|
|
|
print(f"Columns found: {df.columns.tolist()}") |
|
|
print(f"First few rows:\n{df.head()}") |
|
|
|
|
|
|
|
|
date_col = None |
|
|
ketone_col = None |
|
|
|
|
|
for col in df.columns: |
|
|
col_lower = str(col).lower() |
|
|
if 'date' in col_lower or 'time' in col_lower: |
|
|
date_col = col |
|
|
if 'ketone' in col_lower or 'level' in col_lower or 'value' in col_lower or 'reading' in col_lower or 'sensor' in col_lower: |
|
|
ketone_col = col |
|
|
|
|
|
|
|
|
if date_col is None: |
|
|
date_col = df.columns[0] |
|
|
if ketone_col is None: |
|
|
ketone_col = df.columns[1] if len(df.columns) > 1 else df.columns[0] |
|
|
|
|
|
print(f"Using date column: {date_col}") |
|
|
print(f"Using ketone column: {ketone_col}") |
|
|
|
|
|
|
|
|
df[date_col] = pd.to_datetime(df[date_col], errors='coerce') |
|
|
|
|
|
|
|
|
df = df.dropna(subset=[date_col, ketone_col]) |
|
|
|
|
|
|
|
|
df[ketone_col] = pd.to_numeric(df[ketone_col], errors='coerce') |
|
|
df = df.dropna(subset=[ketone_col]) |
|
|
|
|
|
|
|
|
df = df.sort_values(date_col) |
|
|
|
|
|
if len(df) == 0: |
|
|
return None, "No valid data found in the file. Please check your data format." |
|
|
|
|
|
|
|
|
fig, ax = plt.subplots(figsize=(12, 6)) |
|
|
|
|
|
ax.plot(df[date_col], df[ketone_col], marker='o', linestyle='-', |
|
|
linewidth=2, markersize=6, color='#FF6B6B', label='Ketone Levels') |
|
|
|
|
|
|
|
|
ax.set_xlabel('Date', fontsize=12, fontweight='bold') |
|
|
ax.set_ylabel('Ketone Level (mmol/L)', fontsize=12, fontweight='bold') |
|
|
ax.set_title('Ketone Levels Over Time', fontsize=14, fontweight='bold', pad=20) |
|
|
ax.grid(True, alpha=0.3, linestyle='--') |
|
|
ax.legend(loc='best') |
|
|
|
|
|
|
|
|
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d')) |
|
|
ax.xaxis.set_major_locator(mdates.AutoDateLocator()) |
|
|
plt.xticks(rotation=45, ha='right') |
|
|
|
|
|
|
|
|
plt.tight_layout() |
|
|
|
|
|
|
|
|
buf = io.BytesIO() |
|
|
plt.savefig(buf, format='png', dpi=150, bbox_inches='tight') |
|
|
buf.seek(0) |
|
|
plt.close(fig) |
|
|
|
|
|
|
|
|
summary = f""" |
|
|
**Data Summary:** |
|
|
- Total measurements: {len(df)} |
|
|
- Date range: {df[date_col].min().strftime('%Y-%m-%d')} to {df[date_col].max().strftime('%Y-%m-%d')} |
|
|
- Average ketone level: {df[ketone_col].mean():.2f} mmol/L |
|
|
- Min ketone level: {df[ketone_col].min():.2f} mmol/L |
|
|
- Max ketone level: {df[ketone_col].max():.2f} mmol/L |
|
|
""" |
|
|
|
|
|
return buf, summary |
|
|
|
|
|
except Exception as e: |
|
|
error_msg = f"Error processing file: {str(e)}\n\nPlease ensure your file has:\n- A date/time column\n- A ketone level column\n- Valid numeric values" |
|
|
print(f"Error details: {e}") |
|
|
import traceback |
|
|
traceback.print_exc() |
|
|
return None, error_msg |
|
|
|
|
|
|
|
|
with gr.Blocks(title="Ketone Level Plotter") as demo: |
|
|
gr.Markdown(""" |
|
|
# 🔬 Ketone Level Plotter |
|
|
|
|
|
Upload a CSV or Excel file containing your ketone measurements to visualize trends over time. |
|
|
|
|
|
**Expected file format:** |
|
|
- First column: Date/Time (any common date format) |
|
|
- Second column: Ketone levels (numeric values in mmol/L) |
|
|
|
|
|
Or use column names containing 'date', 'time' for dates and 'ketone', 'level', or 'value' for measurements. |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
file_input = gr.File( |
|
|
label="Upload CSV or Excel file", |
|
|
file_types=[".csv", ".xlsx", ".xls"], |
|
|
type="filepath" |
|
|
) |
|
|
plot_button = gr.Button("Generate Plot", variant="primary", size="lg") |
|
|
|
|
|
with gr.Column(scale=2): |
|
|
image_output = gr.Image(label="Ketone Levels Plot", type="filepath") |
|
|
summary_output = gr.Markdown(label="Summary Statistics") |
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
### Tips: |
|
|
- Your file can be either CSV (.csv) or Excel (.xlsx, .xls) |
|
|
- The app will automatically detect date and ketone columns |
|
|
- Make sure dates are in a recognizable format (YYYY-MM-DD, MM/DD/YYYY, etc.) |
|
|
- Ketone levels should be numeric values |
|
|
""") |
|
|
|
|
|
|
|
|
plot_button.click( |
|
|
fn=plot_ketones, |
|
|
inputs=[file_input], |
|
|
outputs=[image_output, summary_output] |
|
|
) |
|
|
|
|
|
|
|
|
file_input.upload( |
|
|
fn=plot_ketones, |
|
|
inputs=[file_input], |
|
|
outputs=[image_output, summary_output] |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |
|
|
|