Initial commit
This commit is contained in:
122
ui/daily_log.py
Normal file
122
ui/daily_log.py
Normal file
@@ -0,0 +1,122 @@
|
||||
import customtkinter as ctk
|
||||
from database import Database
|
||||
from tkinter import messagebox
|
||||
from utils import parse_float, format_float
|
||||
|
||||
class DailyLogFrame(ctk.CTkFrame):
|
||||
def __init__(self, master, db: Database, date_str: str):
|
||||
super().__init__(master)
|
||||
self.db = db
|
||||
self.date_str = date_str
|
||||
|
||||
self.setup_ui()
|
||||
self.load_data()
|
||||
|
||||
def setup_ui(self):
|
||||
self.grid_columnconfigure((0, 1), weight=1)
|
||||
|
||||
# Header
|
||||
self.header = ctk.CTkLabel(self, text=f"Daily Log: {self.date_str}", font=ctk.CTkFont(size=20, weight="bold"))
|
||||
self.header.grid(row=0, column=0, columnspan=2, pady=10, sticky="w")
|
||||
|
||||
# Sleep Section
|
||||
self.sleep_frame = ctk.CTkFrame(self)
|
||||
self.sleep_frame.grid(row=1, column=0, columnspan=2, sticky="ew", pady=10)
|
||||
|
||||
ctk.CTkLabel(self.sleep_frame, text="Sleep Duration (Hours):").grid(row=0, column=0, padx=10, pady=10)
|
||||
self.sleep_entry = ctk.CTkEntry(self.sleep_frame, placeholder_text="e.g. 7,5")
|
||||
self.sleep_entry.grid(row=0, column=1, padx=10, pady=10)
|
||||
|
||||
ctk.CTkLabel(self.sleep_frame, text="Sleep Quality (1-10):").grid(row=0, column=2, padx=10, pady=10)
|
||||
self.sleep_quality_slider = ctk.CTkSlider(self.sleep_frame, from_=1, to=10, number_of_steps=9)
|
||||
self.sleep_quality_slider.grid(row=0, column=3, padx=10, pady=10)
|
||||
self.sleep_quality_label = ctk.CTkLabel(self.sleep_frame, text="5")
|
||||
self.sleep_quality_label.grid(row=0, column=4, padx=5)
|
||||
self.sleep_quality_slider.configure(command=lambda val: self.sleep_quality_label.configure(text=str(int(val))))
|
||||
|
||||
# Body Stats Section
|
||||
self.body_frame = ctk.CTkFrame(self)
|
||||
self.body_frame.grid(row=2, column=0, columnspan=2, sticky="ew", pady=10)
|
||||
|
||||
ctk.CTkLabel(self.body_frame, text="Weight (kg):").grid(row=0, column=0, padx=10, pady=10)
|
||||
self.weight_entry = ctk.CTkEntry(self.body_frame, placeholder_text="0,0")
|
||||
self.weight_entry.grid(row=0, column=1, padx=10, pady=10)
|
||||
|
||||
ctk.CTkLabel(self.body_frame, text="Waist (cm):").grid(row=0, column=2, padx=10, pady=10)
|
||||
self.waist_entry = ctk.CTkEntry(self.body_frame, placeholder_text="0,0")
|
||||
self.waist_entry.grid(row=0, column=3, padx=10, pady=10)
|
||||
|
||||
# Notes / Mood
|
||||
self.notes_frame = ctk.CTkFrame(self)
|
||||
self.notes_frame.grid(row=3, column=0, columnspan=2, sticky="ew", pady=10)
|
||||
|
||||
ctk.CTkLabel(self.notes_frame, text="Mood/Energy (1-10):").grid(row=0, column=0, padx=10, pady=10)
|
||||
self.mood_slider = ctk.CTkSlider(self.notes_frame, from_=1, to=10, number_of_steps=9)
|
||||
self.mood_slider.grid(row=0, column=1, padx=10, pady=10, sticky="ew")
|
||||
self.mood_label = ctk.CTkLabel(self.notes_frame, text="5")
|
||||
self.mood_label.grid(row=0, column=2, padx=5)
|
||||
self.mood_slider.configure(command=lambda val: self.mood_label.configure(text=str(int(val))))
|
||||
|
||||
ctk.CTkLabel(self.notes_frame, text="Notes:").grid(row=1, column=0, padx=10, pady=10, sticky="n")
|
||||
self.notes_entry = ctk.CTkTextbox(self.notes_frame, height=100)
|
||||
self.notes_entry.grid(row=1, column=1, columnspan=2, padx=10, pady=10, sticky="ew")
|
||||
|
||||
# Save Button
|
||||
self.save_btn = ctk.CTkButton(self, text="Save Daily Log", command=self.save_log)
|
||||
self.save_btn.grid(row=4, column=0, columnspan=2, pady=20)
|
||||
|
||||
def load_data(self):
|
||||
log = self.db.get_daily_log(self.date_str)
|
||||
if log:
|
||||
if log['sleep_hours'] is not None: self.sleep_entry.insert(0, format_float(log['sleep_hours']))
|
||||
if log['sleep_quality']:
|
||||
self.sleep_quality_slider.set(log['sleep_quality'])
|
||||
self.sleep_quality_label.configure(text=str(log['sleep_quality']))
|
||||
if log['weight'] is not None: self.weight_entry.insert(0, format_float(log['weight']))
|
||||
if log['waist'] is not None: self.waist_entry.insert(0, format_float(log['waist']))
|
||||
if log['mood']:
|
||||
self.mood_slider.set(log['mood'])
|
||||
self.mood_label.configure(text=str(log['mood']))
|
||||
if log['notes']: self.notes_entry.insert("0.0", log['notes'])
|
||||
|
||||
def save_log(self):
|
||||
# We allow entries even if some fields are empty (parse_float handles this)
|
||||
# However, if they typed something invalid, we should catch it
|
||||
s_val = self.sleep_entry.get()
|
||||
w_val = self.weight_entry.get()
|
||||
wa_val = self.waist_entry.get()
|
||||
|
||||
sleep = parse_float(s_val) if s_val else None
|
||||
weight = parse_float(w_val) if w_val else None
|
||||
waist = parse_float(wa_val) if wa_val else None
|
||||
|
||||
# Validation check: if they entered text but it parsed to None
|
||||
if (s_val and sleep is None) or (w_val and weight is None) or (wa_val and waist is None):
|
||||
messagebox.showerror("Error", "Please enter valid numbers (e.g. 75.5 or 75,5)")
|
||||
return
|
||||
|
||||
data = {
|
||||
'sleep_hours': sleep,
|
||||
'sleep_quality': int(self.sleep_quality_slider.get()),
|
||||
'weight': weight,
|
||||
'waist': waist,
|
||||
'mood': int(self.mood_slider.get()),
|
||||
'notes': self.notes_entry.get("0.0", "end").strip()
|
||||
}
|
||||
self.db.save_daily_log(self.date_str, data)
|
||||
self.save_btn.configure(text="Saved!", fg_color="green")
|
||||
|
||||
# Cancel previous timer if exists
|
||||
if hasattr(self, '_reset_btn_id'):
|
||||
self.after_cancel(self._reset_btn_id)
|
||||
|
||||
self._reset_btn_id = self.after(2000, self.reset_save_button)
|
||||
|
||||
def reset_save_button(self):
|
||||
if self.winfo_exists():
|
||||
self.save_btn.configure(text="Save Daily Log", fg_color=["#3B8ED0", "#1F6AA5"])
|
||||
|
||||
def destroy(self):
|
||||
if hasattr(self, '_reset_btn_id'):
|
||||
self.after_cancel(self._reset_btn_id)
|
||||
super().destroy()
|
||||
Reference in New Issue
Block a user