Source Code Viewer

Python - Time Keeper Application


import tkinter as tk
from tkinter import messagebox
from pynput import keyboard, mouse
import threading
import time
import os
import webbrowser
import requests

class TimeKeeperApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Time Keeper")
        self.root.geometry("400x200")  # <-- Set window size here

        self.root.iconphoto(True, tk.PhotoImage(file="EQPD.png"))


        self.active_time_label = tk.Label(root, text="Active Time: 0.00 sec", font=("Arial", 16))
        self.active_time_label.pack(pady=20)

        self.paused_label = tk.Label(root, text="Paused", font=("Arial", 14), fg="red")
        self.paused_label.pack()
        self.paused_label.pack_forget()  # Initially hidden

        self.start_button = tk.Button(root, text="Start", width=15, height=2, command=self.start_timer)
        self.start_button.pack(pady=5)

        self.stop_button = tk.Button(root, text="Stop", width=15, height=2, command=self.stop_timer, state=tk.DISABLED)
        self.stop_button.pack(pady=5)

        self.timer_running = False
        self.timer_paused = False
        self.active_time = 0
        self.last_activity_time = time.time()
        self.activity_timeout = 120  # seconds of inactivity before pause

        self.time_log = []
        self.current_session_start = None

        self.keyboard_listener = None
        self.mouse_listener = None

        self.monitor_thread = None
        self.gui_update_job = None

    def start_timer(self):
        if not self.timer_running:
            self.timer_running = True
            self.timer_paused = False
            self.active_time = 0
            self.current_session_start = time.time()
            self.last_activity_time = time.time()
            self.time_log = []

            self.start_button.config(state=tk.DISABLED)
            self.stop_button.config(state=tk.NORMAL)

            # Start monitoring activity
            self.monitor_thread = threading.Thread(target=self.monitor_activity)
            self.monitor_thread.daemon = True
            self.monitor_thread.start()

            # Start input listeners
            self.keyboard_listener = keyboard.Listener(on_press=self.update_activity)
            self.mouse_listener = mouse.Listener(on_move=self.update_activity, on_click=self.update_activity)
            self.keyboard_listener.start()
            self.mouse_listener.start()

            # Start GUI time updater
            self.update_gui_time()

    def stop_timer(self):
        if self.timer_running:
            self.timer_running = False

            # Record final session if active
            if not self.timer_paused and self.current_session_start:
                self.time_log.append((self.current_session_start, time.time()))

            self.start_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)

            if self.keyboard_listener:
                self.keyboard_listener.stop()
            if self.mouse_listener:
                self.mouse_listener.stop()

            if self.gui_update_job:
                self.root.after_cancel(self.gui_update_job)

            total_time = sum(end - start for start, end in self.time_log)
            self.save_log(total_time)

    def update_activity(self, *args):
        self.last_activity_time = time.time()

    def monitor_activity(self):
        while self.timer_running:
            current_time = time.time()

            if self.timer_paused:
                if current_time - self.last_activity_time < self.activity_timeout:
                    # Resume
                    self.timer_paused = False
                    self.current_session_start = time.time()
            else:
                if current_time - self.last_activity_time >= self.activity_timeout:
                    # Pause
                    self.timer_paused = True
                    if self.current_session_start:
                        self.time_log.append((self.current_session_start, current_time))
                        self.current_session_start = None
            time.sleep(1)

    def update_gui_time(self):
        if self.timer_running:
            # Recalculate active time
            total = sum(end - start for start, end in self.time_log)
            if not self.timer_paused and self.current_session_start:
                total += time.time() - self.current_session_start

            # Format as HH:MM:SS
            hours, remainder = divmod(int(total), 3600)
            minutes, seconds = divmod(remainder, 60)
            time_str = f"{hours:02}:{minutes:02}:{seconds:02}"

            self.active_time_label.config(text=f"Active Time: {time_str}")

            # Show or hide pause indicator
            if self.timer_paused:
                self.paused_label.pack()
            else:
                self.paused_label.pack_forget()

            self.gui_update_job = self.root.after(500, self.update_gui_time)

    def save_log(self, total_time):
        def format_duration(seconds):
            hours, remainder = divmod(int(seconds), 3600)
            minutes, seconds = divmod(remainder, 60)
            return f"{hours:02}:{minutes:02}:{seconds:02}"

        log_lines = [
            "Time Keeper Log\n",
            f"Total Active Time: {format_duration(total_time)}\n",
            "\nSessions:\n"
        ]

        sessions_payload = []

        for i, (start, end) in enumerate(self.time_log, 1):
            duration = end - start
            log_lines.append(
                f"{i}. Start: {time.ctime(start)}, End: {time.ctime(end)}, Duration: {format_duration(duration)}\n"
            )
            sessions_payload.append({
                "start": start,
                "end": end,
                "duration_seconds": round(duration, 2),
                "duration_formatted": format_duration(duration)
            })

        log_path = os.path.join(os.getcwd(), "time_keeper_log.txt")
        with open(log_path, "w") as f:
            f.writelines(log_lines)

        messagebox.showinfo("Time Keeper", f"Timer stopped.\nTotal Active Time: {format_duration(total_time)}.")
        webbrowser.open(f"file://{log_path}")

        # Send to Google Apps Script Web App
        try:
            url = "API URL"
            payload = {
                "timer_start": int(time.time() * 1000),
                "sessions": sessions_payload
            }

            response = requests.post(url, json=payload)
            print("Status:", response.status_code)
            print("Response:", response.text)

            if response.status_code == 200:
                print("Logged to Google Sheet.")
            else:
                print("Failed to log to Google Sheet:", response.text)
        except Exception as e:
            print("Error sending data to Google Apps Script:", e)

if __name__ == "__main__":
    root = tk.Tk()
    app = TimeKeeperApp(root)
    root.mainloop()