Spaces:
Sleeping
Sleeping
File size: 12,170 Bytes
a1a2096 d068091 5fdef64 d068091 a1a2096 242cb8f a1a2096 242cb8f d068091 5fdef64 a1a2096 5fdef64 a1a2096 5fdef64 a1a2096 242cb8f a1a2096 5fdef64 a1a2096 242cb8f a1a2096 242cb8f a1a2096 242cb8f a1a2096 242cb8f d068091 242cb8f a1a2096 d068091 a1a2096 242cb8f a1a2096 d068091 a1a2096 d068091 a1a2096 d068091 a1a2096 242cb8f a1a2096 242cb8f a1a2096 242cb8f d068091 a1a2096 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
"""
AI Resume Studio โ Huggingย Faceย Space
Author: Oluwafemiย Idiakhoa
Last update: 2025โ06โ27
Features
โโโโโโโโ
1. Generate resume โ Word & PDF downloads
2. Score resume against job description
3. AI Section CoโPilot (rewrite, quantify, bulletizeโฆ)
4. Coverโletter generator
5. Jobโdescription scraper by URL
6. Multilingual export via DeepL (free & pro keys supported)
"""
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Imports & setup
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
import os, tempfile
import requests
import gradio as gr
import google.generativeai as genai
from dotenv import load_dotenv
from bs4 import BeautifulSoup
from docx import Document # pythonโdocx
from reportlab.lib.pagesizes import LETTER
from reportlab.pdfgen import canvas
import deepl # DeepL translation
load_dotenv()
# Gemini model (make sure key has access to this version)
genai.configure(api_key=os.getenv("API_KEY"))
GEMINI_MODEL = genai.GenerativeModel("gemini-1.5-pro-latest")
# DeepL translator
DEEPL_KEY = os.getenv("DEEPL_API_KEY")
DEEPL_TRANSLATOR = deepl.Translator(DEEPL_KEY) if DEEPL_KEY else None
# Supported DeepL target languages (code โ label)
LANGS = {
"EN": "English",
"DE": "German",
"FR": "French",
"ES": "Spanish",
"IT": "Italian",
"NL": "Dutch",
"PT-PT": "Portuguese",
"PT-BR": "Portuguese (BR)",
"PL": "Polish",
"JA": "Japanese",
"ZH": "Chinese",
}
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Helpers
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def ask_gemini(prompt: str, temperature: float = 0.6) -> str:
try:
rsp = GEMINI_MODEL.generate_content(
prompt, generation_config={"temperature": temperature}
)
return rsp.text.strip()
except Exception as err:
return f"[Geminiย Error] {err}"
def translate_if_needed(text: str, target_code: str) -> str:
if target_code == "EN" or not DEEPL_TRANSLATOR:
return text # already English or DeepL key missing
try:
res = DEEPL_TRANSLATOR.translate_text(text, target_lang=target_code)
return res.text
except Exception as err:
return f"[DeepLย Error] {err}\n\n{text}"
def save_docx(text: str) -> str:
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".docx")
doc = Document()
for line in text.splitlines():
doc.add_paragraph(line)
doc.save(tmp.name)
return tmp.name
def save_pdf(text: str) -> str:
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
c = canvas.Canvas(tmp.name, pagesize=LETTER)
width, height = LETTER
y = height - 72
for line in text.splitlines():
c.drawString(72, y, line)
y -= 14
if y < 72:
c.showPage()
y = height - 72
c.save()
return tmp.name
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Core AI functions
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def generate_resume(name, email, phone, summary, experience, education, skills, lang):
prompt = f"""
Create a professional resume in Markdown. **Do not use firstโperson pronouns.**
Targetย language: {LANGS[lang]}
Name: {name}
Email: {email}
Phone: {phone}
Professionalย Summary:
{summary}
Experience:
{experience}
Education:
{education}
Skills:
{skills}
"""
resume_md = ask_gemini(prompt)
return translate_if_needed(resume_md, lang)
def score_resume(resume_md, job_desc):
prompt = f"""
Evaluate the RESUME against the JOBย DESCRIPTION. Return *only* compact Markdown:
### Matchย Score
<integer 0โ100>
### Suggestions
- <bulletย 1>
- <bulletย 2>
- <bulletย 3>
"""
return ask_gemini(prompt.format(resume_md=resume_md, job_desc=job_desc), temperature=0.4)
def refine_section(section_text, instruction, lang):
prompt = f"""
Perform the following instruction on the resume section. Respond in {LANGS[lang]}.
Instruction: {instruction}
Text:
{section_text}
"""
refined = ask_gemini(prompt)
return translate_if_needed(refined, lang)
def generate_cover_letter(resume_md, job_desc, tone, lang):
prompt = f"""
Draft a oneโpage cover letter (โคโฏ300โฏwords) in {tone} tone, aligning the RESUME
to the JOBย DESCRIPTION. Use {LANGS[lang]} throughout. Salutation: "Dear Hiring Manager,".
RESUME:
{resume_md}
JOBย DESCRIPTION:
{job_desc}
"""
letter = ask_gemini(prompt)
return translate_if_needed(letter, lang)
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# JD scraper
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def scrape_job_description(url):
try:
headers = {"User-Agent": "Mozilla/5.0"}
page = requests.get(url, headers=headers, timeout=10)
soup = BeautifulSoup(page.text, "html.parser")
# Heuristics for popular boards
selectors = [
"div.jobsearch-jobDescriptionText", # Indeed
"section.description", # generic
"div.jobs-description__content", # LinkedIn
"div#job-details" # Greenhouse
]
for sel in selectors:
block = soup.select_one(sel)
if block:
return block.get_text(" ", strip=True)
return soup.get_text(" ", strip=True)[:3000]
except Exception as e:
return f"[Error] {e}"
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Wrapper to export resume + files
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def generate_resume_and_files(name, email, phone, summary,
experience, education, skills, lang):
resume_md = generate_resume(name, email, phone, summary,
experience, education, skills, lang)
return resume_md, save_docx(resume_md), save_pdf(resume_md)
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Gradio UI
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
with gr.Blocks(title="AI Resume Studio") as demo:
gr.Markdown("## ๐ง ย Interactiveย AIย Resumeย Studio (Geminiย รย DeepL)")
LANG_CHOICES = [(v, k) for k, v in LANGS.items()]
# โโ 1 ยท Resume generation โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
with gr.Tab("๐ Generate Resume"):
with gr.Row():
name = gr.Textbox(label="Name")
email = gr.Textbox(label="Email")
phone = gr.Textbox(label="Phone")
summary = gr.Textbox(label="Professional Summary")
experience = gr.Textbox(label="Experience")
education = gr.Textbox(label="Education")
skills = gr.Textbox(label="Skills")
lang_sel = gr.Dropdown(LANG_CHOICES, value="EN", label="Outputย Language")
resume_md = gr.Markdown(label="Generated Resume")
docx_file = gr.File(label="โฌย Word (.docx)")
pdf_file = gr.File(label="โฌย PDF (.pdf)")
gen_btn = gr.Button("Generate Resume")
gen_btn.click(
generate_resume_and_files,
inputs=[name, email, phone, summary, experience, education, skills, lang_sel],
outputs=[resume_md, docx_file, pdf_file],
)
# โโ 2 ยท Match score โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
with gr.Tab("๐งฎ Score Resume Against Job"):
resume_in = gr.Textbox(label="Resumeย (Markdown)", lines=12)
jd_in = gr.Textbox(label="Jobย Description", lines=8)
score_out = gr.Markdown(label="Scoreย &ย Suggestions")
score_btn = gr.Button("Evaluate")
score_btn.click(score_resume, inputs=[resume_in, jd_in], outputs=score_out)
# โโ 3 ยท AI Section CoโPilot โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
with gr.Tab("โ๏ธ AI Section CoโPilot"):
sec_text = gr.Textbox(label="Sectionย Text", lines=6)
action = gr.Radio(
["Rewrite", "Make More Concise", "Quantify Achievements", "Convert to Bullet Points"],
label="Action"
)
sec_lang = gr.Dropdown(LANG_CHOICES, value="EN", label="Language")
sec_out = gr.Textbox(label="AI Output", lines=6)
sec_btn = gr.Button("Apply")
sec_btn.click(
refine_section, inputs=[sec_text, action, sec_lang], outputs=sec_out
)
# โโ 4 ยท Coverโletter generator โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
with gr.Tab("๐ง CoverโLetter Generator"):
cv_resume = gr.Textbox(label="Resumeย (Markdown)", lines=12)
cv_jd = gr.Textbox(label="Jobย Description", lines=8)
cv_tone = gr.Radio(["Professional", "Friendly", "Enthusiastic"], value="Professional", label="Tone")
cv_lang = gr.Dropdown(LANG_CHOICES, value="EN", label="Language")
cv_out = gr.Markdown(label="Coverย Letter")
cv_btn = gr.Button("Generate Cover Letter")
cv_btn.click(
generate_cover_letter,
inputs=[cv_resume, cv_jd, cv_tone, cv_lang],
outputs=cv_out,
)
# โโ 5 ยท Jobโdescription scraper โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
with gr.Tab("๐ Job Description Scraper"):
url_in = gr.Textbox(label="Jobย URL")
jd_out = gr.Textbox(label="Extractedย Description", lines=12)
scrape_btn = gr.Button("Fetch Description")
scrape_btn.click(scrape_job_description, inputs=url_in, outputs=jd_out)
demo.launch(share=False) # HFย Spaces already publishes the app
|