Files
hyungi_document_server/app/models/event_history.py
T
Hyungi Ahn 9d9b3359b0 feat(events): PR-1 Events Core — schema + ORM + 최소 API
개인 운영 로그 / 일정 / 할 일 / 회고용 1차 컨테이너 도메인 신설.
plan: ~/.claude/plans/beszel-tingly-sloth.md (라운드 12 v6).

Schema:
- enum 5종 (event_kind / event_status / event_source / event_actor / history_change_kind)
- events 테이블: kind(task|calendar_event|activity_log) + lifecycle 7-state status
- events_history: lifecycle op 자동 기록, FK RESTRICT (이력은 시점 사실)
- CHECK: calendar_event → start_at NOT NULL / activity_log → started_at|ended_at NOT NULL
- partial unique (source, source_ref) — 외부 source dedup (PR-4 활용)
- partial index (active status / activity_log timeline)

API:
- POST /api/events (kind=activity_log shortcut: status=done + ended_at=now() default)
- GET /api/events/{id} | /api/events?kind&status&from&to&project_tag&source
- PATCH /api/events/{id} (extra=forbid + 시간 필드 변경 시 reschedule history)
- POST /api/events/{id}/{complete,cancel,defer,reactivate} (history 자동)
- GET /api/events/today (Asia/Seoul default, deferred 는 defer_until<=now() 만)
- GET /api/events/inbox | /api/events/activity?from&to

제외 (PR-2~5 또는 백로그):
- DELETE (회고 데이터 → /cancel 일관화)
- log shortcut / upcoming endpoint (POST + GET ?from&to 로 흡수)
- /ingest (PR-4 MailPlus forward 시 정확한 요구로 추가)
- iCal export / ntfy 알림 / recurrence / 일반 edit history
2026-05-11 07:19:04 +09:00

44 lines
1.4 KiB
Python

"""events_history ORM — events 의 lifecycle 변경 이력 (append-only).
PR-1 (migrations 248~249). FK ON DELETE RESTRICT 로 부모 events row 직접 삭제 차단
(feedback_history_table_fk_restrict.md — 이력은 시점 사실).
"""
from datetime import datetime
from typing import Any
from sqlalchemy import BigInteger, DateTime, ForeignKey
from sqlalchemy.dialects.postgresql import ENUM as PgEnum
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column
from core.database import Base
from models.event import EventActorEnum
HistoryChangeKindEnum = PgEnum(
"create",
"reschedule",
"defer",
"reactivate",
"complete",
"cancel",
name="history_change_kind",
create_type=False,
)
class EventHistory(Base):
__tablename__ = "events_history"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
event_id: Mapped[int] = mapped_column(
BigInteger, ForeignKey("events.id", ondelete="RESTRICT"), nullable=False
)
changed_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=datetime.now, nullable=False
)
changed_by: Mapped[str] = mapped_column(EventActorEnum, nullable=False)
change_kind: Mapped[str] = mapped_column(HistoryChangeKindEnum, nullable=False)
before: Mapped[dict[str, Any] | None] = mapped_column(JSONB)
after: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)