Spaces:
Sleeping
Sleeping
Upload 9 files
Browse files- backend/__init__.py +0 -0
- backend/agents/matcher.py +18 -0
- backend/agents/normalizer.py +13 -0
- backend/agents/rapidapi_freelancer.py +28 -0
- backend/agents/rapidapi_linkedin.py +28 -0
- backend/agents/rapidapi_upwork.py +28 -0
- backend/agents/remoteok_agent.py +8 -0
- backend/agents/resume_gen.py +0 -0
- backend/agents/resume_parser.py +10 -0
backend/__init__.py
ADDED
|
File without changes
|
backend/agents/matcher.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from sklearn.metrics.pairwise import cosine_similarity
|
| 3 |
+
from groq import Groq
|
| 4 |
+
|
| 5 |
+
client = Groq(api_key=os.getenv("GROQ_API_KEY"))
|
| 6 |
+
|
| 7 |
+
def get_embedding(text: str):
|
| 8 |
+
response = client.embeddings.create(
|
| 9 |
+
model="nomic-embed-text", # Groq embedding model
|
| 10 |
+
input=text
|
| 11 |
+
)
|
| 12 |
+
return np.array(response.data[0].embedding)
|
| 13 |
+
|
| 14 |
+
def compute_match(resume_text: str, job_desc: str) -> float:
|
| 15 |
+
resume_emb = get_embedding(resume_text).reshape(1, -1)
|
| 16 |
+
job_emb = get_embedding(job_desc).reshape(1, -1)
|
| 17 |
+
score = cosine_similarity(resume_emb, job_emb)[0][0]
|
| 18 |
+
return round(float(score), 3)
|
backend/agents/normalizer.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List, Dict
|
| 2 |
+
|
| 3 |
+
def normalize_job(raw: Dict, source: str) -> Dict:
|
| 4 |
+
return {
|
| 5 |
+
"id": str(raw.get("id", "")),
|
| 6 |
+
"source": source,
|
| 7 |
+
"title": raw.get("title") or raw.get("position", ""),
|
| 8 |
+
"company": raw.get("company", ""),
|
| 9 |
+
"description": raw.get("description", ""),
|
| 10 |
+
"skills": raw.get("skills") or raw.get("tags", []),
|
| 11 |
+
"url": raw.get("url", ""),
|
| 12 |
+
"date": raw.get("date", ""),
|
| 13 |
+
}
|
backend/agents/rapidapi_freelancer.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import requests
|
| 3 |
+
from .normalizer import normalize_job
|
| 4 |
+
|
| 5 |
+
RAPIDAPI_KEY = os.getenv("RAPIDAPI_KEY")
|
| 6 |
+
|
| 7 |
+
def fetch_freelancer_jobs(query="AI", limit=5):
|
| 8 |
+
url = "https://freelancer-com.p.rapidapi.com/projects/search" # Example endpoint
|
| 9 |
+
headers = {
|
| 10 |
+
"X-RapidAPI-Key": RAPIDAPI_KEY,
|
| 11 |
+
"X-RapidAPI-Host": "freelancer-com.p.rapidapi.com"
|
| 12 |
+
}
|
| 13 |
+
params = {"q": query, "limit": limit}
|
| 14 |
+
response = requests.get(url, headers=headers, params=params)
|
| 15 |
+
jobs = response.json().get("projects", [])
|
| 16 |
+
|
| 17 |
+
normalized = []
|
| 18 |
+
for job in jobs[:limit]:
|
| 19 |
+
normalized.append(normalize_job({
|
| 20 |
+
"id": job.get("id"),
|
| 21 |
+
"title": job.get("title"),
|
| 22 |
+
"company": "Freelancer Client",
|
| 23 |
+
"description": job.get("description"),
|
| 24 |
+
"skills": [s["name"] for s in job.get("jobs", [])],
|
| 25 |
+
"url": f"https://www.freelancer.com/projects/{job.get('seo_url')}",
|
| 26 |
+
"date": job.get("submitdate")
|
| 27 |
+
}, "Freelancer"))
|
| 28 |
+
return normalized
|
backend/agents/rapidapi_linkedin.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import requests
|
| 3 |
+
from .normalizer import normalize_job
|
| 4 |
+
|
| 5 |
+
RAPIDAPI_KEY = os.getenv("RAPIDAPI_KEY")
|
| 6 |
+
|
| 7 |
+
def fetch_linkedin_jobs(query="software engineer", limit=5):
|
| 8 |
+
url = "https://linkedin-jobs-search.p.rapidapi.com/" # Example endpoint
|
| 9 |
+
headers = {
|
| 10 |
+
"X-RapidAPI-Key": RAPIDAPI_KEY,
|
| 11 |
+
"X-RapidAPI-Host": "linkedin-jobs-search.p.rapidapi.com"
|
| 12 |
+
}
|
| 13 |
+
payload = {"search_terms": query, "location": "remote", "page": "1"}
|
| 14 |
+
response = requests.post(url, headers=headers, json=payload)
|
| 15 |
+
jobs = response.json()
|
| 16 |
+
|
| 17 |
+
normalized = []
|
| 18 |
+
for job in jobs[:limit]:
|
| 19 |
+
normalized.append(normalize_job({
|
| 20 |
+
"id": job.get("job_id"),
|
| 21 |
+
"title": job.get("job_title"),
|
| 22 |
+
"company": job.get("company_name"),
|
| 23 |
+
"description": job.get("job_description"),
|
| 24 |
+
"skills": job.get("job_skills", []),
|
| 25 |
+
"url": job.get("linkedin_url"),
|
| 26 |
+
"date": job.get("posted_date")
|
| 27 |
+
}, "LinkedIn"))
|
| 28 |
+
return normalized
|
backend/agents/rapidapi_upwork.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import requests
|
| 3 |
+
from .normalizer import normalize_job
|
| 4 |
+
|
| 5 |
+
RAPIDAPI_KEY = os.getenv("RAPIDAPI_KEY")
|
| 6 |
+
|
| 7 |
+
def fetch_upwork_jobs(query="python developer", limit=5):
|
| 8 |
+
url = "https://upwork-api.p.rapidapi.com/search/jobs" # Example endpoint
|
| 9 |
+
headers = {
|
| 10 |
+
"X-RapidAPI-Key": RAPIDAPI_KEY,
|
| 11 |
+
"X-RapidAPI-Host": "upwork-api.p.rapidapi.com"
|
| 12 |
+
}
|
| 13 |
+
params = {"q": query, "page": "1"}
|
| 14 |
+
response = requests.get(url, headers=headers, params=params)
|
| 15 |
+
jobs = response.json().get("jobs", [])
|
| 16 |
+
|
| 17 |
+
normalized = []
|
| 18 |
+
for job in jobs[:limit]:
|
| 19 |
+
normalized.append(normalize_job({
|
| 20 |
+
"id": job.get("id"),
|
| 21 |
+
"title": job.get("title"),
|
| 22 |
+
"company": "Upwork Client",
|
| 23 |
+
"description": job.get("description"),
|
| 24 |
+
"skills": job.get("skills", []),
|
| 25 |
+
"url": f"https://www.upwork.com/jobs/{job.get('id')}",
|
| 26 |
+
"date": job.get("date_created")
|
| 27 |
+
}, "Upwork"))
|
| 28 |
+
return normalized
|
backend/agents/remoteok_agent.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
from .normalizer import normalize_job
|
| 3 |
+
|
| 4 |
+
def fetch_remoteok_jobs(limit=10):
|
| 5 |
+
url = "https://remoteok.com/api"
|
| 6 |
+
res = requests.get(url, headers={"User-Agent": "MATCHHIVE"})
|
| 7 |
+
jobs = res.json()[1:] # skip metadata
|
| 8 |
+
return [normalize_job(job, "RemoteOK") for job in jobs[:limit]]
|
backend/agents/resume_gen.py
ADDED
|
File without changes
|
backend/agents/resume_parser.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pdfplumber, docx, os
|
| 2 |
+
|
| 3 |
+
def extract_text(file_path: str) -> str:
|
| 4 |
+
if file_path.endswith(".pdf"):
|
| 5 |
+
with pdfplumber.open(file_path) as pdf:
|
| 6 |
+
return " ".join(page.extract_text() or "" for page in pdf.pages)
|
| 7 |
+
elif file_path.endswith(".docx"):
|
| 8 |
+
doc = docx.Document(file_path)
|
| 9 |
+
return " ".join(p.text for p in doc.paragraphs)
|
| 10 |
+
return ""
|