Spaces:
Sleeping
Sleeping
| """ORM models: users, interview sessions, and graded answers.""" | |
| from __future__ import annotations | |
| import datetime as dt | |
| from typing import Optional | |
| from sqlalchemy import ( | |
| DateTime, | |
| Float, | |
| ForeignKey, | |
| Integer, | |
| String, | |
| Text, | |
| ) | |
| from sqlalchemy.orm import Mapped, mapped_column, relationship | |
| from app.database import Base | |
| def _utcnow() -> dt.datetime: | |
| return dt.datetime.now(dt.timezone.utc) | |
| class User(Base): | |
| __tablename__ = "users" | |
| id: Mapped[int] = mapped_column(Integer, primary_key=True) | |
| email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False) | |
| password_hash: Mapped[str] = mapped_column(String(255), nullable=False) | |
| created_at: Mapped[dt.datetime] = mapped_column(DateTime, default=_utcnow) | |
| sessions: Mapped[list["InterviewSession"]] = relationship( | |
| back_populates="user", cascade="all, delete-orphan", order_by="InterviewSession.created_at.desc()" | |
| ) | |
| class InterviewSession(Base): | |
| __tablename__ = "interview_sessions" | |
| id: Mapped[int] = mapped_column(Integer, primary_key=True) | |
| user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), index=True, nullable=False) | |
| role_title: Mapped[str] = mapped_column(String(255), default="Interview practice") | |
| job_description: Mapped[str] = mapped_column(Text, nullable=False) | |
| created_at: Mapped[dt.datetime] = mapped_column(DateTime, default=_utcnow, index=True) | |
| user: Mapped["User"] = relationship(back_populates="sessions") | |
| answers: Mapped[list["Answer"]] = relationship( | |
| back_populates="session", cascade="all, delete-orphan", order_by="Answer.position" | |
| ) | |
| def average_percent(self) -> int | None: | |
| scored = [a.percent for a in self.answers if a.percent is not None] | |
| if not scored: | |
| return None | |
| return int(round(sum(scored) / len(scored))) | |
| def answered_count(self) -> int: | |
| return sum(1 for a in self.answers if a.percent is not None) | |
| class Answer(Base): | |
| __tablename__ = "answers" | |
| id: Mapped[int] = mapped_column(Integer, primary_key=True) | |
| session_id: Mapped[int] = mapped_column( | |
| ForeignKey("interview_sessions.id"), index=True, nullable=False | |
| ) | |
| position: Mapped[int] = mapped_column(Integer, default=0) | |
| skill: Mapped[str] = mapped_column(String(120), default="general") | |
| question: Mapped[str] = mapped_column(Text, nullable=False) | |
| answer_text: Mapped[str] = mapped_column(Text, default="") | |
| # Scoring results (null until the answer is submitted). | |
| overall: Mapped[Optional[float]] = mapped_column(Float, nullable=True) | |
| percent: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) | |
| band: Mapped[Optional[str]] = mapped_column(String(60), nullable=True) | |
| summary: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| # JSON-encoded per-axis detail and strengths/improvements lists. | |
| axis_json: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| strengths_json: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| improvements_json: Mapped[Optional[str]] = mapped_column(Text, nullable=True) | |
| scored_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime, nullable=True) | |
| session: Mapped["InterviewSession"] = relationship(back_populates="answers") | |