# 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()