Update app.py
Browse files
app.py
CHANGED
@@ -6,21 +6,22 @@ from weasyprint import HTML
|
|
6 |
from markitdown import MarkItDown
|
7 |
from cerebras.cloud.sdk import Cerebras
|
8 |
|
9 |
-
#
|
|
|
|
|
|
|
10 |
api_key = os.environ.get("CEREBRAS_API_KEY")
|
11 |
|
12 |
-
# Inisialisasi MarkItDown
|
13 |
md_converter = MarkItDown()
|
14 |
|
15 |
def create_prompt(resume_string: str, jd_string: str) -> str:
|
16 |
"""
|
17 |
-
Membuat prompt detail
|
18 |
-
|
19 |
-
berbentuk bullet list.
|
20 |
"""
|
21 |
return f"""
|
22 |
-
You are a professional resume optimization expert specializing in tailoring resumes to specific job descriptions.
|
23 |
-
Your goal is to optimize my resume and provide actionable suggestions for improvement to align with the target role.
|
24 |
|
25 |
### Guidelines:
|
26 |
1. **Relevance**:
|
@@ -43,11 +44,7 @@ Your goal is to optimize my resume and provide actionable suggestions for improv
|
|
43 |
|
44 |
5. **Formatting**:
|
45 |
- Output the tailored resume in **clean Markdown format**.
|
46 |
-
- Use "##" for main headings (WORK EXPERIENCE, EDUCATION, etc.).
|
47 |
-
- Use "###" for each role in WORK EXPERIENCE or each education entry.
|
48 |
-
- For each role in WORK EXPERIENCE, use bullet points ("- ") in separate lines to describe responsibilities/achievements.
|
49 |
- Include an **"Additional Suggestions"** section at the end with actionable improvement recommendations.
|
50 |
-
- Resume should not exceed one page if possible.
|
51 |
|
52 |
---
|
53 |
|
@@ -97,30 +94,26 @@ def remove_unwanted_headings(markdown_text: str) -> str:
|
|
97 |
"""
|
98 |
Menghapus heading apa pun yang mengandung kata 'resume' atau 'optimized'
|
99 |
(dalam berbagai huruf besar/kecil).
|
100 |
-
Contoh heading yang akan dihapus:
|
|
|
|
|
|
|
|
|
101 |
"""
|
102 |
pattern = r'^#+.*\b(?:[Rr]esume|[Oo]ptimized)\b.*$'
|
103 |
return re.sub(pattern, '', markdown_text, flags=re.MULTILINE)
|
104 |
|
105 |
-
def fix_work_experience_bullets(text: str) -> str:
|
106 |
-
"""
|
107 |
-
Mencari pola ' - ' di tengah kalimat (yang menandakan bullet),
|
108 |
-
lalu memecahnya ke baris baru agar menjadi bullet list Markdown yang valid.
|
109 |
-
"""
|
110 |
-
# Ganti " - " dengan "\n- " agar bullet pindah ke baris baru
|
111 |
-
return re.sub(r'\s-\s', '\n- ', text)
|
112 |
-
|
113 |
def process_resume(resume, jd_string):
|
114 |
"""
|
115 |
-
Memproses file resume
|
116 |
-
|
117 |
"""
|
118 |
-
#
|
119 |
supported_extensions = ('.pptx', '.docx', '.pdf', '.jpg', '.jpeg', '.png', '.xlsx')
|
120 |
|
121 |
# Cek apakah file resume memiliki ekstensi yang didukung
|
122 |
if resume.name.lower().endswith(supported_extensions):
|
123 |
-
# Konversi file ke Markdown
|
124 |
result = md_converter.convert(resume.name)
|
125 |
resume_string = result.text_content # konten Markdown hasil konversi
|
126 |
else:
|
@@ -128,20 +121,25 @@ def process_resume(resume, jd_string):
|
|
128 |
|
129 |
# Buat prompt untuk AI
|
130 |
prompt = create_prompt(resume_string, jd_string)
|
131 |
-
|
132 |
# Dapatkan response dari AI
|
133 |
response_string = get_resume_response(prompt, api_key)
|
134 |
|
135 |
# Pisahkan response menjadi "optimized resume" dan "additional suggestions"
|
136 |
response_list = response_string.split("## Additional Suggestions")
|
137 |
new_resume = response_list[0].strip()
|
|
|
138 |
suggestions = "## Additional Suggestions\n\n" + response_list[1].strip() if len(response_list) > 1 else ""
|
139 |
|
140 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
new_resume = remove_unwanted_headings(new_resume)
|
142 |
-
|
143 |
-
# 2) Rapikan bullet Work Experience jika AI menaruh ' - ' dalam satu baris
|
144 |
-
new_resume = fix_work_experience_bullets(new_resume)
|
145 |
|
146 |
# Simpan resume asli (Markdown) jika diperlukan
|
147 |
original_resume_path = "resumes/original_resume.md"
|
@@ -158,12 +156,12 @@ def process_resume(resume, jd_string):
|
|
158 |
|
159 |
def export_resume(new_resume):
|
160 |
"""
|
161 |
-
Meng-export resume hasil optimasi (Markdown) menjadi PDF
|
162 |
-
|
163 |
"""
|
164 |
try:
|
165 |
# Konversi Markdown ke HTML
|
166 |
-
html_content = markdown.markdown(new_resume)
|
167 |
|
168 |
# Path output PDF
|
169 |
output_pdf_file = "resumes/optimized_resume.pdf"
|
@@ -171,12 +169,12 @@ def export_resume(new_resume):
|
|
171 |
# Gunakan stylesheet (pastikan path style.css benar)
|
172 |
HTML(string=html_content).write_pdf(
|
173 |
output_pdf_file,
|
174 |
-
stylesheets=["resumes/style.css"] #
|
175 |
)
|
176 |
|
177 |
return output_pdf_file
|
178 |
except Exception as e:
|
179 |
-
return f"Failed to export resume: {str(e)}"
|
180 |
|
181 |
# Bangun aplikasi Gradio
|
182 |
with gr.Blocks() as app:
|
|
|
6 |
from markitdown import MarkItDown
|
7 |
from cerebras.cloud.sdk import Cerebras
|
8 |
|
9 |
+
# Pastikan Anda memiliki file style.css di direktori yang sama
|
10 |
+
# atau sesuaikan path 'stylesheets' di fungsi export_resume
|
11 |
+
|
12 |
+
# Dapatkan API key dari environment variables
|
13 |
api_key = os.environ.get("CEREBRAS_API_KEY")
|
14 |
|
15 |
+
# Inisialisasi MarkItDown
|
16 |
md_converter = MarkItDown()
|
17 |
|
18 |
def create_prompt(resume_string: str, jd_string: str) -> str:
|
19 |
"""
|
20 |
+
Membuat prompt detail untuk AI agar melakukan optimasi resume
|
21 |
+
berdasarkan job description.
|
|
|
22 |
"""
|
23 |
return f"""
|
24 |
+
You are a professional resume optimization expert specializing in tailoring resumes to specific job descriptions. Your goal is to optimize my resume and provide actionable suggestions for improvement to align with the target role.
|
|
|
25 |
|
26 |
### Guidelines:
|
27 |
1. **Relevance**:
|
|
|
44 |
|
45 |
5. **Formatting**:
|
46 |
- Output the tailored resume in **clean Markdown format**.
|
|
|
|
|
|
|
47 |
- Include an **"Additional Suggestions"** section at the end with actionable improvement recommendations.
|
|
|
48 |
|
49 |
---
|
50 |
|
|
|
94 |
"""
|
95 |
Menghapus heading apa pun yang mengandung kata 'resume' atau 'optimized'
|
96 |
(dalam berbagai huruf besar/kecil).
|
97 |
+
Contoh heading yang akan dihapus:
|
98 |
+
# Resume
|
99 |
+
## optimized
|
100 |
+
### Optimized Resume
|
101 |
+
dsb.
|
102 |
"""
|
103 |
pattern = r'^#+.*\b(?:[Rr]esume|[Oo]ptimized)\b.*$'
|
104 |
return re.sub(pattern, '', markdown_text, flags=re.MULTILINE)
|
105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
def process_resume(resume, jd_string):
|
107 |
"""
|
108 |
+
Memproses file resume yang di-upload dan job description, lalu
|
109 |
+
menghasilkan resume yang telah dioptimasi + saran perbaikan.
|
110 |
"""
|
111 |
+
# Format yang didukung
|
112 |
supported_extensions = ('.pptx', '.docx', '.pdf', '.jpg', '.jpeg', '.png', '.xlsx')
|
113 |
|
114 |
# Cek apakah file resume memiliki ekstensi yang didukung
|
115 |
if resume.name.lower().endswith(supported_extensions):
|
116 |
+
# Konversi file ke Markdown menggunakan MarkItDown
|
117 |
result = md_converter.convert(resume.name)
|
118 |
resume_string = result.text_content # konten Markdown hasil konversi
|
119 |
else:
|
|
|
121 |
|
122 |
# Buat prompt untuk AI
|
123 |
prompt = create_prompt(resume_string, jd_string)
|
124 |
+
|
125 |
# Dapatkan response dari AI
|
126 |
response_string = get_resume_response(prompt, api_key)
|
127 |
|
128 |
# Pisahkan response menjadi "optimized resume" dan "additional suggestions"
|
129 |
response_list = response_string.split("## Additional Suggestions")
|
130 |
new_resume = response_list[0].strip()
|
131 |
+
new_resume = re.sub(r'^\* ', '- ', new_resume, flags=re.MULTILINE)
|
132 |
suggestions = "## Additional Suggestions\n\n" + response_list[1].strip() if len(response_list) > 1 else ""
|
133 |
|
134 |
+
# ===== Hapus heading yang mengandung kata resume/optimized =====
|
135 |
+
new_resume = new_resume.replace("# Optimized Resume", "")
|
136 |
+
new_resume = new_resume.replace("## Optimized Resume", "")
|
137 |
+
new_resume = new_resume.replace("Optimized Resume", "")
|
138 |
+
new_resume = new_resume.replace("# Resume", "")
|
139 |
+
new_resume = new_resume.replace("## Resume", "")
|
140 |
+
new_resume = re.sub(r'^#+\s*Resume\s*', '', new_resume, flags=re.MULTILINE)
|
141 |
new_resume = remove_unwanted_headings(new_resume)
|
142 |
+
# ===============================================================
|
|
|
|
|
143 |
|
144 |
# Simpan resume asli (Markdown) jika diperlukan
|
145 |
original_resume_path = "resumes/original_resume.md"
|
|
|
156 |
|
157 |
def export_resume(new_resume):
|
158 |
"""
|
159 |
+
Meng-export resume hasil optimasi (Markdown) menjadi PDF
|
160 |
+
menggunakan WeasyPrint.
|
161 |
"""
|
162 |
try:
|
163 |
# Konversi Markdown ke HTML
|
164 |
+
html_content = markdown.markdown(new_resume, extensions=['extra', 'nl2br'])
|
165 |
|
166 |
# Path output PDF
|
167 |
output_pdf_file = "resumes/optimized_resume.pdf"
|
|
|
169 |
# Gunakan stylesheet (pastikan path style.css benar)
|
170 |
HTML(string=html_content).write_pdf(
|
171 |
output_pdf_file,
|
172 |
+
stylesheets=["resumes/style.css"] # atau "resumes/style.css" jika style.css di folder "resumes"
|
173 |
)
|
174 |
|
175 |
return output_pdf_file
|
176 |
except Exception as e:
|
177 |
+
return f"Failed to export resume: {str(e)} 💔"
|
178 |
|
179 |
# Bangun aplikasi Gradio
|
180 |
with gr.Blocks() as app:
|