Spaces:
Configuration error
Configuration error
Upload 4 files
Browse files- .gitignore +38 -0
- README.md +102 -19
- app.py +78 -0
- requirements.txt +5 -3
.gitignore
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# Virtual Environment
|
| 7 |
+
venv/
|
| 8 |
+
env/
|
| 9 |
+
ENV/
|
| 10 |
+
|
| 11 |
+
# IDE specific files
|
| 12 |
+
.idea/
|
| 13 |
+
.vscode/
|
| 14 |
+
*.swp
|
| 15 |
+
*.swo
|
| 16 |
+
|
| 17 |
+
# OS generated files
|
| 18 |
+
.DS_Store
|
| 19 |
+
.DS_Store?
|
| 20 |
+
._*
|
| 21 |
+
.Spotlight-V100
|
| 22 |
+
.Trashes
|
| 23 |
+
ehthumbs.db
|
| 24 |
+
Thumbs.db
|
| 25 |
+
|
| 26 |
+
# Logs and databases
|
| 27 |
+
*.log
|
| 28 |
+
*.sqlite
|
| 29 |
+
*.db
|
| 30 |
+
|
| 31 |
+
# Environment variables
|
| 32 |
+
.env
|
| 33 |
+
|
| 34 |
+
# Downloaded models
|
| 35 |
+
*.bin
|
| 36 |
+
*.h5
|
| 37 |
+
*.pth
|
| 38 |
+
*.pt
|
README.md
CHANGED
|
@@ -1,19 +1,102 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Resume Parser & Summarizer (Streamlit + Hugging Face)
|
| 2 |
+
|
| 3 |
+
A Streamlit web app that parses resumes (PDF/DOCX) and generates an AI summary using the Hugging Face Inference API. It extracts contact details and detected skills and produces a concise summary.
|
| 4 |
+
|
| 5 |
+
## Features
|
| 6 |
+
|
| 7 |
+
- Upload resumes in PDF or DOCX
|
| 8 |
+
- Extract contact information (email, phone)
|
| 9 |
+
- Detect common technical skills via keyword scan
|
| 10 |
+
- Summarize resume content using a Hugging Face summarization model
|
| 11 |
+
- Streamlit UI with sidebar for HF model and API token
|
| 12 |
+
|
| 13 |
+
## Local Installation
|
| 14 |
+
|
| 15 |
+
1. Clone the repository:
|
| 16 |
+
```bash
|
| 17 |
+
git clone <repository-url>
|
| 18 |
+
cd windsurf-project-2
|
| 19 |
+
```
|
| 20 |
+
|
| 21 |
+
2. Create a virtual environment (recommended):
|
| 22 |
+
```bash
|
| 23 |
+
python -m venv venv
|
| 24 |
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
3. Install the required packages:
|
| 28 |
+
```bash
|
| 29 |
+
pip install -r requirements.txt
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
4. (Optional) Set your Hugging Face token for higher rate limits:
|
| 33 |
+
```bash
|
| 34 |
+
# Windows (PowerShell)
|
| 35 |
+
setx HF_TOKEN "hf_XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
| 36 |
+
# macOS/Linux
|
| 37 |
+
export HF_TOKEN="hf_XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
## Usage
|
| 41 |
+
|
| 42 |
+
1. Run the Streamlit application:
|
| 43 |
+
```bash
|
| 44 |
+
streamlit run app.py
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
2. Open your web browser and navigate to `http://localhost:8501`
|
| 48 |
+
|
| 49 |
+
3. Upload a resume file (PDF or DOCX) using the file uploader
|
| 50 |
+
|
| 51 |
+
4. In the sidebar, set the summarization model (default: `facebook/bart-large-cnn`) and provide your HF API token if not set as a secret or environment variable.
|
| 52 |
+
|
| 53 |
+
5. View extracted info and the AI-generated summary.
|
| 54 |
+
|
| 55 |
+
## Hugging Face Inference API
|
| 56 |
+
|
| 57 |
+
This app uses the Hugging Face Inference API via `huggingface-hub` to run text summarization. You can choose a model such as:
|
| 58 |
+
|
| 59 |
+
- `facebook/bart-large-cnn` (default)
|
| 60 |
+
- `sshleifer/distilbart-cnn-12-6` (faster, smaller)
|
| 61 |
+
|
| 62 |
+
You may supply an API token via any of the following (priority order):
|
| 63 |
+
|
| 64 |
+
1. `st.secrets["HF_TOKEN"]`
|
| 65 |
+
2. Environment variable `HF_TOKEN`
|
| 66 |
+
3. Sidebar password input
|
| 67 |
+
|
| 68 |
+
Create a token at: https://huggingface.co/settings/tokens
|
| 69 |
+
|
| 70 |
+
## Deploy to Hugging Face Spaces
|
| 71 |
+
|
| 72 |
+
1. Create a new Space at https://huggingface.co/spaces and choose "Streamlit" as the SDK.
|
| 73 |
+
2. Push your repository files (ensure `app.py` and `requirements.txt` are present).
|
| 74 |
+
3. In the Space, go to Settings → Secrets and add:
|
| 75 |
+
- Key: `HF_TOKEN`
|
| 76 |
+
- Value: your Hugging Face token
|
| 77 |
+
4. The app will build automatically. Once running, you can change models from the sidebar.
|
| 78 |
+
|
| 79 |
+
Notes:
|
| 80 |
+
- Keep dependencies minimal for faster builds (see `requirements.txt`).
|
| 81 |
+
- Uploaded files are kept in the Space runtime; do not rely on persistence across restarts.
|
| 82 |
+
|
| 83 |
+
## Dependencies
|
| 84 |
+
|
| 85 |
+
- Python 3.9+
|
| 86 |
+
- Streamlit
|
| 87 |
+
- PyPDF2
|
| 88 |
+
- python-docx
|
| 89 |
+
- huggingface-hub
|
| 90 |
+
|
| 91 |
+
## License
|
| 92 |
+
|
| 93 |
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
| 94 |
+
|
| 95 |
+
## Notes & Next Steps
|
| 96 |
+
|
| 97 |
+
This is a lightweight implementation designed to run well on Hugging Face Spaces. Ideas to extend:
|
| 98 |
+
|
| 99 |
+
- Use a custom summarization prompt or instruction-tuned models.
|
| 100 |
+
- Add job description matching and scoring.
|
| 101 |
+
- Export parsed data to CSV/JSON.
|
| 102 |
+
- Improve skill/entity extraction with advanced NLP if needed.
|
app.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import os
|
| 3 |
+
from utils.resume_parser import parse_resume
|
| 4 |
+
from utils.ai_summarizer import summarize_resume
|
| 5 |
+
|
| 6 |
+
# Set page config
|
| 7 |
+
st.set_page_config(
|
| 8 |
+
page_title="Resume Parser & Summarizer",
|
| 9 |
+
page_icon="📄",
|
| 10 |
+
layout="wide"
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
def main():
|
| 14 |
+
st.title("📄 Resume Parser & Summarizer")
|
| 15 |
+
st.write("Upload a resume (PDF or DOCX) to extract and summarize key information.")
|
| 16 |
+
|
| 17 |
+
# Create necessary directories
|
| 18 |
+
os.makedirs("data", exist_ok=True)
|
| 19 |
+
|
| 20 |
+
# Sidebar: Hugging Face Settings
|
| 21 |
+
st.sidebar.header("Hugging Face Settings")
|
| 22 |
+
default_model = "facebook/bart-large-cnn"
|
| 23 |
+
hf_model = st.sidebar.text_input(
|
| 24 |
+
"Summarization model (repo id)", value=default_model,
|
| 25 |
+
help="e.g., facebook/bart-large-cnn, sshleifer/distilbart-cnn-12-6"
|
| 26 |
+
)
|
| 27 |
+
# Prefer token from secrets, then environment, then user input
|
| 28 |
+
secrets_token = st.secrets.get("HF_TOKEN") if hasattr(st, "secrets") else None
|
| 29 |
+
env_token = os.environ.get("HF_TOKEN")
|
| 30 |
+
token_placeholder = "Using st.secrets['HF_TOKEN']" if secrets_token else ("Using env HF_TOKEN" if env_token else "Paste your token (optional)")
|
| 31 |
+
input_token = st.sidebar.text_input("Hugging Face API Token", value="", type="password", help=token_placeholder)
|
| 32 |
+
hf_token = input_token or secrets_token or env_token
|
| 33 |
+
|
| 34 |
+
# File uploader
|
| 35 |
+
uploaded_file = st.file_uploader("Choose a resume file", type=["pdf", "docx"])
|
| 36 |
+
|
| 37 |
+
if uploaded_file is not None:
|
| 38 |
+
# Save the uploaded file
|
| 39 |
+
file_path = os.path.join("data", uploaded_file.name)
|
| 40 |
+
with open(file_path, "wb") as f:
|
| 41 |
+
f.write(uploaded_file.getbuffer())
|
| 42 |
+
|
| 43 |
+
st.success(f"Successfully uploaded {uploaded_file.name}")
|
| 44 |
+
|
| 45 |
+
# Parse the resume
|
| 46 |
+
with st.spinner("Parsing resume..."):
|
| 47 |
+
resume_data = parse_resume(file_path)
|
| 48 |
+
|
| 49 |
+
if resume_data:
|
| 50 |
+
st.subheader("Extracted Information")
|
| 51 |
+
|
| 52 |
+
# Display basic info
|
| 53 |
+
col1, col2 = st.columns(2)
|
| 54 |
+
with col1:
|
| 55 |
+
if resume_data.get('name'):
|
| 56 |
+
st.markdown(f"**Name:** {resume_data['name']}")
|
| 57 |
+
if resume_data.get('email'):
|
| 58 |
+
st.markdown(f"**Email:** {resume_data['email']}")
|
| 59 |
+
if resume_data.get('phone'):
|
| 60 |
+
st.markdown(f"**Phone:** {resume_data['phone']}")
|
| 61 |
+
|
| 62 |
+
with col2:
|
| 63 |
+
if resume_data.get('skills'):
|
| 64 |
+
st.markdown("**Skills:**")
|
| 65 |
+
st.write(", ".join(resume_data['skills']))
|
| 66 |
+
|
| 67 |
+
# Generate and display summary
|
| 68 |
+
with st.spinner("Generating summary..."):
|
| 69 |
+
summary = summarize_resume(resume_data, hf_model=hf_model, hf_token=hf_token)
|
| 70 |
+
st.subheader("AI-Powered Summary")
|
| 71 |
+
st.write(summary)
|
| 72 |
+
|
| 73 |
+
# Display full text (collapsible)
|
| 74 |
+
with st.expander("View Full Extracted Text"):
|
| 75 |
+
st.text(resume_data.get('raw_text', 'No text extracted'))
|
| 76 |
+
|
| 77 |
+
if __name__ == "__main__":
|
| 78 |
+
main()
|
requirements.txt
CHANGED
|
@@ -1,3 +1,5 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit==1.32.0
|
| 2 |
+
python-docx==1.1.0
|
| 3 |
+
PyPDF2==3.0.1
|
| 4 |
+
huggingface-hub==0.24.5
|
| 5 |
+
requests>=2.31.0
|