Update app.py
Browse files
app.py
CHANGED
@@ -6,9 +6,6 @@ from weasyprint import HTML
|
|
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 |
|
@@ -91,47 +88,33 @@ def get_resume_response(prompt: str, api_key: str, model: str = "llama-3.3-70b",
|
|
91 |
return response_string
|
92 |
|
93 |
def remove_unwanted_headings(markdown_text: str) -> str:
|
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
|
107 |
"""
|
108 |
-
|
109 |
-
|
110 |
"""
|
111 |
-
|
|
|
|
|
|
|
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
|
119 |
else:
|
120 |
return "File format not supported for conversion to Markdown.", "", "", "", ""
|
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", "")
|
@@ -139,48 +122,35 @@ def process_resume(resume, jd_string):
|
|
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"
|
146 |
with open(original_resume_path, "w", encoding='utf-8') as f:
|
147 |
f.write(resume_string)
|
148 |
|
149 |
-
# Simpan resume hasil optimasi (Markdown)
|
150 |
optimized_resume_path = "resumes/optimized_resume.md"
|
151 |
with open(optimized_resume_path, "w", encoding='utf-8') as f:
|
152 |
f.write(new_resume)
|
153 |
-
|
154 |
-
# Kembalikan output untuk di-render di Gradio
|
155 |
return resume_string, new_resume, original_resume_path, optimized_resume_path, suggestions
|
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"
|
168 |
-
|
169 |
-
# Gunakan stylesheet (pastikan path style.css benar)
|
170 |
HTML(string=html_content).write_pdf(
|
171 |
output_pdf_file,
|
172 |
-
stylesheets=["resumes/style.css"]
|
173 |
)
|
174 |
-
|
175 |
return output_pdf_file
|
176 |
except Exception as e:
|
177 |
return f"Failed to export resume: {str(e)} π"
|
178 |
|
179 |
-
#
|
180 |
with gr.Blocks() as app:
|
181 |
gr.Markdown("# Resume Optimizer π")
|
182 |
gr.Markdown("Upload your resume, paste the job description, and get actionable insights!")
|
183 |
-
|
184 |
with gr.Row():
|
185 |
resume_input = gr.File(label="Upload Your Resume")
|
186 |
jd_input = gr.Textbox(
|
@@ -189,33 +159,31 @@ with gr.Blocks() as app:
|
|
189 |
interactive=True,
|
190 |
placeholder="Paste job description..."
|
191 |
)
|
192 |
-
|
193 |
run_button = gr.Button("Optimize Resume π€")
|
194 |
-
|
195 |
with gr.Row():
|
196 |
before_md = gr.Markdown(label="Original Resume (Before)")
|
197 |
after_md = gr.Markdown(label="Optimized Resume (After)")
|
198 |
output_suggestions = gr.Markdown(label="Suggestions")
|
199 |
-
|
200 |
with gr.Row():
|
201 |
download_before = gr.File(label="Download Original Resume")
|
202 |
download_after = gr.File(label="Download Optimized Resume")
|
203 |
|
204 |
export_button = gr.Button("Export Optimized Resume as PDF π")
|
205 |
export_result = gr.File(label="Download PDF")
|
206 |
-
|
207 |
-
# Saat tombol Optimize Resume diklik
|
208 |
run_button.click(
|
209 |
process_resume,
|
210 |
inputs=[resume_input, jd_input],
|
211 |
outputs=[before_md, after_md, download_before, download_after, output_suggestions]
|
212 |
)
|
213 |
-
|
214 |
-
# Saat tombol Export PDF diklik
|
215 |
export_button.click(
|
216 |
export_resume,
|
217 |
inputs=[after_md],
|
218 |
outputs=[export_result]
|
219 |
)
|
220 |
-
|
221 |
-
app.launch()
|
|
|
6 |
from markitdown import MarkItDown
|
7 |
from cerebras.cloud.sdk import Cerebras
|
8 |
|
|
|
|
|
|
|
9 |
# Dapatkan API key dari environment variables
|
10 |
api_key = os.environ.get("CEREBRAS_API_KEY")
|
11 |
|
|
|
88 |
return response_string
|
89 |
|
90 |
def remove_unwanted_headings(markdown_text: str) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
pattern = r'^#+.*\b(?:[Rr]esume|[Oo]ptimized)\b.*$'
|
92 |
return re.sub(pattern, '', markdown_text, flags=re.MULTILINE)
|
93 |
|
94 |
+
def fix_jobsdesk_bullets(text: str) -> str:
|
95 |
"""
|
96 |
+
Mengubah semua baris yang dimulai dengan tanda '-' (opsional dengan spasi)
|
97 |
+
menjadi format bullet list Markdown standar.
|
98 |
"""
|
99 |
+
return re.sub(r'^\s*-\s+', '- ', text, flags=re.MULTILINE)
|
100 |
+
|
101 |
+
def process_resume(resume, jd_string):
|
102 |
+
# Cek ekstensi dan konversi dokumen menggunakan MarkItDown
|
103 |
supported_extensions = ('.pptx', '.docx', '.pdf', '.jpg', '.jpeg', '.png', '.xlsx')
|
|
|
|
|
104 |
if resume.name.lower().endswith(supported_extensions):
|
|
|
105 |
result = md_converter.convert(resume.name)
|
106 |
+
resume_string = result.text_content
|
107 |
else:
|
108 |
return "File format not supported for conversion to Markdown.", "", "", "", ""
|
109 |
|
|
|
110 |
prompt = create_prompt(resume_string, jd_string)
|
|
|
|
|
111 |
response_string = get_resume_response(prompt, api_key)
|
112 |
+
|
|
|
113 |
response_list = response_string.split("## Additional Suggestions")
|
114 |
new_resume = response_list[0].strip()
|
115 |
new_resume = re.sub(r'^\* ', '- ', new_resume, flags=re.MULTILINE)
|
116 |
suggestions = "## Additional Suggestions\n\n" + response_list[1].strip() if len(response_list) > 1 else ""
|
117 |
+
|
|
|
118 |
new_resume = new_resume.replace("# Optimized Resume", "")
|
119 |
new_resume = new_resume.replace("## Optimized Resume", "")
|
120 |
new_resume = new_resume.replace("Optimized Resume", "")
|
|
|
122 |
new_resume = new_resume.replace("## Resume", "")
|
123 |
new_resume = re.sub(r'^#+\s*Resume\s*', '', new_resume, flags=re.MULTILINE)
|
124 |
new_resume = remove_unwanted_headings(new_resume)
|
125 |
+
new_resume = fix_jobsdesk_bullets(new_resume) # Ubah tanda '-' menjadi bullet list
|
126 |
+
|
|
|
127 |
original_resume_path = "resumes/original_resume.md"
|
128 |
with open(original_resume_path, "w", encoding='utf-8') as f:
|
129 |
f.write(resume_string)
|
130 |
|
|
|
131 |
optimized_resume_path = "resumes/optimized_resume.md"
|
132 |
with open(optimized_resume_path, "w", encoding='utf-8') as f:
|
133 |
f.write(new_resume)
|
134 |
+
|
|
|
135 |
return resume_string, new_resume, original_resume_path, optimized_resume_path, suggestions
|
136 |
|
137 |
def export_resume(new_resume):
|
|
|
|
|
|
|
|
|
138 |
try:
|
|
|
139 |
html_content = markdown.markdown(new_resume, extensions=['extra', 'nl2br'])
|
|
|
|
|
140 |
output_pdf_file = "resumes/optimized_resume.pdf"
|
|
|
|
|
141 |
HTML(string=html_content).write_pdf(
|
142 |
output_pdf_file,
|
143 |
+
stylesheets=["resumes/style.css"]
|
144 |
)
|
|
|
145 |
return output_pdf_file
|
146 |
except Exception as e:
|
147 |
return f"Failed to export resume: {str(e)} π"
|
148 |
|
149 |
+
# Bagian aplikasi Gradio (sama seperti kode Anda)
|
150 |
with gr.Blocks() as app:
|
151 |
gr.Markdown("# Resume Optimizer π")
|
152 |
gr.Markdown("Upload your resume, paste the job description, and get actionable insights!")
|
153 |
+
|
154 |
with gr.Row():
|
155 |
resume_input = gr.File(label="Upload Your Resume")
|
156 |
jd_input = gr.Textbox(
|
|
|
159 |
interactive=True,
|
160 |
placeholder="Paste job description..."
|
161 |
)
|
162 |
+
|
163 |
run_button = gr.Button("Optimize Resume π€")
|
164 |
+
|
165 |
with gr.Row():
|
166 |
before_md = gr.Markdown(label="Original Resume (Before)")
|
167 |
after_md = gr.Markdown(label="Optimized Resume (After)")
|
168 |
output_suggestions = gr.Markdown(label="Suggestions")
|
169 |
+
|
170 |
with gr.Row():
|
171 |
download_before = gr.File(label="Download Original Resume")
|
172 |
download_after = gr.File(label="Download Optimized Resume")
|
173 |
|
174 |
export_button = gr.Button("Export Optimized Resume as PDF π")
|
175 |
export_result = gr.File(label="Download PDF")
|
176 |
+
|
|
|
177 |
run_button.click(
|
178 |
process_resume,
|
179 |
inputs=[resume_input, jd_input],
|
180 |
outputs=[before_md, after_md, download_before, download_after, output_suggestions]
|
181 |
)
|
182 |
+
|
|
|
183 |
export_button.click(
|
184 |
export_resume,
|
185 |
inputs=[after_md],
|
186 |
outputs=[export_result]
|
187 |
)
|
188 |
+
|
189 |
+
app.launch()
|