2026-02-22 22:07:23 -07:00
|
|
|
import streamlit as st
|
|
|
|
|
import mysql.connector
|
|
|
|
|
from mysql.connector import errorcode
|
|
|
|
|
import pandas as pd
|
|
|
|
|
from datetime import datetime, date
|
2026-02-23 21:30:38 -07:00
|
|
|
from dotenv import load_dotenv
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
load_dotenv()
|
|
|
|
|
user = os.getenv('user')
|
|
|
|
|
password = os.getenv('password')
|
|
|
|
|
host = os.getenv('host')
|
|
|
|
|
database = os.getenv('database')
|
|
|
|
|
port = os.getenv('port')
|
2026-02-22 22:07:23 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
# --- Database Helper Functions ---
|
|
|
|
|
def get_db_connection():
|
|
|
|
|
try:
|
|
|
|
|
return mysql.connector.connect(
|
|
|
|
|
user=user, password=password, host=host, port=port, database=database
|
|
|
|
|
)
|
|
|
|
|
except mysql.connector.Error as err:
|
|
|
|
|
st.error(f"Database Error: {err}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def load_template(workout_type):
|
|
|
|
|
conn = get_db_connection()
|
|
|
|
|
if not conn: return pd.DataFrame()
|
2026-02-22 22:07:23 -07:00
|
|
|
|
|
|
|
|
cursor = conn.cursor()
|
2026-02-25 16:45:16 -07:00
|
|
|
# Map selection to table name safely
|
|
|
|
|
table_map = {'chest': 'chest', 'back': 'back', 'legs': 'legs'}
|
|
|
|
|
table = table_map.get(workout_type, 'chest')
|
|
|
|
|
|
|
|
|
|
query = f"SELECT * FROM `{table}`"
|
2026-02-22 22:07:23 -07:00
|
|
|
cursor.execute(query)
|
|
|
|
|
data = cursor.fetchall()
|
2026-02-25 16:45:16 -07:00
|
|
|
columns = cursor.column_names
|
2026-02-22 22:07:23 -07:00
|
|
|
cursor.close()
|
|
|
|
|
conn.close()
|
2026-02-25 16:45:16 -07:00
|
|
|
|
|
|
|
|
df = pd.DataFrame(data, columns=columns)
|
|
|
|
|
df['date'] = date.today()
|
|
|
|
|
return df
|
2026-02-22 22:07:23 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
def save_workout(df):
|
|
|
|
|
conn = get_db_connection()
|
|
|
|
|
if not conn: return
|
|
|
|
|
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
# Note: Target table uses 'inputdate'
|
|
|
|
|
query = ("INSERT INTO `workouts` "
|
|
|
|
|
"(`inputdate`, `group`, `exercise`, `set`, `weight`, `reps`, `notes`) "
|
|
|
|
|
"VALUES (%s, %s, %s, %s, %s, %s, %s)")
|
|
|
|
|
|
|
|
|
|
for _, row in df.iterrows():
|
|
|
|
|
cursor.execute(query, (
|
|
|
|
|
row['date'], row['group'], row['exercise'],
|
|
|
|
|
row['set'], row['weight'], row['reps'], row['notes']
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
conn.commit()
|
|
|
|
|
cursor.close()
|
|
|
|
|
conn.close()
|
|
|
|
|
st.success("Workout saved successfully!")
|
2026-02-24 20:17:41 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
# --- Main Application ---
|
|
|
|
|
st.title("Workout Logger")
|
2026-02-24 20:17:41 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
workout_options = ['chest', 'back', 'legs']
|
|
|
|
|
selected_workout = st.selectbox('Select a Workout', options=workout_options)
|
2026-02-24 20:17:41 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
if 'current_workout' not in st.session_state or st.session_state.current_workout != selected_workout:
|
2026-02-24 20:17:41 -07:00
|
|
|
st.session_state.current_workout = selected_workout
|
2026-02-25 16:45:16 -07:00
|
|
|
st.session_state.workout_df = load_template(selected_workout)
|
|
|
|
|
st.session_state.current_index = 0
|
2026-02-24 20:17:41 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
df = st.session_state.workout_df
|
2026-02-24 20:17:41 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
if not df.empty:
|
|
|
|
|
# Navigation Dropdown
|
|
|
|
|
step_labels = [f"{i+1}. {row['exercise']} (Set {row['set']})" for i, row in df.iterrows()]
|
|
|
|
|
selected_step = st.selectbox("Jump to Exercise", options=step_labels, index=st.session_state.current_index)
|
|
|
|
|
st.session_state.current_index = step_labels.index(selected_step)
|
2026-02-24 20:17:41 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
# Current Row Data
|
|
|
|
|
idx = st.session_state.current_index
|
|
|
|
|
row = df.iloc[idx]
|
2026-02-24 20:17:41 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
st.divider()
|
2026-02-24 20:17:41 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
# Input Form - Using dynamic key to ensure fresh widgets for each row
|
|
|
|
|
with st.form(key=f"row_form_{idx}"):
|
|
|
|
|
st.subheader(f"{row['exercise']}")
|
|
|
|
|
st.caption(f"Set: {row['set']} | Group: {row['group']}")
|
2026-02-24 20:17:41 -07:00
|
|
|
|
2026-02-25 16:45:16 -07:00
|
|
|
c1, c2 = st.columns(2)
|
|
|
|
|
new_weight = c1.number_input("Weight", value=float(row['weight']), step=2.5)
|
|
|
|
|
new_reps = c2.number_input("Reps", value=int(row['reps']), step=1)
|
|
|
|
|
new_notes = st.text_input("Notes", value=str(row['notes']) if row['notes'] else "")
|
|
|
|
|
|
|
|
|
|
if st.form_submit_button("Save & Next"):
|
|
|
|
|
st.session_state.workout_df.at[idx, 'weight'] = new_weight
|
|
|
|
|
st.session_state.workout_df.at[idx, 'reps'] = new_reps
|
|
|
|
|
st.session_state.workout_df.at[idx, 'notes'] = new_notes
|
|
|
|
|
|
|
|
|
|
if idx < len(df) - 1:
|
|
|
|
|
st.session_state.current_index += 1
|
|
|
|
|
st.rerun()
|
|
|
|
|
|
|
|
|
|
st.divider()
|
|
|
|
|
st.dataframe(st.session_state.workout_df)
|
|
|
|
|
if st.button("Commit Workout to Database"):
|
|
|
|
|
save_workout(st.session_state.workout_df)
|