File size: 5,903 Bytes
4f4d6f1
 
 
 
 
 
 
 
 
 
446c80c
4f4d6f1
446c80c
 
 
d76087e
446c80c
 
 
 
4f4d6f1
446c80c
4f4d6f1
 
 
 
 
446c80c
 
4f4d6f1
 
 
 
446c80c
720a543
446c80c
 
 
 
 
 
 
 
720a543
 
 
 
 
 
 
 
 
 
 
 
 
 
446c80c
720a543
 
4f4d6f1
720a543
 
 
 
446c80c
720a543
 
4f4d6f1
 
720a543
 
4f4d6f1
446c80c
720a543
 
4f4d6f1
 
720a543
446c80c
 
 
 
 
 
 
720a543
 
 
 
 
 
 
 
446c80c
720a543
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446c80c
 
 
 
720a543
446c80c
4f4d6f1
 
 
720a543
 
4f4d6f1
 
 
720a543
 
4f4d6f1
 
720a543
 
 
 
 
 
 
4f4d6f1
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# 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()