Spaces:
Sleeping
Sleeping
| # 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() |