Spaces:
Running on Zero
Running on Zero
| """ | |
| ORM models. | |
| Conversation history and long-term memory are architecturally separate | |
| systems (see README). users/conversations/messages hold conversation | |
| history. Memory (this file's Memory class, added Day 2) is a distinct, | |
| much smaller table: only durable facts the extractor decided to STORE, | |
| never the raw conversation. | |
| """ | |
| import uuid | |
| from datetime import datetime, timezone | |
| from sqlalchemy import Column, String, DateTime, ForeignKey, Text, Float, Boolean | |
| from sqlalchemy.orm import relationship | |
| from app.database import Base | |
| def _uuid() -> str: | |
| return str(uuid.uuid4()) | |
| def _now() -> datetime: | |
| return datetime.now(timezone.utc) | |
| class User(Base): | |
| __tablename__ = "users" | |
| id = Column(String, primary_key=True, default=_uuid) | |
| external_id = Column(String, unique=True, nullable=False, index=True) | |
| created_at = Column(DateTime, default=_now) | |
| conversations = relationship("Conversation", back_populates="user", cascade="all, delete-orphan") | |
| class Conversation(Base): | |
| __tablename__ = "conversations" | |
| id = Column(String, primary_key=True, default=_uuid) | |
| user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) | |
| title = Column(String, nullable=True) | |
| created_at = Column(DateTime, default=_now) | |
| updated_at = Column(DateTime, default=_now, onupdate=_now) | |
| user = relationship("User", back_populates="conversations") | |
| messages = relationship( | |
| "Message", back_populates="conversation", cascade="all, delete-orphan", order_by="Message.created_at" | |
| ) | |
| class Message(Base): | |
| __tablename__ = "messages" | |
| id = Column(String, primary_key=True, default=_uuid) | |
| conversation_id = Column(String, ForeignKey("conversations.id"), nullable=False, index=True) | |
| role = Column(String, nullable=False) # "user" | "assistant" | |
| content = Column(Text, nullable=False) | |
| created_at = Column(DateTime, default=_now) | |
| conversation = relationship("Conversation", back_populates="messages") | |
| class Memory(Base): | |
| """ | |
| A durable fact extracted from conversation, per Day 2's extraction | |
| step. NOT the same as a Message - this table is meant to stay | |
| small and curated, one row per distinct fact worth remembering. | |
| status stays simple for now ("active" only). HISTORICAL/ACTIVE | |
| temporal tracking and conflict resolution (UPDATE/SUPERSEDE) are a | |
| later stage - don't add those columns before that logic exists to | |
| use them. | |
| """ | |
| __tablename__ = "memories" | |
| id = Column(String, primary_key=True, default=_uuid) | |
| user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) | |
| memory_type = Column(String, nullable=False) # "semantic" | "episodic" | "temporal" | |
| content = Column(Text, nullable=False) | |
| importance = Column(Float, nullable=False) | |
| confidence = Column(Float, nullable=False) | |
| status = Column(String, nullable=False, default="active") | |
| source_conversation_id = Column(String, ForeignKey("conversations.id"), nullable=True) | |
| created_at = Column(DateTime, default=_now) | |
| updated_at = Column(DateTime, default=_now, onupdate=_now) | |
| user = relationship("User") | |
| class JobApplication(Base): | |
| """ | |
| One row per job applied to. Reminders/reports (later features) read | |
| from this table rather than being separate stores - keep this one | |
| table as the single source of truth for application status. | |
| status is a plain string, not an enum column, to keep this DB | |
| portable and migrations simple - validate allowed values in the | |
| Pydantic schema layer instead (app/schemas.py), not here. | |
| """ | |
| __tablename__ = "job_applications" | |
| id = Column(String, primary_key=True, default=_uuid) | |
| user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) | |
| company = Column(String, nullable=False) | |
| role_title = Column(String, nullable=False) | |
| status = Column(String, nullable=False, default="applied") | |
| # "applied" | "interview" | "offer" | "rejected" | "no_response" | |
| job_url = Column(String, nullable=True) | |
| notes = Column(Text, nullable=True) | |
| applied_date = Column(DateTime, nullable=False) | |
| follow_up_date = Column(DateTime, nullable=True) | |
| last_contact_date = Column(DateTime, nullable=True) | |
| match_score = Column(Float, nullable=True) | |
| tailored_pitch = Column(Text, nullable=True) | |
| created_at = Column(DateTime, default=_now) | |
| updated_at = Column(DateTime, default=_now, onupdate=_now) | |
| user = relationship("User") | |
| class UserResume(Base): | |
| """ | |
| Persists user resumes. Supports multiple named resumes per user | |
| (e.g., Full Stack, AI/ML, DevOps) with a primary active flag. | |
| """ | |
| __tablename__ = "user_resumes" | |
| id = Column(String, primary_key=True, default=_uuid) | |
| user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) | |
| title = Column(String, nullable=False, default="Master Resume") | |
| file_name = Column(String, nullable=True) | |
| resume_text = Column(Text, nullable=False) | |
| is_primary = Column(Boolean, default=False) | |
| created_at = Column(DateTime, default=_now) | |
| updated_at = Column(DateTime, default=_now, onupdate=_now) | |
| user = relationship("User") | |
| class JobMatch(Base): | |
| """ | |
| Stores comparison and match analysis between a user's resume and a job description. | |
| """ | |
| __tablename__ = "job_matches" | |
| id = Column(String, primary_key=True, default=_uuid) | |
| user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) | |
| job_url = Column(String, nullable=True) | |
| company = Column(String, nullable=True) | |
| role_title = Column(String, nullable=True) | |
| job_description = Column(Text, nullable=False) | |
| match_score = Column(Float, nullable=False) # 0 to 100 | |
| verdict = Column(String, nullable=False) # "Strong Match", etc. | |
| analysis_json = Column(Text, nullable=False) # Full structured JSON string | |
| created_at = Column(DateTime, default=_now) | |
| user = relationship("User") | |
| class UserGmailToken(Base): | |
| """ | |
| Stores OAuth2 credentials for Gmail API access. | |
| Scope is strictly restricted to 'https://www.googleapis.com/auth/gmail.send' | |
| (send-only access, no read or mailbox modification permissions). | |
| """ | |
| __tablename__ = "user_gmail_tokens" | |
| id = Column(String, primary_key=True, default=_uuid) | |
| user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) | |
| email = Column(String, nullable=True) | |
| access_token = Column(Text, nullable=False) | |
| refresh_token = Column(Text, nullable=True) | |
| expires_at = Column(DateTime, nullable=True) | |
| scopes = Column(String, nullable=False, default="https://www.googleapis.com/auth/gmail.send") | |
| created_at = Column(DateTime, default=_now) | |
| updated_at = Column(DateTime, default=_now, onupdate=_now) | |
| user = relationship("User") | |