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: | |
| api_key = os.environ.get('GEMINI_API_KEY') | |
| if api_key: | |
| genai.configure(api_key=api_key) | |
| model = genai.GenerativeModel('gemini-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 andal untuk membersihkan dan mem-parsing JSON dari teks mentah.""" | |
| 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(hasil_penilaian): | |
| """Menyusun hasil penilaian menjadi string Markdown yang rapi.""" | |
| skor = hasil_penilaian.get('skor_keseluruhan', 0) | |
| penilaian = hasil_penilaian.get('penilaian_per_kategori', {}) | |
| kelebihan = "\n".join([f"- {poin}" for poin in hasil_penilaian.get('poin_kelebihan', [])]) | |
| perbaikan = "\n".join([f"- {poin}" for poin in hasil_penilaian.get('poin_perbaikan', [])]) | |
| # Membuat progress bar untuk setiap kategori | |
| progress_bars = "" | |
| for kategori, nilai in penilaian.items(): | |
| # Mengganti underscore dengan spasi dan membuat judul | |
| nama_kategori = kategori.replace("_", " ").title() | |
| progress_bars += f"**{nama_kategori}**\n`{'β' * int(nilai/10)}{'β' * (10 - int(nilai/10))}` {nilai}/100\n" | |
| return f""" | |
| ### HASIL PENILAIAN CV ANDA | |
| **β Skor Keseluruhan: {skor} / 100** | |
| *** | |
| {progress_bars} | |
| *** | |
| **β Hal yang Sudah Baik:** | |
| {kelebihan} | |
| **π‘ Poin yang Perlu Diperbaiki:** | |
| {perbaikan} | |
| """ | |
| def score_cv(cv_file): | |
| """Fungsi utama yang dijalankan Gradio untuk menilai CV.""" | |
| if not API_CONFIGURED: | |
| raise gr.Error("API Key Gemini belum terkonfigurasi. Periksa Logs aplikasi di Hugging Face.") | |
| if cv_file is None: | |
| raise gr.Error("Mohon upload file CV (PDF) Anda.") | |
| try: | |
| print("--- Memulai Proses Penilaian CV ---") | |
| 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 penilaian ke Gemini...") | |
| prompt_penilaian = f""" | |
| Anda adalah seorang career coach dan perekrut profesional. Tugas Anda adalah menilai kualitas sebuah CV berdasarkan kriteria standar industri. | |
| Teks CV: | |
| --- | |
| {teks_cv} | |
| --- | |
| Lakukan penilaian berdasarkan kriteria berikut dan berikan output dalam format JSON yang ketat. | |
| Kriteria Penilaian: | |
| 1. Kelengkapan_Informasi (0-100): Apakah ada nama, kontak, ringkasan, pengalaman, pendidikan, dan skill? | |
| 2. Keterbacaan_dan_Format (0-100): Apakah formatnya rapi, mudah dibaca, dan menggunakan bullet points dengan baik? | |
| 3. Dampak_Pengalaman_Kerja (0-100): Apakah deskripsi pengalaman menggunakan kata kerja aktif dan hasil yang terukur (angka/metrik)? Semakin banyak metrik, semakin tinggi nilainya. | |
| Instruksi Output JSON: | |
| Berikan output dalam format JSON dengan struktur berikut: | |
| - "skor_keseluruhan": Rata-rata dari ketiga skor kategori (angka 0-100). | |
| - "penilaian_per_kategori": Objek berisi skor untuk "kelengkapan_informasi", "keterbacaan_dan_format", dan "dampak_pengalaman_kerja". | |
| - "poin_kelebihan": Sebuah array (list) berisi 2-3 poin positif dari CV ini. | |
| - "poin_perbaikan": Sebuah array (list) berisi 3-4 poin saran yang paling penting dan actionable untuk meningkatkan kualitas CV. | |
| Pastikan output hanya berupa JSON saja tanpa teks tambahan. | |
| """ | |
| response = model.generate_content(prompt_penilaian) | |
| hasil_penilaian = clean_and_parse_json(response.text) | |
| print("β Penilaian berhasil diterima.") | |
| print("3. Menyusun output akhir...") | |
| final_output = format_output_markdown(hasil_penilaian) | |
| print("--- Proses Selesai ---") | |
| return final_output | |
| except Exception as e: | |
| print(f"π ERROR DALAM FUNGSI PENILAIAN: {e}") | |
| raise gr.Error(f"Terjadi kesalahan: {e}") | |
| # --- MEMBUAT INTERFACE GRADIO --- | |
| with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# π‘ Aplikasi Penilai Kualitas CV (CV Scorer)") | |
| gr.Markdown("Upload CV Anda dalam format PDF untuk mendapatkan skor dan saran perbaikan instan berdasarkan standar industri.") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| cv_pdf = gr.File(label="Upload CV Anda (PDF)", file_types=[".pdf"]) | |
| score_button = gr.Button("β¨ Nilai CV Saya", variant="primary") | |
| with gr.Column(scale=2): | |
| output_score = gr.Markdown(label="Hasil Penilaian") | |
| score_button.click( | |
| fn=score_cv, | |
| inputs=[cv_pdf], | |
| outputs=[output_score], | |
| show_progress='full' | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |