"""Data models for drafts, ratings, and categories.""" from __future__ import annotations from dataclasses import dataclass, field from datetime import datetime @dataclass class Draft: name: str # e.g. "draft-zheng-dispatch-agent-identity-management" rev: str # e.g. "00" title: str abstract: str time: str # ISO datetime from API dt_id: int | None = None # Datatracker document ID pages: int | None = None words: int | None = None group: str | None = None # Working group name (resolved) group_uri: str | None = None # Raw API URI expires: str | None = None ad: str | None = None # Area director URI shepherd: str | None = None states: list[str] = field(default_factory=list) full_text: str | None = None categories: list[str] = field(default_factory=list) tags: list[str] = field(default_factory=list) fetched_at: str | None = None @property def text_url(self) -> str: return f"https://www.ietf.org/archive/id/{self.name}-{self.rev}.txt" @property def datatracker_url(self) -> str: return f"https://datatracker.ietf.org/doc/{self.name}/" @property def date(self) -> str: """Return just the date portion of time.""" if self.time: return self.time[:10] return "" @dataclass class Rating: draft_name: str novelty: int # 1-5 maturity: int # 1-5 overlap: int # 1-5 (5 = highly overlapping with others) momentum: int # 1-5 relevance: int # 1-5 summary: str # 2-4 sentence AI summary novelty_note: str = "" maturity_note: str = "" overlap_note: str = "" momentum_note: str = "" relevance_note: str = "" categories: list[str] = field(default_factory=list) rated_at: str | None = None @property def composite_score(self) -> float: """Weighted composite: novelty and relevance matter most.""" return ( self.novelty * 0.30 + self.relevance * 0.25 + self.maturity * 0.20 + self.momentum * 0.15 + (6 - self.overlap) * 0.10 # Invert: less overlap = better )