feat: define ORM models for JD, Candidate, MatchResult with Alembic migration
Browse files- backend/src/models/__init__.py +5 -0
- backend/src/models/candidate.py +53 -0
- backend/src/models/jd.py +28 -0
- backend/src/models/match_result.py +27 -0
backend/src/models/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .jd import JobDescription
|
| 2 |
+
from .candidate import Candidate
|
| 3 |
+
from .match_result import MatchResult
|
| 4 |
+
|
| 5 |
+
__all__ = ["JobDescription", "Candidate", "MatchResult"]
|
backend/src/models/candidate.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import uuid
|
| 2 |
+
from datetime import datetime
|
| 3 |
+
from sqlalchemy import String, Text, DateTime, JSON, Float, Integer, Boolean, func
|
| 4 |
+
from sqlalchemy.orm import Mapped, mapped_column
|
| 5 |
+
from sqlalchemy.dialects.postgresql import UUID
|
| 6 |
+
from ..database import Base
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Candidate(Base):
|
| 10 |
+
__tablename__ = "candidates"
|
| 11 |
+
|
| 12 |
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
| 13 |
+
external_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
| 14 |
+
|
| 15 |
+
name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
| 16 |
+
email: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
| 17 |
+
|
| 18 |
+
looking_for: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
| 19 |
+
currently_employed: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
| 20 |
+
notice_period: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
| 21 |
+
open_to_working_at: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
| 22 |
+
role_type: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
| 23 |
+
engineer_type: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
| 24 |
+
|
| 25 |
+
years_of_experience: Mapped[float | None] = mapped_column(Float, nullable=True)
|
| 26 |
+
programming_languages: Mapped[list] = mapped_column(JSON, default=list)
|
| 27 |
+
backend_frameworks: Mapped[list] = mapped_column(JSON, default=list)
|
| 28 |
+
frontend_technologies: Mapped[list] = mapped_column(JSON, default=list)
|
| 29 |
+
gen_ai_experience: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
| 30 |
+
recent_experience_type: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
| 31 |
+
education_status: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
| 32 |
+
degree: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
| 33 |
+
|
| 34 |
+
parsed_summary: Mapped[str | None] = mapped_column(Text, nullable=True)
|
| 35 |
+
parsed_skills: Mapped[str | None] = mapped_column(Text, nullable=True)
|
| 36 |
+
parsed_work_experience: Mapped[list] = mapped_column(JSON, default=list)
|
| 37 |
+
|
| 38 |
+
most_recent_company: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
| 39 |
+
most_recent_company_description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
| 40 |
+
most_recent_company_is_funded: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
| 41 |
+
most_recent_company_is_product_company: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
| 42 |
+
most_recent_company_total_funding: Mapped[float | None] = mapped_column(Float, nullable=True)
|
| 43 |
+
most_recent_company_funding_status: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
| 44 |
+
|
| 45 |
+
time_in_current_company: Mapped[float | None] = mapped_column(Float, nullable=True)
|
| 46 |
+
is_actively_or_passively_looking: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
| 47 |
+
|
| 48 |
+
growth_velocity: Mapped[float] = mapped_column(Float, default=0.5)
|
| 49 |
+
embedding_hash: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
| 50 |
+
qdrant_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
| 51 |
+
|
| 52 |
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
| 53 |
+
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
backend/src/models/jd.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import uuid
|
| 2 |
+
from datetime import datetime
|
| 3 |
+
from sqlalchemy import String, Text, DateTime, JSON, Float, Integer, func
|
| 4 |
+
from sqlalchemy.orm import Mapped, mapped_column
|
| 5 |
+
from sqlalchemy.dialects.postgresql import UUID, ARRAY
|
| 6 |
+
from ..database import Base
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class JobDescription(Base):
|
| 10 |
+
__tablename__ = "job_descriptions"
|
| 11 |
+
|
| 12 |
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
| 13 |
+
title: Mapped[str] = mapped_column(String(255))
|
| 14 |
+
raw_text: Mapped[str] = mapped_column(Text)
|
| 15 |
+
parsed_requirements: Mapped[dict] = mapped_column(JSON, default=dict)
|
| 16 |
+
required_skills: Mapped[list] = mapped_column(JSON, default=list)
|
| 17 |
+
min_yoe: Mapped[float | None] = mapped_column(Float, nullable=True)
|
| 18 |
+
max_yoe: Mapped[float | None] = mapped_column(Float, nullable=True)
|
| 19 |
+
role_type: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
| 20 |
+
engineer_type: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
| 21 |
+
location: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
| 22 |
+
remote_allowed: Mapped[bool | None] = mapped_column(nullable=True)
|
| 23 |
+
jd_quality: Mapped[dict] = mapped_column(JSON, default=dict)
|
| 24 |
+
embedding_text: Mapped[str | None] = mapped_column(Text, nullable=True)
|
| 25 |
+
qdrant_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
| 26 |
+
status: Mapped[str] = mapped_column(String(32), default="pending")
|
| 27 |
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
| 28 |
+
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
backend/src/models/match_result.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import uuid
|
| 2 |
+
from datetime import datetime
|
| 3 |
+
from sqlalchemy import String, DateTime, JSON, Float, ForeignKey, func
|
| 4 |
+
from sqlalchemy.orm import Mapped, mapped_column
|
| 5 |
+
from sqlalchemy.dialects.postgresql import UUID
|
| 6 |
+
from ..database import Base
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class MatchResult(Base):
|
| 10 |
+
__tablename__ = "match_results"
|
| 11 |
+
|
| 12 |
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
| 13 |
+
jd_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("job_descriptions.id", ondelete="CASCADE"), index=True)
|
| 14 |
+
candidate_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("candidates.id", ondelete="CASCADE"), index=True)
|
| 15 |
+
|
| 16 |
+
rank: Mapped[int | None] = mapped_column(nullable=True)
|
| 17 |
+
stage1_score: Mapped[float] = mapped_column(Float, default=0.0)
|
| 18 |
+
stage2_score: Mapped[float | None] = mapped_column(Float, nullable=True)
|
| 19 |
+
final_score: Mapped[float] = mapped_column(Float, default=0.0)
|
| 20 |
+
|
| 21 |
+
component_scores: Mapped[dict] = mapped_column(JSON, default=dict)
|
| 22 |
+
gaps: Mapped[list] = mapped_column(JSON, default=list)
|
| 23 |
+
explanation: Mapped[str | None] = mapped_column(String, nullable=True)
|
| 24 |
+
explanation_generated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
| 25 |
+
|
| 26 |
+
weights_used: Mapped[dict] = mapped_column(JSON, default=dict)
|
| 27 |
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|