diff --git a/main.py b/main.py deleted file mode 100644 index 995319d..0000000 --- a/main.py +++ /dev/null @@ -1,352 +0,0 @@ -import os -import sys -import json -import customtkinter as ctk -from datetime import datetime -from pygame import mixer - - -BREAK_BTN_COLOR = "#9a9a9a" -BREAK_HOVER = "#adaaaa" -RESET_BTN_COLOR = "#bd9909" -RESET_HOVER = "#dbb30f" - -DEF_POMODORO_MINS = 25 -DEF_SB_MINS = 5 -DEF_LB_MINS = 10 - -mixer.init() -beep = mixer.Sound('beep.mp3') - - -def load_file(filename, on_no_file=None): - try: - with open(filename, 'r') as file: - data = json.load(file) - except FileNotFoundError: - return on_no_file - - return data - -def load_config(): - return load_file('config.json', {'theme': 'Default'}) - -def load_data(): - return load_file('data.json', None) - -def save_config(config): - with open('config.json', 'w') as config_file: - json.dump(config, config_file, indent=4) - -def reload_app(): - os.execl(sys.executable, sys.executable, *sys.argv) - - -class StatsFrame(ctk.CTkFrame): - def __init__(self, master): - super().__init__(master) - self.data_file = 'data.json' - - self.total_today_var = ctk.StringVar(value="Pomodoros Today: 0") - self.total_today = ctk.CTkLabel(self, textvariable=self.total_today_var, font=("Helvetica", 16, "bold"), anchor="w") - self.total_today.pack(padx=20, pady=(20, 0), fill="x") - - self.total_hours_var = ctk.StringVar(value="Total Hours Studied: 0") - self.total_hours = ctk.CTkLabel(self, textvariable=self.total_hours_var, font=("Helvetica", 16, "bold"), anchor="w") - self.total_hours.pack(padx=20, pady=(20, 0), fill="x") - - self.total_var = ctk.StringVar(value="Total Pomodoros: 0") - self.total = ctk.CTkLabel(self, textvariable=self.total_var, font=("Helvetica", 16, "bold"), anchor="w") - self.total.pack(padx=20, pady=(20, 0), fill="x") - - self.update_stats = ctk.CTkButton(self, text="Update", width=90, font=("Roboto", 16), command=self.load_stats) - self.update_stats.pack(pady=40, side=ctk.BOTTOM) - - self.load_stats() - - def load_stats(self): - data = load_data() - if not data: - return - - current_date = datetime.now().strftime("%Y-%m-%d") - total_today = data.get('sessions_by_date', {}).get(current_date, 0) - - self.total_today_var.set(f"Pomodoros Today: {total_today}") - - total_seconds_studied = data.get('total_seconds_studied', 0) - total_hours = total_seconds_studied / 3600 - - self.total_hours_var.set(f"Total Hours Studied: {total_hours:.1f}") - - self.total_var.set(f"Total Pomodoros: {data.get('total_pomodoro_sessions', 0)}") - - -class EntryFrame(ctk.CTkFrame): - def __init__(self, master, text, config_attr, defvalue, command): - super().__init__(master) - - self.label = ctk.CTkLabel(self, text=text) - self.label.pack(pady=(10, 10), padx=(10, 10)) - - # Inner frame to hold the entry and button - self.controls_frame = ctk.CTkFrame(self) - self.controls_frame.pack(fill=ctk.X, expand=True, padx=10) - - self.entry_var = ctk.IntVar(value=config.get(config_attr, defvalue)) - self.entry = ctk.CTkEntry(self.controls_frame, width=35, textvariable=self.entry_var) - self.entry.pack(side=ctk.LEFT, fill=ctk.X, expand=True, padx=(0, 10)) - - self.set_button = ctk.CTkButton(self.controls_frame, width=120, text="Set", command=command) - self.set_button.pack(side=ctk.RIGHT) - - def get(self): - return self.entry_var.get() - - -class SettingsFrame(ctk.CTkScrollableFrame): - def __init__(self, master): - super().__init__(master) - self.themes_dir = 'themes' - config = load_config() - - # Pomodoro Duration - self.pomodoro_entry = EntryFrame(self, "Pomodoro Duration (mins):", "pomodoro_time", DEF_POMODORO_MINS, self.change_pomodoro_time) - self.pomodoro_entry.pack() - - # Short Break Duration - self.sb_entry = EntryFrame(self, "Short Break Duration (mins):", "short_break_time", DEF_SB_MINS, self.change_sb_time) - self.sb_entry.pack(pady=(5, 0)) - - # Long Break Duration - self.lb_entry = EntryFrame(self, "Long Break Duration (mins):", "long_break_time", DEF_LB_MINS, self.change_lb_time) - self.lb_entry.pack(pady=(5, 0)) - - # Selecting theme - self.theme_label = ctk.CTkLabel(self, text="Select Theme:") - self.theme_label.pack(pady=(20, 0)) - - self.theme_options = [os.path.splitext(theme)[0] for theme in os.listdir(self.themes_dir) if theme.endswith('.json')] - - selected = ctk.StringVar(value=config.get('theme', 'Default')) - self.theme_menu = ctk.CTkOptionMenu(self, variable=selected, values=self.theme_options, anchor="n", command=self.change_theme) - self.theme_menu.pack(pady=(10, 0)) - - # Volume Slider - self.volume_label = ctk.CTkLabel(self, text="Adjust Beep Volume:") - self.volume_label.pack(pady=(20, 0)) - - self.volume_slider = ctk.CTkSlider(self, from_=0, to=100, number_of_steps=100, command=self.change_volume) - self.volume_slider.pack(pady=(10, 0)) - - # Set the slider to the current volume - self.volume_slider.set(config.get('volume', 10)) # Default volume to 10% if not set - self.change_volume(config.get('volume', 10)) - - # Play Beep button - self.beep_button = ctk.CTkButton(self, text="Play", width=70, command=beep.play) - self.beep_button.pack(pady=(15, 0)) - - def change_time(self, entry, config_param): - time = entry.get() - if time and time > 0: - config = load_config() - config[config_param] = time - save_config(config) - - def change_pomodoro_time(self): - self.change_time(self.pomodoro_entry, "pomodoro_time") - - def change_sb_time(self): - self.change_time(self.sb_entry, "short_break_time") - - def change_lb_time(self): - self.change_time(self.lb_entry, "long_break_time") - - def change_theme(self, theme): - config = load_config() - config['theme'] = theme - save_config(config) - reload_app() - - def change_volume(self, volume): - volume = float(volume) / 100 - beep.set_volume(volume) - config = load_config() - config['volume'] = int(volume * 100) - save_config(config) - - -class PomodoroFrame(ctk.CTkFrame): - def __init__(self, master): - super().__init__(master) - self.data_file = 'data.json' - config = load_config() - - # Helper text that appears when a break is running - self.break_text = ctk.StringVar(value="") - self.break_label = ctk.CTkLabel(self, textvariable=self.break_text, font=("Roboto", 15)) - self.break_label.pack(pady=(5, 0)) - - # Display - self.pomodoro_time = config.get("pomodoro_time", DEF_POMODORO_MINS) * 60 - - minutes, seconds = divmod(self.pomodoro_time, 60) - self.timer_display = ctk.CTkLabel(self, text=f"{minutes:02d}:{seconds:02d}", font=("Helvetica", 58)) - self.timer_display.pack(pady=(15, 41)) - - # Controls - self.start_button = ctk.CTkButton(self, text="Start", font=("Roboto", 17), border_width=2, command=self.start_timer) - self.start_color = self.start_button.cget("fg_color") - self.start_button.pack(pady=(0, 10)) - - self.sb_button = ctk.CTkButton(self, text="Short break", font=("Roboto", 17), fg_color=BREAK_BTN_COLOR, hover_color=BREAK_HOVER, command=self.short_break) - self.sb_button.pack(pady=(0, 10)) - - self.lb_button = ctk.CTkButton(self, text="Long break", font=("Roboto", 17), fg_color=BREAK_BTN_COLOR, hover_color=BREAK_HOVER, command=self.long_break) - self.lb_button.pack(pady=(0, 10)) - - self.reset_button = ctk.CTkButton(self, text="Reset", font=("Roboto", 17), fg_color=RESET_BTN_COLOR, hover_color=RESET_HOVER, command=self.reset) - self.reset_button.pack() - - # State - self.running = False - self.break_running = False - self.next_timer_update = None - self.remaining_time = self.pomodoro_time - - def start_timer(self): - if self.next_timer_update: - self.after_cancel(self.next_timer_update) - - self.running = not self.running - btn_text = "Pause" if self.running else "Resume" - btn_fg = "transparent" if self.running else self.start_color - self.start_button.configure(text=btn_text, fg_color=btn_fg, hover=not self.running) - self.update_timer() - - def update_timer(self): - if self.running and self.remaining_time > 0: - self.remaining_time -= 1 - if not self.break_running: - self.track_second() - minutes, seconds = divmod(self.remaining_time, 60) - self.timer_display.configure(text=f"{minutes:02d}:{seconds:02d}") - self.next_timer_update = self.after(1000, self.update_timer) - elif self.remaining_time == 0: - self.session_ended() - - def track_second(self): - if os.path.exists(self.data_file): - with open(self.data_file, 'r') as file: - data = json.load(file) - data['total_seconds_studied'] += 1 - else: - data = {'total_seconds_studied': 0} - - with open(self.data_file, 'w') as file: - json.dump(data, file, indent=4) - - def reset(self, to:str="pomodoro_time", default:int=DEF_POMODORO_MINS): - self.running = False - self.break_running = False - self.break_text.set("") - - if self.next_timer_update: - self.after_cancel(self.next_timer_update) - self.next_timer_update = None - - config = load_config() - # TODO: Make cleaner? - self.pomodoro_time = int(config.get(to, default) * 60) - - # Reset the timer - self.remaining_time = self.pomodoro_time - minutes, seconds = divmod(self.remaining_time, 60) - self.timer_display.configure(text=f"{minutes:02d}:{seconds:02d}") - self.start_button.configure(text="Start", fg_color=self.start_color) - - def session_ended(self): - was_break = self.break_running - # Reset sets break_running to false - # TODO: surely there is a better way - - self.reset() - beep.play() - - if was_break: - return - - current_date = datetime.now().strftime("%Y-%m-%d") - - # TODO: I don't like the if check every second - if os.path.exists(self.data_file): - with open(self.data_file, 'r') as file: - data = json.load(file) - data['total_pomodoro_sessions'] = data.get('total_pomodoro_sessions', 0) + 1 - - if 'sessions_by_date' not in data: - data['sessions_by_date'] = {} - data['sessions_by_date'][current_date] = data['sessions_by_date'].get(current_date, 0) + 1 - else: - data = { - 'total_seconds_studied': 0, - 'total_pomodoro_sessions': 1, - 'sessions_by_date': {current_date: 1} - } - - with open(self.data_file, 'w') as file: - json.dump(data, file, indent=4) - - - def short_break(self): - self.reset(to="short_break_time", default=DEF_SB_MINS) - self.break_running = True - self.break_text.set("Short break") - self.start_timer() - - def long_break(self): - self.reset(to="long_break_time", default=DEF_LB_MINS) - self.break_running = True - self.break_text.set("Long break") - self.start_timer() - - -class TabView(ctk.CTkTabview): - def __init__(self, master, **kwargs): - super().__init__(master, **kwargs) - - self.add("Main") - self.add("Settings") - self.add("Stats") - - self.main_frame = PomodoroFrame(self.tab("Main")) - self.main_frame.pack(expand=True, fill='both') - - self.settings_frame = SettingsFrame(self.tab("Settings")) - self.settings_frame.pack(expand=True, fill='both') - - self.stats_frame = StatsFrame(self.tab("Stats")) - self.stats_frame.pack(expand=True, fill='both') - - -class PomodoroApp(ctk.CTk): - WIDTH = 350 - HEIGHT = 400 - - def __init__(self): - super().__init__() - - self.title("Pomodoro Tracker") - self.geometry(f"{PomodoroApp.WIDTH}x{PomodoroApp.HEIGHT}") - - self.tabview = TabView(master=self) - self.tabview.pack(pady=(15, 30), expand=True, fill='y') - - -if __name__ == "__main__": - config = load_config() - ctk.set_default_color_theme(f"themes/{config['theme']}.json") - ctk.set_appearance_mode("dark") - app = PomodoroApp() - app.mainloop() diff --git a/pomodorodiscord/main.py b/pomodorodiscord/main.py new file mode 100644 index 0000000..28ad0e0 --- /dev/null +++ b/pomodorodiscord/main.py @@ -0,0 +1,45 @@ +import customtkinter as ctk +from src.pomodoro_frame import PomodoroFrame +from src.settings_frame import SettingsFrame +from src.stats_frame import StatsFrame +from src.utils import load_config + + +class TabView(ctk.CTkTabview): + def __init__(self, master, **kwargs): + super().__init__(master, **kwargs) + + self.add("Main") + self.add("Settings") + self.add("Stats") + + self.main_frame = PomodoroFrame(self.tab("Main")) + self.main_frame.pack(expand=True, fill='both') + + self.settings_frame = SettingsFrame(self.tab("Settings")) + self.settings_frame.pack(expand=True, fill='both') + + self.stats_frame = StatsFrame(self.tab("Stats")) + self.stats_frame.pack(expand=True, fill='both') + + +class PomodoroApp(ctk.CTk): + WIDTH = 350 + HEIGHT = 400 + + def __init__(self): + super().__init__() + + self.title("Pomodoro Tracker") + self.geometry(f"{PomodoroApp.WIDTH}x{PomodoroApp.HEIGHT}") + + self.tabview = TabView(master=self) + self.tabview.pack(pady=(15, 30), expand=True, fill='y') + + +if __name__ == "__main__": + config = load_config() + ctk.set_default_color_theme(f"themes/{config['theme']}.json") + ctk.set_appearance_mode("dark") + app = PomodoroApp() + app.mainloop() diff --git a/beep.mp3 b/pomodorodiscord/sounds/beep.mp3 similarity index 100% rename from beep.mp3 rename to pomodorodiscord/sounds/beep.mp3 diff --git a/pomodorodiscord/src/__init__.py b/pomodorodiscord/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pomodorodiscord/src/pomodoro_frame.py b/pomodorodiscord/src/pomodoro_frame.py new file mode 100644 index 0000000..aeb30d7 --- /dev/null +++ b/pomodorodiscord/src/pomodoro_frame.py @@ -0,0 +1,146 @@ +import os +import json +import customtkinter as ctk +from datetime import datetime +from src.utils import load_config, DEF_POMODORO_MINS, DEF_SB_MINS, DEF_LB_MINS, beep + +BREAK_BTN_COLOR = "#9a9a9a" +BREAK_HOVER = "#adaaaa" +RESET_BTN_COLOR = "#bd9909" +RESET_HOVER = "#dbb30f" + + +class PomodoroFrame(ctk.CTkFrame): + def __init__(self, master): + super().__init__(master) + self.data_file = 'data.json' + config = load_config() + + # Helper text that appears when a break is running + self.break_text = ctk.StringVar(value="") + self.break_label = ctk.CTkLabel(self, textvariable=self.break_text, font=("Roboto", 15)) + self.break_label.pack(pady=(5, 0)) + + # Display + self.pomodoro_time = config.get("pomodoro_time", DEF_POMODORO_MINS) * 60 + + minutes, seconds = divmod(self.pomodoro_time, 60) + self.timer_display = ctk.CTkLabel(self, text=f"{minutes:02d}:{seconds:02d}", font=("Helvetica", 58)) + self.timer_display.pack(pady=(15, 41)) + + # Controls + self.start_button = ctk.CTkButton(self, text="Start", font=("Roboto", 17), border_width=2, command=self.start_timer) + self.start_color = self.start_button.cget("fg_color") + self.start_button.pack(pady=(0, 10)) + + self.sb_button = ctk.CTkButton(self, text="Short break", font=("Roboto", 17), fg_color=BREAK_BTN_COLOR, hover_color=BREAK_HOVER, command=self.short_break) + self.sb_button.pack(pady=(0, 10)) + + self.lb_button = ctk.CTkButton(self, text="Long break", font=("Roboto", 17), fg_color=BREAK_BTN_COLOR, hover_color=BREAK_HOVER, command=self.long_break) + self.lb_button.pack(pady=(0, 10)) + + self.reset_button = ctk.CTkButton(self, text="Reset", font=("Roboto", 17), fg_color=RESET_BTN_COLOR, hover_color=RESET_HOVER, command=self.reset) + self.reset_button.pack() + + # State + self.running = False + self.break_running = False + self.next_timer_update = None + self.remaining_time = self.pomodoro_time + + def start_timer(self): + if self.next_timer_update: + self.after_cancel(self.next_timer_update) + + self.running = not self.running + btn_text = "Pause" if self.running else "Resume" + btn_fg = "transparent" if self.running else self.start_color + self.start_button.configure(text=btn_text, fg_color=btn_fg, hover=not self.running) + self.update_timer() + + def update_timer(self): + if self.running and self.remaining_time > 0: + self.remaining_time -= 1 + if not self.break_running: + self.track_second() + minutes, seconds = divmod(self.remaining_time, 60) + self.timer_display.configure(text=f"{minutes:02d}:{seconds:02d}") + self.next_timer_update = self.after(1000, self.update_timer) + elif self.remaining_time == 0: + self.session_ended() + + def track_second(self): + if os.path.exists(self.data_file): + with open(self.data_file, 'r') as file: + data = json.load(file) + data['total_seconds_studied'] += 1 + else: + data = {'total_seconds_studied': 0} + + with open(self.data_file, 'w') as file: + json.dump(data, file, indent=4) + + def reset(self, to:str="pomodoro_time", default:int=DEF_POMODORO_MINS): + self.running = False + self.break_running = False + self.break_text.set("") + + if self.next_timer_update: + self.after_cancel(self.next_timer_update) + self.next_timer_update = None + + config = load_config() + # TODO: Make cleaner? + self.pomodoro_time = int(config.get(to, default) * 60) + + # Reset the timer + self.remaining_time = self.pomodoro_time + minutes, seconds = divmod(self.remaining_time, 60) + self.timer_display.configure(text=f"{minutes:02d}:{seconds:02d}") + self.start_button.configure(text="Start", fg_color=self.start_color) + + def session_ended(self): + was_break = self.break_running + # Reset sets break_running to false + # TODO: surely there is a better way + + self.reset() + beep.play() + + if was_break: + return + + current_date = datetime.now().strftime("%Y-%m-%d") + + # TODO: I don't like the if check every second + if os.path.exists(self.data_file): + with open(self.data_file, 'r') as file: + data = json.load(file) + data['total_pomodoro_sessions'] = data.get('total_pomodoro_sessions', 0) + 1 + + if 'sessions_by_date' not in data: + data['sessions_by_date'] = {} + data['sessions_by_date'][current_date] = data['sessions_by_date'].get(current_date, 0) + 1 + else: + data = { + 'total_seconds_studied': 0, + 'total_pomodoro_sessions': 1, + 'sessions_by_date': {current_date: 1} + } + + with open(self.data_file, 'w') as file: + json.dump(data, file, indent=4) + + + def short_break(self): + self.reset(to="short_break_time", default=DEF_SB_MINS) + self.break_running = True + self.break_text.set("Short break") + self.start_timer() + + def long_break(self): + self.reset(to="long_break_time", default=DEF_LB_MINS) + self.break_running = True + self.break_text.set("Long break") + self.start_timer() + diff --git a/pomodorodiscord/src/settings_frame.py b/pomodorodiscord/src/settings_frame.py new file mode 100644 index 0000000..c1f0871 --- /dev/null +++ b/pomodorodiscord/src/settings_frame.py @@ -0,0 +1,99 @@ +import os +import customtkinter as ctk +from src.utils import load_config, save_config, reload_app, DEF_POMODORO_MINS, DEF_SB_MINS, DEF_LB_MINS, beep + + +class EntryFrame(ctk.CTkFrame): + def __init__(self, master, text, config, config_attr, defvalue, command): + super().__init__(master) + + self.label = ctk.CTkLabel(self, text=text) + self.label.pack(pady=(10, 10), padx=(10, 10)) + + # Inner frame to hold the entry and button + self.controls_frame = ctk.CTkFrame(self) + self.controls_frame.pack(fill=ctk.X, expand=True, padx=10) + + self.entry_var = ctk.IntVar(value=config.get(config_attr, defvalue)) + self.entry = ctk.CTkEntry(self.controls_frame, width=35, textvariable=self.entry_var) + self.entry.pack(side=ctk.LEFT, fill=ctk.X, expand=True, padx=(0, 10)) + + self.set_button = ctk.CTkButton(self.controls_frame, width=120, text="Set", command=command) + self.set_button.pack(side=ctk.RIGHT) + + def get(self): + return self.entry_var.get() + + +class SettingsFrame(ctk.CTkScrollableFrame): + def __init__(self, master): + super().__init__(master) + self.themes_dir = 'themes' + config = load_config() + + # Pomodoro Duration + self.pomodoro_entry = EntryFrame(self, "Pomodoro Duration (mins):", config, "pomodoro_time", DEF_POMODORO_MINS, self.change_pomodoro_time) + self.pomodoro_entry.pack() + + # Short Break Duration + self.sb_entry = EntryFrame(self, "Short Break Duration (mins):", config, "short_break_time", DEF_SB_MINS, self.change_sb_time) + self.sb_entry.pack(pady=(5, 0)) + + # Long Break Duration + self.lb_entry = EntryFrame(self, "Long Break Duration (mins):", config, "long_break_time", DEF_LB_MINS, self.change_lb_time) + self.lb_entry.pack(pady=(5, 0)) + + # Selecting theme + self.theme_label = ctk.CTkLabel(self, text="Select Theme:") + self.theme_label.pack(pady=(20, 0)) + + self.theme_options = [os.path.splitext(theme)[0] for theme in os.listdir(self.themes_dir) if theme.endswith('.json')] + + selected = ctk.StringVar(value=config.get('theme', 'Default')) + self.theme_menu = ctk.CTkOptionMenu(self, variable=selected, values=self.theme_options, anchor="n", command=self.change_theme) + self.theme_menu.pack(pady=(10, 0)) + + # Volume Slider + self.volume_label = ctk.CTkLabel(self, text="Adjust Beep Volume:") + self.volume_label.pack(pady=(20, 0)) + + self.volume_slider = ctk.CTkSlider(self, from_=0, to=100, number_of_steps=100, command=self.change_volume) + self.volume_slider.pack(pady=(10, 0)) + + # Set the slider to the current volume + self.volume_slider.set(config.get('volume', 10)) # Default volume to 10% if not set + self.change_volume(config.get('volume', 10)) + + # Play Beep button + self.beep_button = ctk.CTkButton(self, text="Play", width=70, command=beep.play) + self.beep_button.pack(pady=(15, 0)) + + def change_time(self, entry, config_param): + time = entry.get() + if time and time > 0: + config = load_config() + config[config_param] = time + save_config(config) + + def change_pomodoro_time(self): + self.change_time(self.pomodoro_entry, "pomodoro_time") + + def change_sb_time(self): + self.change_time(self.sb_entry, "short_break_time") + + def change_lb_time(self): + self.change_time(self.lb_entry, "long_break_time") + + def change_theme(self, theme): + config = load_config() + config['theme'] = theme + save_config(config) + reload_app() + + def change_volume(self, volume): + volume = float(volume) / 100 + beep.set_volume(volume) + config = load_config() + config['volume'] = int(volume * 100) + save_config(config) + diff --git a/pomodorodiscord/src/stats_frame.py b/pomodorodiscord/src/stats_frame.py new file mode 100644 index 0000000..3c3a03c --- /dev/null +++ b/pomodorodiscord/src/stats_frame.py @@ -0,0 +1,44 @@ +import customtkinter as ctk +from datetime import datetime +from src.utils import load_data + + +class StatsFrame(ctk.CTkFrame): + def __init__(self, master): + super().__init__(master) + self.data_file = 'data.json' + + self.total_today_var = ctk.StringVar(value="Pomodoros Today: 0") + self.total_today = ctk.CTkLabel(self, textvariable=self.total_today_var, font=("Helvetica", 16, "bold"), anchor="w") + self.total_today.pack(padx=20, pady=(20, 0), fill="x") + + self.total_hours_var = ctk.StringVar(value="Total Hours Studied: 0") + self.total_hours = ctk.CTkLabel(self, textvariable=self.total_hours_var, font=("Helvetica", 16, "bold"), anchor="w") + self.total_hours.pack(padx=20, pady=(20, 0), fill="x") + + self.total_var = ctk.StringVar(value="Total Pomodoros: 0") + self.total = ctk.CTkLabel(self, textvariable=self.total_var, font=("Helvetica", 16, "bold"), anchor="w") + self.total.pack(padx=20, pady=(20, 0), fill="x") + + self.update_stats = ctk.CTkButton(self, text="Update", width=90, font=("Roboto", 16), command=self.load_stats) + self.update_stats.pack(pady=40, side=ctk.BOTTOM) + + self.load_stats() + + def load_stats(self): + data = load_data() + if not data: + return + + current_date = datetime.now().strftime("%Y-%m-%d") + total_today = data.get('sessions_by_date', {}).get(current_date, 0) + + self.total_today_var.set(f"Pomodoros Today: {total_today}") + + total_seconds_studied = data.get('total_seconds_studied', 0) + total_hours = total_seconds_studied / 3600 + + self.total_hours_var.set(f"Total Hours Studied: {total_hours:.1f}") + + self.total_var.set(f"Total Pomodoros: {data.get('total_pomodoro_sessions', 0)}") + diff --git a/pomodorodiscord/src/utils.py b/pomodorodiscord/src/utils.py new file mode 100644 index 0000000..133a1c3 --- /dev/null +++ b/pomodorodiscord/src/utils.py @@ -0,0 +1,34 @@ +import os +import sys +import json +from pygame import mixer + +mixer.init() +beep = mixer.Sound('sounds/beep.mp3') + +DEF_POMODORO_MINS = 25 +DEF_SB_MINS = 5 +DEF_LB_MINS = 10 + + +def load_file(filename, on_no_file=None): + try: + with open(filename, 'r') as file: + data = json.load(file) + except FileNotFoundError: + return on_no_file + + return data + +def load_config(): + return load_file('config.json', {'theme': 'Default'}) + +def load_data(): + return load_file('data.json', None) + +def save_config(config): + with open('config.json', 'w') as config_file: + json.dump(config, config_file, indent=4) + +def reload_app(): + os.execl(sys.executable, sys.executable, *sys.argv) diff --git a/themes/Anthracite.json b/pomodorodiscord/themes/Anthracite.json similarity index 100% rename from themes/Anthracite.json rename to pomodorodiscord/themes/Anthracite.json diff --git a/themes/Blue.json b/pomodorodiscord/themes/Blue.json similarity index 100% rename from themes/Blue.json rename to pomodorodiscord/themes/Blue.json diff --git a/themes/Default.json b/pomodorodiscord/themes/Default.json similarity index 100% rename from themes/Default.json rename to pomodorodiscord/themes/Default.json diff --git a/themes/GhostTrain.json b/pomodorodiscord/themes/GhostTrain.json similarity index 100% rename from themes/GhostTrain.json rename to pomodorodiscord/themes/GhostTrain.json diff --git a/themes/Green.json b/pomodorodiscord/themes/Green.json similarity index 100% rename from themes/Green.json rename to pomodorodiscord/themes/Green.json diff --git a/themes/MoonlitSky.json b/pomodorodiscord/themes/MoonlitSky.json similarity index 100% rename from themes/MoonlitSky.json rename to pomodorodiscord/themes/MoonlitSky.json