All checks were successful
Build And Test / publish (push) Successful in 48s
Use datetime.now(timezone.utc) instead of deprecated utcnow(). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
101 lines
3.3 KiB
Python
101 lines
3.3 KiB
Python
"""SQLModel database models for caching PIM data."""
|
|
|
|
from datetime import datetime, timezone
|
|
from typing import Optional
|
|
from sqlmodel import SQLModel, Field
|
|
|
|
|
|
def utc_now() -> datetime:
|
|
"""Return timezone-aware UTC datetime."""
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
class CacheMeta(SQLModel, table=True):
|
|
"""Generic key-value cache metadata."""
|
|
|
|
__tablename__ = "cache_meta"
|
|
|
|
key: str = Field(primary_key=True)
|
|
value: Optional[str] = None
|
|
expires_at: Optional[int] = None
|
|
|
|
|
|
class EmailCache(SQLModel, table=True):
|
|
"""Cached email data."""
|
|
|
|
__tablename__ = "email_cache"
|
|
|
|
id: str = Field(primary_key=True)
|
|
mailbox: str = Field(index=True)
|
|
subject: Optional[str] = None
|
|
from_address: Optional[str] = None
|
|
date: Optional[datetime] = Field(default=None, index=True)
|
|
is_read: bool = False
|
|
is_flagged: bool = False
|
|
snippet: Optional[str] = None
|
|
full_data: Optional[str] = Field(default=None, description="JSON blob of full email data")
|
|
cached_at: datetime = Field(default_factory=utc_now)
|
|
|
|
|
|
class EventCache(SQLModel, table=True):
|
|
"""Cached calendar event data."""
|
|
|
|
__tablename__ = "event_cache"
|
|
|
|
id: str = Field(primary_key=True)
|
|
calendar_id: str = Field(index=True)
|
|
title: Optional[str] = None
|
|
start_time: Optional[datetime] = Field(default=None, index=True)
|
|
end_time: Optional[datetime] = None
|
|
full_data: Optional[str] = Field(default=None, description="JSON blob of full event data")
|
|
cached_at: datetime = Field(default_factory=utc_now)
|
|
|
|
|
|
class ContactCache(SQLModel, table=True):
|
|
"""Cached contact data."""
|
|
|
|
__tablename__ = "contact_cache"
|
|
|
|
id: str = Field(primary_key=True)
|
|
addressbook_id: str = Field(index=True)
|
|
display_name: Optional[str] = Field(default=None, index=True)
|
|
primary_email: Optional[str] = None
|
|
full_data: Optional[str] = Field(default=None, description="JSON blob of full contact data")
|
|
cached_at: datetime = Field(default_factory=utc_now)
|
|
|
|
|
|
class SyncState(SQLModel, table=True):
|
|
"""Track sync state for incremental updates."""
|
|
|
|
__tablename__ = "sync_state"
|
|
|
|
resource_type: str = Field(primary_key=True, description="Type: mailbox, calendar, addressbook")
|
|
resource_id: str = Field(primary_key=True)
|
|
last_sync: Optional[datetime] = None
|
|
sync_token: Optional[str] = None
|
|
|
|
|
|
class SeenEmail(SQLModel, table=True):
|
|
"""Track emails that have been processed for notifications."""
|
|
|
|
__tablename__ = "seen_emails"
|
|
|
|
id: Optional[int] = Field(default=None, primary_key=True)
|
|
email_uid: str = Field(index=True, description="IMAP UID of the email")
|
|
mailbox: str = Field(index=True, description="Mailbox path (e.g., INBOX)")
|
|
message_id: Optional[str] = Field(
|
|
default=None,
|
|
index=True,
|
|
description="RFC 2822 Message-ID header for cross-server dedup"
|
|
)
|
|
from_address: Optional[str] = Field(default=None, description="Sender email for logging")
|
|
subject: Optional[str] = Field(default=None, description="Subject for logging")
|
|
email_date: Optional[datetime] = Field(default=None, description="Email date")
|
|
seen_at: datetime = Field(default_factory=utc_now)
|
|
notification_sent: bool = Field(default=False)
|
|
notification_sent_at: Optional[datetime] = None
|
|
notification_error: Optional[str] = Field(
|
|
default=None,
|
|
description="Last error if notification failed"
|
|
)
|