cvcv / app.py
firmanaziz's picture
Update app.py
446c80c verified
# Install dependensi yang dibutuhkan oleh Hugging Face Spaces
#!pip install -q gradio google-generativeai pymupdf
import gradio as gr
import google.generativeai as genai
import fitz # PyMuPDF
import json
import os
# --- KONFIGURASI API KEY DARI HUGGING FACE SECRETS ---
API_CONFIGURED = False
try:
# PASTIKAN NAMA SECRET ANDA ADALAH 'GEMINI_API_KEY', BUKAN 'GOOGLE_API_KEY'
api_key = os.environ.get('GEMINI_API_KEY')
if api_key:
genai.configure(api_key=api_key)
model = genai.GenerativeModel('gemini-1.5-flash-latest')
API_CONFIGURED = True
print("βœ… Konfigurasi API dan model berhasil.")
else:
print("πŸ›‘ Secret 'GEMINI_API_KEY' tidak ditemukan.")
except Exception as e:
print(f"πŸ›‘ Terjadi error saat inisialisasi: {e}")
# --- FUNGSI-FUNGSI UTAMA ---
def ekstrak_teks_dari_pdf(path_file_pdf):
try:
with fitz.open(path_file_pdf) as dokumen:
teks_lengkap = "".join(halaman.get_text() for halaman in dokumen)
return teks_lengkap
except Exception as e:
raise gr.Error(f"Gagal membaca file PDF: {e}")
def clean_and_parse_json(raw_text):
"""Fungsi yang lebih andal untuk membersihkan dan mem-parsing JSON dari teks mentah."""
# Menemukan awal dan akhir blok JSON
start_index = raw_text.find('{')
end_index = raw_text.rfind('}')
if start_index == -1 or end_index == -1:
raise json.JSONDecodeError("Tidak ditemukan blok JSON valid dalam respons.", raw_text, 0)
json_str = raw_text[start_index:end_index+1]
return json.loads(json_str)
def format_output_markdown(parsed_json, hasil_analisis):
"""Menyusun hasil akhir menjadi string Markdown yang rapi."""
nama = parsed_json.get('nama_lengkap', 'N/A')
skor = hasil_analisis.get('skor_kecocokan', 'N/A')
ringkasan = hasil_analisis.get('ringkasan_analisis', 'N/A')
kekuatan = "\n".join([f"- {poin}" for poin in hasil_analisis.get('poin_kekuatan', [])])
kelemahan = "\n".join([f"- {poin}" for poin in hasil_analisis.get('potensi_kelemahan', [])])
return f"""
### HASIL ANALISIS KECOCOKAN KANDIDAT
**πŸ‘€ Nama Kandidat:** {nama}
***
**⭐ Skor Kecocokan:** {skor} / 10
**πŸ“ Ringkasan Analisis:**
{ringkasan}
**βœ… Poin Kekuatan Utama:**
{kekuatan}
**⚠️ Potensi Kelemahan / Pertanyaan Lanjutan:**
{kelemahan}
"""
def analyze_cv(cv_file, deskripsi_pekerjaan):
"""Fungsi utama yang dijalankan oleh Gradio saat tombol ditekan."""
if not API_CONFIGURED:
raise gr.Error("API Key Gemini belum terkonfigurasi. Periksa Logs aplikasi di Hugging Face.")
if cv_file is None or not deskripsi_pekerjaan.strip():
raise gr.Error("Mohon upload file CV (PDF) dan isi deskripsi pekerjaan.")
try:
print("--- Memulai Proses Analisis ---")
print("1. Mengekstrak teks dari PDF...")
teks_cv = ekstrak_teks_dari_pdf(cv_file.name)
if not teks_cv:
raise gr.Error("PDF kosong atau tidak dapat dibaca.")
print("βœ… Teks berhasil diekstrak.")
print("2. Mengirim permintaan ekstraksi JSON ke Gemini...")
prompt_ekstraksi = f"Ekstrak informasi dari teks CV berikut dan sajikan dalam format JSON. Pastikan JSON memiliki struktur: nama_lengkap, kontak, ringkasan, keterampilan, pengalaman_kerja, dan pendidikan. Teks CV:\n---\n{teks_cv}\n---\nOutput harus berupa JSON saja."
response_ekstraksi = model.generate_content(prompt_ekstraksi)
parsed_json = clean_and_parse_json(response_ekstraksi.text)
print("βœ… Ekstraksi JSON berhasil.")
print("3. Mengirim permintaan analisis kecocokan ke Gemini...")
prompt_analisis = f"Anda adalah Manajer Perekrutan. Analisis data kandidat (JSON) dan bandingkan dengan deskripsi pekerjaan. Deskripsi Pekerjaan: {deskripsi_pekerjaan}. Data Kandidat (JSON): {json.dumps(parsed_json, indent=2)}. Instruksi: Berikan output JSON dengan struktur: skor_kecocokan, ringkasan_analisis, poin_kekuatan, potensi_kelemahan. Output harus JSON saja."
response_analisis = model.generate_content(prompt_analisis)
hasil_analisis = clean_and_parse_json(response_analisis.text)
print("βœ… Analisis kecocokan berhasil.")
print("4. Menyusun output akhir...")
final_output = format_output_markdown(parsed_json, hasil_analisis)
print("--- Proses Selesai ---")
return final_output
except Exception as e:
print(f"πŸ›‘ ERROR DALAM FUNGSI ANALISIS: {e}")
raise gr.Error(f"Terjadi kesalahan: {e}")
# --- MEMBUAT INTERFACE GRADIO ---
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# πŸ€– Aplikasi Analisis CV Otomatis")
gr.Markdown("Upload CV dalam format PDF dan masukkan deskripsi pekerjaan untuk mendapatkan analisis kecocokan secara instan.")
with gr.Row():
with gr.Column(scale=1):
cv_pdf = gr.File(label="1. Upload CV Anda (PDF)", file_types=[".pdf"])
job_desc = gr.Textbox(lines=10, label="2. Masukkan Deskripsi Pekerjaan", placeholder="Contoh:\nPosisi: Senior Backend Engineer...")
with gr.Row():
clear_button = gr.ClearButton(value="Hapus Semua")
analyze_button = gr.Button("✨ Analisis Sekarang", variant="primary")
with gr.Column(scale=2):
output_analysis = gr.Markdown(label="Hasil Analisis")
# Menghubungkan tombol hapus ke semua komponen input dan output
clear_button.add([cv_pdf, job_desc, output_analysis])
analyze_button.click(
fn=analyze_cv,
inputs=[cv_pdf, job_desc],
outputs=[output_analysis],
show_progress='full' # Menampilkan status proses
)
if __name__ == "__main__":
demo.launch()