mirror of
https://github.com/srbhr/Resume-Matcher.git
synced 2026-02-01 03:26:22 +00:00
Add get_improvement_by_tailored_resume() method to database.py that queries the improvements table by tailored_resume_id. This enables on-demand cover letter and outreach generation by finding the associated job description. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
202 lines
6.2 KiB
Python
202 lines
6.2 KiB
Python
"""TinyDB database layer for JSON storage."""
|
|
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from uuid import uuid4
|
|
|
|
from tinydb import Query, TinyDB
|
|
from tinydb.table import Table
|
|
|
|
from app.config import settings
|
|
|
|
|
|
class Database:
|
|
"""TinyDB wrapper for resume matcher data."""
|
|
|
|
def __init__(self, db_path: Path | None = None):
|
|
self.db_path = db_path or settings.db_path
|
|
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
self._db: TinyDB | None = None
|
|
|
|
@property
|
|
def db(self) -> TinyDB:
|
|
"""Lazy initialization of TinyDB instance."""
|
|
if self._db is None:
|
|
self._db = TinyDB(self.db_path)
|
|
return self._db
|
|
|
|
@property
|
|
def resumes(self) -> Table:
|
|
"""Resumes table."""
|
|
return self.db.table("resumes")
|
|
|
|
@property
|
|
def jobs(self) -> Table:
|
|
"""Job descriptions table."""
|
|
return self.db.table("jobs")
|
|
|
|
@property
|
|
def improvements(self) -> Table:
|
|
"""Improvement results table."""
|
|
return self.db.table("improvements")
|
|
|
|
def close(self) -> None:
|
|
"""Close database connection."""
|
|
if self._db is not None:
|
|
self._db.close()
|
|
self._db = None
|
|
|
|
# Resume operations
|
|
def create_resume(
|
|
self,
|
|
content: str,
|
|
content_type: str = "md",
|
|
filename: str | None = None,
|
|
is_master: bool = False,
|
|
parent_id: str | None = None,
|
|
processed_data: dict[str, Any] | None = None,
|
|
processing_status: str = "pending",
|
|
cover_letter: str | None = None,
|
|
outreach_message: str | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Create a new resume entry.
|
|
|
|
processing_status: "pending", "processing", "ready", "failed"
|
|
"""
|
|
resume_id = str(uuid4())
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
|
|
doc = {
|
|
"resume_id": resume_id,
|
|
"content": content,
|
|
"content_type": content_type,
|
|
"filename": filename,
|
|
"is_master": is_master,
|
|
"parent_id": parent_id,
|
|
"processed_data": processed_data,
|
|
"processing_status": processing_status,
|
|
"cover_letter": cover_letter,
|
|
"outreach_message": outreach_message,
|
|
"created_at": now,
|
|
"updated_at": now,
|
|
}
|
|
self.resumes.insert(doc)
|
|
return doc
|
|
|
|
def get_resume(self, resume_id: str) -> dict[str, Any] | None:
|
|
"""Get resume by ID."""
|
|
Resume = Query()
|
|
result = self.resumes.search(Resume.resume_id == resume_id)
|
|
return result[0] if result else None
|
|
|
|
def get_master_resume(self) -> dict[str, Any] | None:
|
|
"""Get the master resume if exists."""
|
|
Resume = Query()
|
|
result = self.resumes.search(Resume.is_master == True)
|
|
return result[0] if result else None
|
|
|
|
def update_resume(
|
|
self, resume_id: str, updates: dict[str, Any]
|
|
) -> dict[str, Any] | None:
|
|
"""Update resume by ID."""
|
|
Resume = Query()
|
|
updates["updated_at"] = datetime.now(timezone.utc).isoformat()
|
|
self.resumes.update(updates, Resume.resume_id == resume_id)
|
|
return self.get_resume(resume_id)
|
|
|
|
def delete_resume(self, resume_id: str) -> bool:
|
|
"""Delete resume by ID."""
|
|
Resume = Query()
|
|
removed = self.resumes.remove(Resume.resume_id == resume_id)
|
|
return len(removed) > 0
|
|
|
|
def list_resumes(self) -> list[dict[str, Any]]:
|
|
"""List all resumes."""
|
|
return list(self.resumes.all())
|
|
|
|
def set_master_resume(self, resume_id: str) -> bool:
|
|
"""Set a resume as the master, unsetting any existing master."""
|
|
Resume = Query()
|
|
# Unset current master
|
|
self.resumes.update({"is_master": False}, Resume.is_master == True)
|
|
# Set new master
|
|
updated = self.resumes.update(
|
|
{"is_master": True}, Resume.resume_id == resume_id
|
|
)
|
|
return len(updated) > 0
|
|
|
|
# Job operations
|
|
def create_job(
|
|
self, content: str, resume_id: str | None = None
|
|
) -> dict[str, Any]:
|
|
"""Create a new job description entry."""
|
|
job_id = str(uuid4())
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
|
|
doc = {
|
|
"job_id": job_id,
|
|
"content": content,
|
|
"resume_id": resume_id,
|
|
"created_at": now,
|
|
}
|
|
self.jobs.insert(doc)
|
|
return doc
|
|
|
|
def get_job(self, job_id: str) -> dict[str, Any] | None:
|
|
"""Get job by ID."""
|
|
Job = Query()
|
|
result = self.jobs.search(Job.job_id == job_id)
|
|
return result[0] if result else None
|
|
|
|
# Improvement operations
|
|
def create_improvement(
|
|
self,
|
|
original_resume_id: str,
|
|
tailored_resume_id: str,
|
|
job_id: str,
|
|
improvements: list[dict[str, Any]],
|
|
) -> dict[str, Any]:
|
|
"""Create an improvement result entry."""
|
|
request_id = str(uuid4())
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
|
|
doc = {
|
|
"request_id": request_id,
|
|
"original_resume_id": original_resume_id,
|
|
"tailored_resume_id": tailored_resume_id,
|
|
"job_id": job_id,
|
|
"improvements": improvements,
|
|
"created_at": now,
|
|
}
|
|
self.improvements.insert(doc)
|
|
return doc
|
|
|
|
def get_improvement_by_tailored_resume(
|
|
self, tailored_resume_id: str
|
|
) -> dict[str, Any] | None:
|
|
"""Get improvement record by tailored resume ID.
|
|
|
|
This is used to retrieve the job context for on-demand
|
|
cover letter and outreach message generation.
|
|
"""
|
|
Improvement = Query()
|
|
result = self.improvements.search(
|
|
Improvement.tailored_resume_id == tailored_resume_id
|
|
)
|
|
return result[0] if result else None
|
|
|
|
# Stats
|
|
def get_stats(self) -> dict[str, Any]:
|
|
"""Get database statistics."""
|
|
return {
|
|
"total_resumes": len(self.resumes),
|
|
"total_jobs": len(self.jobs),
|
|
"total_improvements": len(self.improvements),
|
|
"has_master_resume": self.get_master_resume() is not None,
|
|
}
|
|
|
|
|
|
# Global database instance
|
|
db = Database()
|