Resume_analysis_RAG / CHROMA_DB /collections.py
DEEPAN-C's picture
Upload folder using huggingface_hub
e4f4981 verified
Raw
History Blame Contribute Delete
6.83 kB
# collections.py
import os
from typing import Dict, Any, List
from sentence_transformers import SentenceTransformer
from langchain_community.document_loaders import PyPDFLoader
import chromadb
class ChromaDBManager:
def __init__(self, db_path: str = "resume_chroma_db", collection_name: str = "resumes", sections_collection_name: str = "resume_sections", in_memory: bool = False):
if in_memory:
self.client = chromadb.Client()
else:
if not os.path.exists(db_path):
os.makedirs(db_path)
self.client = chromadb.PersistentClient(path=db_path)
# Full-resume collection
self.collection = self.client.get_or_create_collection(
name=collection_name,
embedding_function=None
)
# Section-level collection
self.sections_collection = self.client.get_or_create_collection(
name=sections_collection_name,
embedding_function=None
)
if not in_memory:
print(f"ChromaDB initialized at {db_path}")
def add_record(self, db_record: Dict[str, Any]):
"""Add both full resume and its sections into ChromaDB"""
resume_id = db_record["id"]
filename = db_record["metadata"]["filename"]
# --- Store FULL RESUME embedding ---
try:
self.collection.delete(where={"filename": filename})
except Exception:
pass
self.collection.add(
ids=[resume_id],
documents=[db_record["metadata"]["full_text"]],
embeddings=[db_record["embedding"]],
metadatas=[{"resume_id": resume_id, "filename": filename}]
)
# --- Store SECTION embeddings ---
try:
self.sections_collection.delete(where={"filename": filename})
except Exception:
pass
sections = db_record["metadata"]["sections"]
section_embeddings = db_record["metadata"]["section_embeddings"]
ids, docs, embs, metas = [], [], [], []
for name, text in sections.items():
if not text.strip():
continue
ids.append(f"{resume_id}_{name}")
docs.append(text)
embs.append(section_embeddings[name])
metas.append({
"resume_id": resume_id,
"section_name": name,
"filename": filename
})
if ids:
self.sections_collection.add(
ids=ids,
documents=docs,
embeddings=embs,
metadatas=metas
)
print(f"✅ Added resume {resume_id} with {len(ids)} sections")
def query(self, query_text: str, query_embedding: List[float], top_k: int = 5, min_similarity: float = 0.3):
"""Query against section-level embeddings"""
results = self.sections_collection.query(
query_embeddings=[query_embedding],
n_results=top_k,
include=['documents', 'metadatas', 'distances']
)
if not results or not results.get("documents") or not results.get("distances"):
return {"matches": [], "resume_scores": {}}
matches, resume_scores = [], {}
for doc, meta, dist in zip(results["documents"][0], results["metadatas"][0], results["distances"][0]):
if doc is None or meta is None or dist is None:
continue
match_percentage = round((1 - dist) * 100, 2)
if match_percentage >= (min_similarity * 100):
match = {
"resume_id": meta["resume_id"],
"filename": meta["filename"],
"section_name": meta["section_name"],
"match_percentage": match_percentage,
"text": doc,
}
matches.append(match)
rid = meta["resume_id"]
resume_scores.setdefault(rid, []).append(match_percentage)
# Average per resume
resume_scores = {rid: round(sum(scores)/len(scores), 2) for rid, scores in resume_scores.items()}
return {
"matches": matches,
"resume_scores": resume_scores,
"query": query_text
}
def get_resume_embedding(self, resume_id: str) -> List[float]:
"""Retrieve the full resume text embedding given a resume ID."""
results = self.collection.get(
ids=[resume_id],
include=['embeddings']
)
if not results or not results.get("embeddings") or not results["embeddings"][0]:
return None
return results["embeddings"][0]
def load_job_description_pdf(file_path: str) -> str:
"""Extracts text from a job description PDF"""
loader = PyPDFLoader(file_path)
pages = loader.load()
return "\n".join(page.page_content for page in pages)
# Example standalone usage
if __name__ == "__main__":
embedding_model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
chroma_manager = ChromaDBManager()
resume_folder = "/Users/deepandee/Desktop/RAG/DATA_resume"
from TEXT_EMBEDDING_MODEL.textEmbedding_model import process_extracted_data
for file in os.listdir(resume_folder):
if file.endswith(".pdf"):
file_path = os.path.join(resume_folder, file)
# Extract text safely
import pdfplumber
content = ""
try:
with pdfplumber.open(file_path) as pdf:
for page in pdf.pages:
text = page.extract_text()
if text:
content += text + "\n"
except Exception as e:
print(f"⚠️ Error reading {file}: {e}")
continue
extracted = process_extracted_data({
"success": True,
"filename": file,
"file_path": file_path,
"sections": {"content": content}
}, embedding_model)
if extracted:
chroma_manager.add_record(extracted)
# Query job description
job_desc_folder = "/Users/deepandee/Desktop/RAG/JOB_DESCRIPTIONS"
for job_file in os.listdir(job_desc_folder):
if job_file.endswith(".pdf"):
job_path = os.path.join(job_desc_folder, job_file)
job_text = load_job_description_pdf(job_path)
job_emb = embedding_model.encode(job_text).tolist()
matches = chroma_manager.query(job_text, job_emb, top_k=5, min_similarity=0.1)
print(f"\nMatches for job: {job_file}")
for match in matches["matches"]:
print(f"- {match['resume_id']} ({match['match_percentage']}%) → {match['text'][:100]}...")