firmanaziz commited on
Commit
446c80c
Β·
verified Β·
1 Parent(s): 8f32e21

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -56
app.py CHANGED
@@ -8,33 +8,41 @@ import json
8
  import os
9
 
10
  # --- KONFIGURASI API KEY DARI HUGGING FACE SECRETS ---
11
- # Pastikan Anda sudah mengatur Secret bernama 'GEMINI_API_KEY' di repository Space Anda
12
  try:
13
- api_key = os.environ.get('GEMINI_API_KEY')
14
- genai.configure(api_key=api_key)
15
- print("βœ… Konfigurasi API berhasil!")
16
- model = genai.GenerativeModel('gemini-1.5-flash-latest')
17
- print(f"βœ… Model '{model.model_name}' berhasil diinisialisasi.")
18
- API_CONFIGURED = True
19
- except (TypeError, ValueError) as e:
20
- print(f"πŸ›‘ Error Konfigurasi API: Pastikan GEMINI_API_KEY sudah di-set di Secrets. Detail: {e}")
21
- API_CONFIGURED = False
22
  except Exception as e:
23
- print(f"πŸ›‘ Terjadi error tak terduga saat inisialisasi: {e}")
24
- API_CONFIGURED = False
25
 
26
  # --- FUNGSI-FUNGSI UTAMA ---
27
 
28
  def ekstrak_teks_dari_pdf(path_file_pdf):
29
- """Membuka file PDF dari path temporer dan mengekstrak teksnya."""
30
  try:
31
- dokumen = fitz.open(path_file_pdf)
32
- teks_lengkap = "".join(halaman.get_text() for halaman in dokumen)
33
- dokumen.close()
34
  return teks_lengkap
35
  except Exception as e:
36
  raise gr.Error(f"Gagal membaca file PDF: {e}")
37
 
 
 
 
 
 
 
 
 
 
 
 
38
  def format_output_markdown(parsed_json, hasil_analisis):
39
  """Menyusun hasil akhir menjadi string Markdown yang rapi."""
40
  nama = parsed_json.get('nama_lengkap', 'N/A')
@@ -43,58 +51,58 @@ def format_output_markdown(parsed_json, hasil_analisis):
43
  kekuatan = "\n".join([f"- {poin}" for poin in hasil_analisis.get('poin_kekuatan', [])])
44
  kelemahan = "\n".join([f"- {poin}" for poin in hasil_analisis.get('potensi_kelemahan', [])])
45
 
46
- markdown_output = f"""
47
  ### HASIL ANALISIS KECOCOKAN KANDIDAT
48
  **πŸ‘€ Nama Kandidat:** {nama}
49
  ***
50
  **⭐ Skor Kecocokan:** {skor} / 10
51
-
52
  **πŸ“ Ringkasan Analisis:**
53
  {ringkasan}
54
-
55
  **βœ… Poin Kekuatan Utama:**
56
  {kekuatan}
57
-
58
  **⚠️ Potensi Kelemahan / Pertanyaan Lanjutan:**
59
  {kelemahan}
60
  """
61
- return markdown_output
62
 
63
  def analyze_cv(cv_file, deskripsi_pekerjaan):
64
  """Fungsi utama yang dijalankan oleh Gradio saat tombol ditekan."""
65
  if not API_CONFIGURED:
66
- raise gr.Error("API Key Gemini belum terkonfigurasi. Hubungi pemilik aplikasi.")
67
  if cv_file is None or not deskripsi_pekerjaan.strip():
68
  raise gr.Error("Mohon upload file CV (PDF) dan isi deskripsi pekerjaan.")
69
 
70
- print("1. Memulai ekstraksi teks dari PDF...")
71
- teks_cv = ekstrak_teks_dari_pdf(cv_file.name) # cv_file.name adalah path temporer
72
- if not teks_cv:
73
- raise gr.Error("PDF kosong atau tidak dapat dibaca.")
74
-
75
- print("2. Memulai ekstraksi CV menjadi JSON...")
76
- 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."
77
- response_ekstraksi = model.generate_content(prompt_ekstraksi)
78
  try:
79
- parsed_json = json.loads(response_ekstraksi.text.strip().replace('```json', '').replace('```', ''))
80
- except (json.JSONDecodeError, AttributeError) as e:
81
- raise gr.Error(f"Gagal mem-parsing CV menjadi JSON. Error: {e}\n\nRespons Mentah:\n{response_ekstraksi.text}")
82
-
83
- print("3. Memulai analisis kecocokan...")
84
- prompt_analisis = f"""
85
- Anda adalah seorang Manajer Perekrutan senior. Analisis data kandidat (JSON) dan bandingkan dengan deskripsi pekerjaan.
86
- Deskripsi Pekerjaan: {deskripsi_pekerjaan}
87
- Data Kandidat (JSON): {json.dumps(parsed_json, indent=2)}
88
- Instruksi: Berikan output JSON dengan struktur: skor_kecocokan, ringkasan_analisis, poin_kekuatan, potensi_kelemahan. Output harus JSON saja.
89
- """
90
- response_analisis = model.generate_content(prompt_analisis)
91
- try:
92
- hasil_analisis = json.loads(response_analisis.text.strip().replace('```json', '').replace('```', ''))
93
- except (json.JSONDecodeError, AttributeError) as e:
94
- raise gr.Error(f"Gagal menganalisis kecocokan. Error: {e}\n\nRespons Mentah:\n{response_analisis.text}")
95
-
96
- print("4. Menyusun output...")
97
- return format_output_markdown(parsed_json, hasil_analisis)
 
 
 
 
 
 
 
 
 
98
 
99
  # --- MEMBUAT INTERFACE GRADIO ---
100
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
@@ -104,20 +112,22 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
104
  with gr.Row():
105
  with gr.Column(scale=1):
106
  cv_pdf = gr.File(label="1. Upload CV Anda (PDF)", file_types=[".pdf"])
107
- job_desc = gr.Textbox(
108
- lines=10,
109
- label="2. Masukkan Deskripsi Pekerjaan",
110
- placeholder="Contoh:\nPosisi: Senior Backend Engineer\n\nKami mencari seorang Senior Backend Engineer dengan pengalaman minimal 4 tahun..."
111
- )
112
- analyze_button = gr.Button("✨ Analisis Sekarang", variant="primary")
113
 
114
  with gr.Column(scale=2):
115
  output_analysis = gr.Markdown(label="Hasil Analisis")
 
 
 
116
 
117
  analyze_button.click(
118
  fn=analyze_cv,
119
  inputs=[cv_pdf, job_desc],
120
- outputs=[output_analysis]
 
121
  )
122
 
123
  if __name__ == "__main__":
 
8
  import os
9
 
10
  # --- KONFIGURASI API KEY DARI HUGGING FACE SECRETS ---
11
+ API_CONFIGURED = False
12
  try:
13
+ # PASTIKAN NAMA SECRET ANDA ADALAH 'GEMINI_API_KEY', BUKAN 'GOOGLE_API_KEY'
14
+ api_key = os.environ.get('GEMINI_API_KEY')
15
+ if api_key:
16
+ genai.configure(api_key=api_key)
17
+ model = genai.GenerativeModel('gemini-1.5-flash-latest')
18
+ API_CONFIGURED = True
19
+ print("βœ… Konfigurasi API dan model berhasil.")
20
+ else:
21
+ print("πŸ›‘ Secret 'GEMINI_API_KEY' tidak ditemukan.")
22
  except Exception as e:
23
+ print(f"πŸ›‘ Terjadi error saat inisialisasi: {e}")
 
24
 
25
  # --- FUNGSI-FUNGSI UTAMA ---
26
 
27
  def ekstrak_teks_dari_pdf(path_file_pdf):
 
28
  try:
29
+ with fitz.open(path_file_pdf) as dokumen:
30
+ teks_lengkap = "".join(halaman.get_text() for halaman in dokumen)
 
31
  return teks_lengkap
32
  except Exception as e:
33
  raise gr.Error(f"Gagal membaca file PDF: {e}")
34
 
35
+ def clean_and_parse_json(raw_text):
36
+ """Fungsi yang lebih andal untuk membersihkan dan mem-parsing JSON dari teks mentah."""
37
+ # Menemukan awal dan akhir blok JSON
38
+ start_index = raw_text.find('{')
39
+ end_index = raw_text.rfind('}')
40
+ if start_index == -1 or end_index == -1:
41
+ raise json.JSONDecodeError("Tidak ditemukan blok JSON valid dalam respons.", raw_text, 0)
42
+
43
+ json_str = raw_text[start_index:end_index+1]
44
+ return json.loads(json_str)
45
+
46
  def format_output_markdown(parsed_json, hasil_analisis):
47
  """Menyusun hasil akhir menjadi string Markdown yang rapi."""
48
  nama = parsed_json.get('nama_lengkap', 'N/A')
 
51
  kekuatan = "\n".join([f"- {poin}" for poin in hasil_analisis.get('poin_kekuatan', [])])
52
  kelemahan = "\n".join([f"- {poin}" for poin in hasil_analisis.get('potensi_kelemahan', [])])
53
 
54
+ return f"""
55
  ### HASIL ANALISIS KECOCOKAN KANDIDAT
56
  **πŸ‘€ Nama Kandidat:** {nama}
57
  ***
58
  **⭐ Skor Kecocokan:** {skor} / 10
59
+
60
  **πŸ“ Ringkasan Analisis:**
61
  {ringkasan}
62
+
63
  **βœ… Poin Kekuatan Utama:**
64
  {kekuatan}
65
+
66
  **⚠️ Potensi Kelemahan / Pertanyaan Lanjutan:**
67
  {kelemahan}
68
  """
 
69
 
70
  def analyze_cv(cv_file, deskripsi_pekerjaan):
71
  """Fungsi utama yang dijalankan oleh Gradio saat tombol ditekan."""
72
  if not API_CONFIGURED:
73
+ raise gr.Error("API Key Gemini belum terkonfigurasi. Periksa Logs aplikasi di Hugging Face.")
74
  if cv_file is None or not deskripsi_pekerjaan.strip():
75
  raise gr.Error("Mohon upload file CV (PDF) dan isi deskripsi pekerjaan.")
76
 
 
 
 
 
 
 
 
 
77
  try:
78
+ print("--- Memulai Proses Analisis ---")
79
+
80
+ print("1. Mengekstrak teks dari PDF...")
81
+ teks_cv = ekstrak_teks_dari_pdf(cv_file.name)
82
+ if not teks_cv:
83
+ raise gr.Error("PDF kosong atau tidak dapat dibaca.")
84
+ print("βœ… Teks berhasil diekstrak.")
85
+
86
+ print("2. Mengirim permintaan ekstraksi JSON ke Gemini...")
87
+ 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."
88
+ response_ekstraksi = model.generate_content(prompt_ekstraksi)
89
+ parsed_json = clean_and_parse_json(response_ekstraksi.text)
90
+ print("βœ… Ekstraksi JSON berhasil.")
91
+
92
+ print("3. Mengirim permintaan analisis kecocokan ke Gemini...")
93
+ 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."
94
+ response_analisis = model.generate_content(prompt_analisis)
95
+ hasil_analisis = clean_and_parse_json(response_analisis.text)
96
+ print("βœ… Analisis kecocokan berhasil.")
97
+
98
+ print("4. Menyusun output akhir...")
99
+ final_output = format_output_markdown(parsed_json, hasil_analisis)
100
+ print("--- Proses Selesai ---")
101
+ return final_output
102
+
103
+ except Exception as e:
104
+ print(f"πŸ›‘ ERROR DALAM FUNGSI ANALISIS: {e}")
105
+ raise gr.Error(f"Terjadi kesalahan: {e}")
106
 
107
  # --- MEMBUAT INTERFACE GRADIO ---
108
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
 
112
  with gr.Row():
113
  with gr.Column(scale=1):
114
  cv_pdf = gr.File(label="1. Upload CV Anda (PDF)", file_types=[".pdf"])
115
+ job_desc = gr.Textbox(lines=10, label="2. Masukkan Deskripsi Pekerjaan", placeholder="Contoh:\nPosisi: Senior Backend Engineer...")
116
+ with gr.Row():
117
+ clear_button = gr.ClearButton(value="Hapus Semua")
118
+ analyze_button = gr.Button("✨ Analisis Sekarang", variant="primary")
 
 
119
 
120
  with gr.Column(scale=2):
121
  output_analysis = gr.Markdown(label="Hasil Analisis")
122
+
123
+ # Menghubungkan tombol hapus ke semua komponen input dan output
124
+ clear_button.add([cv_pdf, job_desc, output_analysis])
125
 
126
  analyze_button.click(
127
  fn=analyze_cv,
128
  inputs=[cv_pdf, job_desc],
129
+ outputs=[output_analysis],
130
+ show_progress='full' # Menampilkan status proses
131
  )
132
 
133
  if __name__ == "__main__":