Upload 9 files
Browse files- LICENSE +21 -0
- README.md +1 -13
- app.py +41 -0
- ats_checker.py +16 -0
- embeddings.py +6 -0
- generator.py +18 -0
- parser.py +7 -0
- requirements.txt +14 -0
- vector_store.py +19 -0
LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2025 Ashish Singh Mehra
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
README.md
CHANGED
@@ -1,13 +1 @@
|
|
1 |
-
|
2 |
-
title: Resume And CoverLetter Generator
|
3 |
-
emoji: 💻
|
4 |
-
colorFrom: yellow
|
5 |
-
colorTo: indigo
|
6 |
-
sdk: streamlit
|
7 |
-
sdk_version: 1.42.0
|
8 |
-
app_file: app.py
|
9 |
-
pinned: false
|
10 |
-
license: mit
|
11 |
-
---
|
12 |
-
|
13 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
+
# Resume-Cover-Letter-Generator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from embeddings import generate_embeddings
|
3 |
+
from generator import generate_resume, generate_cover_letter
|
4 |
+
from parser import extract_text_from_pdf
|
5 |
+
from vector_store import store_resume_embedding, search_similar_resumes
|
6 |
+
|
7 |
+
# Streamlit App
|
8 |
+
st.title("Resume and Cover Letter Generator")
|
9 |
+
|
10 |
+
# Upload Resume File
|
11 |
+
uploaded_file = st.file_uploader("Upload your resume (PDF)", type="pdf")
|
12 |
+
if uploaded_file:
|
13 |
+
# Extract text from the uploaded PDF
|
14 |
+
text = extract_text_from_pdf(uploaded_file.read())
|
15 |
+
# Generate embeddings for the uploaded resume
|
16 |
+
vector = generate_embeddings(text)
|
17 |
+
# Store the resume embedding for future searches
|
18 |
+
store_resume_embedding(uploaded_file.name, vector, text)
|
19 |
+
st.success("Resume uploaded successfully!")
|
20 |
+
|
21 |
+
# Form to generate documents
|
22 |
+
with st.form("generate_documents"):
|
23 |
+
name = st.text_input("Your Name")
|
24 |
+
job_description = st.text_area("Job Description")
|
25 |
+
submit_button = st.form_submit_button("Generate Resume and Cover Letter")
|
26 |
+
|
27 |
+
if submit_button:
|
28 |
+
if not name or not job_description:
|
29 |
+
st.error("Please fill in both fields.")
|
30 |
+
else:
|
31 |
+
# Search for similar resumes based on job description
|
32 |
+
similar_resumes = search_similar_resumes(job_description)
|
33 |
+
# Generate resume and cover letter
|
34 |
+
resume = generate_resume(name, job_description, similar_resumes)
|
35 |
+
cover_letter = generate_cover_letter(name, job_description, similar_resumes)
|
36 |
+
|
37 |
+
# Display the generated resume and cover letter
|
38 |
+
st.subheader("Generated Resume")
|
39 |
+
st.write(resume)
|
40 |
+
st.subheader("Generated Cover Letter")
|
41 |
+
st.write(cover_letter)
|
ats_checker.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
from collections import Counter
|
3 |
+
from sklearn.feature_extraction.text import TfidfVectorizer
|
4 |
+
|
5 |
+
def extract_keywords(text, top_n=10):
|
6 |
+
words = re.findall(r'\b\w+\b', text.lower())
|
7 |
+
common_words = set(["the", "and", "is", "to", "in", "for", "on", "with"]) # Stopwords
|
8 |
+
filtered_words = [word for word in words if word not in common_words]
|
9 |
+
return [word for word, _ in Counter(filtered_words).most_common(top_n)]
|
10 |
+
|
11 |
+
def compute_ats_score(resume_text, job_desc):
|
12 |
+
resume_keywords = extract_keywords(resume_text)
|
13 |
+
job_keywords = extract_keywords(job_desc)
|
14 |
+
|
15 |
+
match_score = len(set(resume_keywords) & set(job_keywords)) / len(set(job_keywords))
|
16 |
+
return f"ATS Score: {match_score:.2f}"
|
embeddings.py
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sentence_transformers import SentenceTransformer
|
2 |
+
|
3 |
+
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
|
4 |
+
|
5 |
+
def generate_embeddings(text):
|
6 |
+
return model.encode([text])[0] # Convert text to vector
|
generator.py
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import google.generativeai as genai
|
2 |
+
|
3 |
+
# Configure Gemini API
|
4 |
+
genai.configure(api_key="AIzaSyCQkmt7LZKp_BHTd05DtIkJEFQD3V1Usqw")
|
5 |
+
|
6 |
+
# Initialize Gemini model
|
7 |
+
llm = genai.GenerativeModel("gemini-pro")
|
8 |
+
|
9 |
+
def generate_resume(name, job_desc, similar_resumes):
|
10 |
+
prompt = f"Generate a professional resume for {name} applying for:\n{job_desc}\n\nReference these resumes:\n{similar_resumes}"
|
11 |
+
response = llm.generate_content(prompt)
|
12 |
+
return response.text
|
13 |
+
|
14 |
+
def generate_cover_letter(name, job_desc, similar_resumes):
|
15 |
+
prompt = f"Write a tailored cover letter for {name} based on:\n{job_desc}\n\nReference resumes:\n{similar_resumes}"
|
16 |
+
response = llm.generate_content(prompt)
|
17 |
+
return response.text
|
18 |
+
|
parser.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import fitz
|
2 |
+
|
3 |
+
def extract_text_from_pdf(file_data):
|
4 |
+
pdf_doc = fitz.open(stream=file_data, filetype='pdf')
|
5 |
+
text = " ".join([page.get_text("text") for page in pdf_doc])
|
6 |
+
return text
|
7 |
+
|
requirements.txt
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
langchain
|
2 |
+
transformers
|
3 |
+
faiss-cpu
|
4 |
+
pinecone-client
|
5 |
+
fastapi
|
6 |
+
uvicorn
|
7 |
+
pydantic
|
8 |
+
python-multipart
|
9 |
+
pdfplumber
|
10 |
+
python-docx
|
11 |
+
pymupdf
|
12 |
+
sentence-transformers
|
13 |
+
langchain_community
|
14 |
+
google-generativeai
|
vector_store.py
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import faiss
|
2 |
+
import numpy as np
|
3 |
+
from embeddings import generate_embeddings
|
4 |
+
|
5 |
+
|
6 |
+
|
7 |
+
dimension = 384
|
8 |
+
index = faiss.IndexFlatL2(dimension)
|
9 |
+
resume_db = {}
|
10 |
+
|
11 |
+
def store_resume_embedding(filename, vector, text):
|
12 |
+
global resume_db
|
13 |
+
index.add(np.array([vector]))
|
14 |
+
resume_db[len(resume_db)] = {"filename": filename, "text": text}
|
15 |
+
|
16 |
+
def search_similar_resumes(job_description):
|
17 |
+
job_vector = generate_embeddings(job_description)
|
18 |
+
D, I = index.search(np.array([job_vector]), k=3)
|
19 |
+
return [resume_db[i]["text"] for i in I[0] if i in resume_db]
|