[AI] Tracking

This commit is contained in:
Mikan
2026-03-10 02:47:12 +03:00
parent bf51fc454d
commit 97dbcfd8f0
3 changed files with 88 additions and 6 deletions

View File

@@ -26,6 +26,7 @@ class Application:
self.is_idle: bool = False self.is_idle: bool = False
self._tracking_thread: Optional[threading.Thread] = None self._tracking_thread: Optional[threading.Thread] = None
self._running = True self._running = True
self.session_start_time: Optional[float] = None
# Загружаем задачу # Загружаем задачу
if self.config.current_task_id: if self.config.current_task_id:
@@ -73,6 +74,7 @@ class Application:
def _check_and_update_tracking(self): def _check_and_update_tracking(self):
with self._lock: with self._lock:
if not self.tracking_enabled or not self.current_task: if not self.tracking_enabled or not self.current_task:
self.session_start_time = None
return return
current_time = time.time() current_time = time.time()
@@ -85,12 +87,17 @@ class Application:
if current_time - self.last_activity > 60 or is_blocked: if current_time - self.last_activity > 60 or is_blocked:
self.is_idle = True self.is_idle = True
self.last_window_group = None self.last_window_group = None
self.session_start_time = None
else: else:
self.is_idle = False self.is_idle = False
if self.last_window_group != group_name: if self.last_window_group != group_name:
self.last_window_group = group_name self.last_window_group = group_name
self._add_work_detail(group_name, 5.0) self._add_work_detail(group_name, 5.0)
# Начинаем/продолжаем сессию
if self.session_start_time is None:
self.session_start_time = current_time
# Сохраняем при переходе в бездействие # Сохраняем при переходе в бездействие
if not was_idle and self.is_idle: if not was_idle and self.is_idle:
self.maybe_save_task() self.maybe_save_task()
@@ -222,7 +229,11 @@ class Application:
"detail": f"{window_info.title} | {window_info.process_name}" "detail": f"{window_info.title} | {window_info.process_name}"
} }
# === Остальные методы (без изменений, но с локом где нужно) === def get_current_session_duration(self) -> float:
with self._lock:
if self.session_start_time is None:
return 0.0
return time.time() - self.session_start_time
def _load_or_create_config(self) -> TrackerConfig: def _load_or_create_config(self) -> TrackerConfig:
if self.config_path.exists(): if self.config_path.exists():

View File

@@ -12,10 +12,11 @@ if TYPE_CHECKING:
class OverlayWindow: class OverlayWindow:
def __init__(self, app: "Application"): def __init__(self, app: "Application"):
self.app = app self.app = app
self.session_timer_label = None
self.root = tk.Tk() self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.on_close) self.root.protocol("WM_DELETE_WINDOW", self.on_close)
self.root.title("Citrus Time Tracker") self.root.title("Citrus Time Tracker")
self.root.geometry("320x180") self.root.geometry("380x190")
self.root.overrideredirect(True) # убираем заголовок self.root.overrideredirect(True) # убираем заголовок
self.root.attributes("-topmost", True) # всегда поверх self.root.attributes("-topmost", True) # всегда поверх
self.root.attributes("-alpha", 0.5) # полупрозрачность по умолчанию self.root.attributes("-alpha", 0.5) # полупрозрачность по умолчанию
@@ -48,9 +49,9 @@ class OverlayWindow:
self.root.update_idletasks() self.root.update_idletasks()
screen_width = self.root.winfo_screenwidth() screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight() screen_height = self.root.winfo_screenheight()
x = (screen_width // 2) - (320 // 2) x = (screen_width // 2) - (380 // 2)
y = (screen_height // 2) - (180 // 2) y = (screen_height // 2) - (190 // 2)
self.root.geometry(f"320x180+{x}+{y}") self.root.geometry(f"380x190+{x}+{y}")
def _build_ui(self): def _build_ui(self):
# Главный фрейм с отступами # Главный фрейм с отступами
@@ -69,6 +70,9 @@ class OverlayWindow:
self.time_info_label = Label(main_frame, text="Времени нет", anchor="w", font=("TkDefaultFont", 9)) self.time_info_label = Label(main_frame, text="Времени нет", anchor="w", font=("TkDefaultFont", 9))
self.time_info_label.pack(anchor="w") self.time_info_label.pack(anchor="w")
self.session_timer_label = Label(main_frame, text="0:00", font=("Arial", 16, "bold"), fg="green")
self.session_timer_label.pack(pady=(5, 0))
# Текущее окно # Текущее окно
self.window_group_label = Label(main_frame, text="Неизвестное приложение", anchor="w", fg="gray") self.window_group_label = Label(main_frame, text="Неизвестное приложение", anchor="w", fg="gray")
self.window_group_label.pack(anchor="w") self.window_group_label.pack(anchor="w")
@@ -89,6 +93,10 @@ class OverlayWindow:
self.adjust_time_btn = Button(btn_frame, text="✏️ Время", command=self.on_adjust_time) self.adjust_time_btn = Button(btn_frame, text="✏️ Время", command=self.on_adjust_time)
self.adjust_time_btn.pack(side="left", fill="x", expand=True, padx=(2, 0)) self.adjust_time_btn.pack(side="left", fill="x", expand=True, padx=(2, 0))
Button(btn_frame, text="📊 Отчёт", command=self.on_show_report).pack(side="left", fill="x", expand=True, padx=2)
self._update_display() self._update_display()
def _update_display(self): def _update_display(self):
@@ -127,7 +135,13 @@ class OverlayWindow:
def _auto_update(self): def _auto_update(self):
self._update_display() self._update_display()
self.root.after(2000, self._auto_update) # обновляем каждые 2 сек
# Обновляем таймер сессии каждую секунду
session_sec = self.app.get_current_session_duration()
session_str = format_seconds(session_sec)
self.session_timer_label.config(text=session_str)
self.root.after(500, self._auto_update) # теперь каждую секунду
# === Обработчики событий (пока без логики, только открытие окон) === # === Обработчики событий (пока без логики, только открытие окон) ===
@@ -146,6 +160,10 @@ class OverlayWindow:
from .adjust_time_window import AdjustTimeWindow from .adjust_time_window import AdjustTimeWindow
AdjustTimeWindow(self.root, self.app, self._update_display) AdjustTimeWindow(self.root, self.app, self._update_display)
def on_show_report(self):
from .report_window import ReportWindow
ReportWindow(self.root, self.app)
# === UI-управление === # === UI-управление ===
def on_mouse_enter(self, event): def on_mouse_enter(self, event):

View File

@@ -0,0 +1,53 @@
import tkinter as tk
from tkinter import Toplevel, Text, Scrollbar, Button
from ..utils import format_seconds
class ReportWindow:
def __init__(self, parent, app: "Application"):
self.window = Toplevel(parent)
self.window.title("Отчёт по задаче")
self.window.geometry("500x400")
self.window.transient(parent)
self.window.grab_set()
if not app.current_task:
tk.Label(self.window, text="Нет активной задачи", font=("Arial", 12)).pack(pady=20)
Button(self.window, text="Закрыть", command=self.window.destroy).pack()
return
task = app.current_task
text_widget = Text(self.window, wrap="word", padx=10, pady=10)
scrollbar = Scrollbar(self.window, command=text_widget.yview)
text_widget.config(yscrollcommand=scrollbar.set)
# Собираем отчёт
report = f"Задача: {task.task_id}\n"
if task.project_id:
report += f"Проект: {task.project_id}\n"
report += f"Всего затрачено: {format_seconds(task.total_worked_seconds)}\n\n"
# Группировка по группам
group_totals = {}
for session in task.sessions:
for detail in session.details:
group = detail.group_name
group_totals[group] = group_totals.get(group, 0) + detail.duration_seconds
report += "Распределение по группам:\n"
for group, total in sorted(group_totals.items(), key=lambda x: -x[1]):
report += f"{group}: {format_seconds(total)}\n"
report += "\nПодробные сессии:\n"
for i, session in enumerate(task.sessions, 1):
report += f"\nСессия {i} ({session.start_time.strftime('%H:%M')}{session.end_time.strftime('%H:%M')}):\n"
for detail in session.details:
report += f" - {detail.group_name}: {format_seconds(detail.duration_seconds)}\n"
text_widget.insert("1.0", report)
text_widget.config(state="disabled")
text_widget.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
Button(self.window, text="Закрыть", command=self.window.destroy).pack(pady=10)