2026-05-07 16:31:56 +08:00

163 lines
6.3 KiB
Python

from __future__ import annotations
import json
import sqlite3
import threading
from pathlib import Path
from typing import Any
class Database:
def __init__(self, database_path: Path, employee_seed_path: Path):
database_path.parent.mkdir(parents=True, exist_ok=True)
self.connection = sqlite3.connect(database_path, check_same_thread=False)
self.connection.row_factory = sqlite3.Row
self._lock = threading.RLock()
self._migrate()
self.load_employees(employee_seed_path)
def close(self) -> None:
with self._lock:
self.connection.close()
def _migrate(self) -> None:
with self._lock:
self.connection.executescript(
"""
CREATE TABLE IF NOT EXISTS employees (
id INTEGER PRIMARY KEY AUTOINCREMENT,
feishu_user_id TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
department TEXT NOT NULL DEFAULT '',
manager TEXT NOT NULL DEFAULT '',
active INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS daily_reports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
feishu_user_id TEXT NOT NULL,
employee_name TEXT NOT NULL,
report_date TEXT NOT NULL,
today_done TEXT NOT NULL,
tomorrow_plan TEXT NOT NULL,
blockers TEXT NOT NULL DEFAULT '',
help_needed TEXT NOT NULL DEFAULT '',
submitted_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(feishu_user_id, report_date)
);
"""
)
self.connection.commit()
def load_employees(self, employee_seed_path: Path) -> None:
if not employee_seed_path.exists():
return
employees = json.loads(employee_seed_path.read_text(encoding="utf-8"))
self.upsert_employees(employees)
def upsert_employees(self, employees: list[dict[str, Any]]) -> None:
with self._lock, self.connection:
for employee in employees:
self.connection.execute(
"""
INSERT INTO employees (feishu_user_id, name, department, manager, active, updated_at)
VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
ON CONFLICT(feishu_user_id) DO UPDATE SET
name = excluded.name,
department = excluded.department,
manager = excluded.manager,
active = excluded.active,
updated_at = CURRENT_TIMESTAMP
""",
(
employee["feishu_user_id"],
employee["name"],
employee.get("department", ""),
employee.get("manager", ""),
0 if employee.get("active") is False else 1,
),
)
def upsert_employee(self, employee: dict[str, Any]) -> None:
self.upsert_employees([employee])
def list_active_employees(self) -> list[dict[str, Any]]:
with self._lock:
rows = self.connection.execute(
"""
SELECT feishu_user_id, name, department, manager, active
FROM employees
WHERE active = 1
ORDER BY department, name
"""
).fetchall()
return [dict(row) for row in rows]
def find_employee(self, feishu_user_id: str) -> dict[str, Any] | None:
with self._lock:
row = self.connection.execute(
"""
SELECT feishu_user_id, name, department, manager, active
FROM employees
WHERE feishu_user_id = ?
""",
(feishu_user_id,),
).fetchone()
return dict(row) if row else None
def upsert_report(self, report: dict[str, Any]) -> None:
with self._lock, self.connection:
self.connection.execute(
"""
INSERT INTO daily_reports (
feishu_user_id, employee_name, report_date, today_done, tomorrow_plan,
blockers, help_needed, submitted_at, updated_at
)
VALUES (
:feishu_user_id, :employee_name, :report_date, :today_done, :tomorrow_plan,
:blockers, :help_needed, :submitted_at, :updated_at
)
ON CONFLICT(feishu_user_id, report_date) DO UPDATE SET
employee_name = excluded.employee_name,
today_done = excluded.today_done,
tomorrow_plan = excluded.tomorrow_plan,
blockers = excluded.blockers,
help_needed = excluded.help_needed,
updated_at = excluded.updated_at
""",
report,
)
def list_reports_for_date(self, report_date: str) -> list[dict[str, Any]]:
with self._lock:
rows = self.connection.execute(
"""
SELECT id, feishu_user_id, employee_name, report_date, today_done,
tomorrow_plan, blockers, help_needed, submitted_at, updated_at
FROM daily_reports
WHERE report_date = ?
ORDER BY employee_name
""",
(report_date,),
).fetchall()
return [dict(row) for row in rows]
def list_reports_for_employee(self, feishu_user_id: str, limit: int = 10) -> list[dict[str, Any]]:
with self._lock:
rows = self.connection.execute(
"""
SELECT id, feishu_user_id, employee_name, report_date, today_done,
tomorrow_plan, blockers, help_needed, submitted_at, updated_at
FROM daily_reports
WHERE feishu_user_id = ?
ORDER BY report_date DESC, updated_at DESC
LIMIT ?
""",
(feishu_user_id, limit),
).fetchall()
return [dict(row) for row in rows]