핵심 기능: - 단축근무·표준·반일 등 다양한 근무 패턴 (5개 프리셋 + 사용자 정의) - Windows 이벤트 뷰어 자동 출퇴근 감지 - 30분 단위 연장근무 적립/사용 시스템 - 1.0/0.5/0.25일 연차·반차·반반차 - 자동 점심·저녁·외출·자동 백업·화면 잠금 자동 외출 - 한국 공휴일 자동 등록 (음력 포함, holidays 패키지) - matplotlib 차트 기반 주간/월간/패턴 통계 - 미니 위젯 + 시스템 트레이 통합 - 한국어/English i18n - 자가 업데이트 (updater.exe + Gitea Releases) 아키텍처: - core/ (db, time_calculator, notifier, i18n, version, settings_keys) - ui/ (main_window + 9 dialogs + 3 controllers) - utils/ (backup, lock_detector, debug_log, updater_client, time_format) - tests/ (66 pytest 단위) + 통합/i18n GUI 검증 CI/CD: - .gitea/workflows/ci.yml: push 시 pytest + 통합 테스트 - .gitea/workflows/release.yml: v* 태그 push 시 두 .exe 자동 빌드 + Releases 첨부 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.2 KiB
Project Conventions and Operational Gotchas
🛠️ Setup & Execution
- Dependencies:
pip install -r requirements.txt. Optional:pip install anthropicfor AI insight feature. - Run:
python main.py - Module-level tests:
- Event monitoring:
python core/event_monitor.py - Time calculation:
python core/time_calculator.py
- Event monitoring:
- Integration tests:
python _integration_test.py(35 scenarios),python _i18n_gui_test.py(5 ko/en GUI),python _gui_smoke_test.py(8 widget). All should be green before release.
🗄️ Architecture Notes (Core Business Logic)
- 8 SQLite tables in
database.db:work_records,overtime_bank,overtime_usage,leave_records,break_records,settings,achievements,holidays. Migrations ininit_database()ALTER existing DBs. work_records.dateUNIQUE — one row per workday.- Overtime tracking: earned (bank) and used (usage) tables separate. Both have NULLable
work_record_idfor manual entries — never filter them out. - Time representation:
TimeCalculator.work_minutesis the canonical attribute (int).work_hoursis a property for compatibility. UI/DB syncWORK_MINUTES ↔ WORK_HOURSautomatically (floor on minutes→hours). - Settings: all keys defined in
core/settings_keys.py. Import constants — never use raw strings.get_setting()returns string; useget_setting_int/float/bool()helpers or read fromget_settings()dict (already typed). - i18n:
tr('key', **kwargs)andtr_html('help.html.X'). Sentences use format placeholders. Language switch requires restart for full effect.
⚠️ Critical Invariants (MUST PRESERVE)
1. Time-off subtraction order in update_display()
Pass actual break_minutes to calculate_remaining_time. Subtract total_time_off = overtime_used + leave_used from the resulting timedelta AFTER the call. NEVER mutate break_minutes to break_minutes - overtime_used — this caused a +29h display bug previously and was the original Phase 1 fix.
2. Hot-path caching
update_display() runs at 1Hz. Any DB call inside this method must be cached (see _auto_lunch_enabled_cache, _today_non_working_cache, cached_time_format). Periodic checks (health/weekly notifications) are gated by now.minute % 5 == 0.
3. Time format separation
24-hour datetime for ALL internal calculation. 12-hour conversion happens only in MainWindow.format_time() (adds Korean "오전"/"오후" markers when applicable).
4. Workday boundary
workday_boundary_hour (default 6). Overnight work stays on the previous day's record until that hour. Don't naively use date.today() in time logic without considering this rollover.
5. Migration idempotency
All migrate_* methods must early-return if already applied. Use sentinel keys (balance_adjustment_migrated_v2, annual_leave_keys_migrated) — without them, every startup runs the migration query.
⚙️ Build Process
python -m PyInstaller --clean main.spec
- Output:
dist/main.exe(console disabled, UPX compressed) main.specincludes icon (3d-alarm.ico), data file (3d-alarm.png)- Stale
dist/main.exerunning →PermissionError. Kill it first. - Optional packages (
holidays,anthropic) only baked in if installed in build env.
🚦 External Integrations
- Anthropic Claude API (optional):
core/ai_analysis.py. Withoutanthropicpackage or API key, falls back tostatic_summary(). Don't crash the stats view. - HTTP API (
utils/http_api.py): bound to127.0.0.1:17389only — never expose externally. Read-only by design. - Cloud sync via
db_path_override: settings stores DB path; main.py + main_window.py both bootstrap with default DB to read this key, then reopen with override path. Don't break the bootstrap order. holidayspackage:add_korean_holidays_auto()returns-1if package missing → UI falls back toadd_korean_holidays()(8 fixed dates).
🐞 Past Incidents
- +29h remaining time bug (Phase 1): caused by
break_minutes -= overtime_used. Fixed by subtractingtotal_time_offAFTERcalculate_remaining_timecall. Never re-introduce. - Manual overtime invisible:
overtime_viewpreviously filteredwork_record_id IS NOT NULL. Manual additions (no work_record) were hidden. Show all rows; label NULL rows as "수동 추가" / "Manual". annual_leave_totalvsannual_leave_days: two keys for the same value. UI used_days, internal methods used_total. Now auto-synced insave_settings(). If you add a method reading either, also handle the sibling.- Banker's rounding:
round(450/60)= 8 in Python (round-half-even). Useint(value) // 60(floor) for hours derivation when consistency matters.
🌐 i18n Coverage Status
- Fully translated: window titles, menus, buttons, group boxes, mini widget, tray menu, all 6 notifications, HelpView 6 tabs, settings_view core labels, stats_view labels.
- Partially translated: settings_view sub-labels (입력란 placeholder 등), calendar_view detail labels.
- Not yet translated: dialog inner labels in break_view/overtime_view/leave_view. Window titles for these dialogs ARE translated.
Adding new translations: add key to _DICT['ko'] AND _DICT['en'], replace literal with tr('key').