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 | |
| import urllib.parse | |
| # --- 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-pro-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 generate_search_links(keywords): | |
| """Membuat tautan pencarian dari kata kunci untuk berbagai platform.""" | |
| if not keywords: | |
| return {} | |
| keywords_encoded = urllib.parse.quote_plus(keywords) | |
| keywords_hyphenated = keywords.lower().replace(" ", "-").replace("(", "").replace(")", "") | |
| links = { | |
| "LinkedIn": f"https://www.linkedin.com/jobs/search/?keywords={keywords_encoded}&location=Indonesia", | |
| "JobStreet": f"https://id.jobstreet.com/id/{keywords_hyphenated}-jobs/", | |
| "Glints": f"https://glints.com/id/opportunities/jobs/explore?keyword={keywords_encoded}", | |
| "Indeed": f"https://id.indeed.com/jobs?q={keywords_encoded}", | |
| "Google Jobs": f"https://www.google.com/search?q={keywords_encoded}+jobs+in+Indonesia&ibp=htl;jobs" | |
| } | |
| return links | |
| def format_output_markdown(analysis_json, links): | |
| """Menyusun laporan komprehensif DAN tautan pencarian menjadi satu output.""" | |
| jabatan = analysis_json.get("jabatan_ideal", "N/A") | |
| alasan_list = analysis_json.get("alasan_kecocokan", []) | |
| deskripsi_list = analysis_json.get("deskripsi_pekerjaan", []) | |
| potensi_list = analysis_json.get("potensi_karir", []) | |
| gaji = analysis_json.get("kisaran_gaji", {}) | |
| kelebihan_list = analysis_json.get("kelebihan_tambahan", []) | |
| alasan_md = "\n".join([f"- {item}" for item in alasan_list]) | |
| deskripsi_md = "\n".join([f"- {item}" for item in deskripsi_list]) | |
| potensi_md = "\n".join([f"- {item}" for item in potensi_list]) | |
| kelebihan_md = "\n".join([f"- {item}" for item in kelebihan_list]) | |
| gaji_md = f""" | |
| | Level | Estimasi Gaji / Bulan | | |
| | :--- | :--- | | |
| | **Junior** | {gaji.get("junior", "N/A")} | | |
| | **Mid-Level** | {gaji.get("mid_level", "N/A")} | | |
| | **Senior / Lead** | {gaji.get("senior", "N/A")} | | |
| > (Estimasi berdasarkan data pasar kerja di Indonesia) | |
| """ | |
| # ================================================================== | |
| # BAGIAN BARU: Membuat daftar tautan pencarian | |
| # ================================================================== | |
| markdown_links = "" | |
| for site, url in links.items(): | |
| markdown_links += f"- **[{site}]({url})**\n" | |
| return f""" | |
| ### π― {jabatan} | |
| *** | |
| #### π§© Alasan Kecocokan | |
| {alasan_md} | |
| #### πΌ Deskripsi Pekerjaan Umum | |
| Seorang **{jabatan.split('(')[0].strip()}** bertanggung jawab untuk: | |
| {deskripsi_md} | |
| #### π Potensi Jenjang Karir | |
| Dari posisi ini, Anda dapat berkembang menjadi: | |
| {potensi_md} | |
| #### π° Kisaran Gaji (Indonesia) | |
| {gaji_md} | |
| #### π Kelebihan Tambahan untuk Karirmu | |
| {kelebihan_md} | |
| *** | |
| ### π Klik untuk Mencari Lowongan Terbaru | |
| {markdown_links} | |
| """ | |
| def analyze_career_path(cv_file): | |
| """Fungsi utama pipeline: Analisis CV -> Buat Laporan -> Buat Link.""" | |
| if not API_CONFIGURED: | |
| raise gr.Error("API Key Gemini belum terkonfigurasi. Periksa Logs aplikasi.") | |
| if cv_file is None: | |
| raise gr.Error("Mohon upload file CV (PDF) Anda.") | |
| try: | |
| print("--- Memulai Proses Analisis Karir ---") | |
| 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 analisis karir ke Gemini...") | |
| prompt_analisis_karir = f""" | |
| Anda adalah seorang "Career Analyst AI" yang sangat berpengalaman. Tugas Anda adalah membaca teks CV, menganalisisnya secara mendalam, dan membuat laporan peluang karir yang komprehensif. | |
| Teks CV: | |
| --- | |
| {teks_cv} | |
| --- | |
| Lakukan analisis dan berikan output dalam format JSON yang ketat dengan struktur berikut: | |
| - "jabatan_ideal": Tentukan satu jabatan paling ideal untuk kandidat ini, termasuk spesialisasi jika ada (Contoh: "Frontend Developer (Web & Mobile)"). | |
| - "alasan_kecocokan": Buat sebuah array (list) berisi 3-4 poin utama MENGAPA kandidat ini sangat cocok untuk jabatan tersebut, hubungkan langsung dengan data dari CV. | |
| - "deskripsi_pekerjaan": Buat sebuah array (list) berisi 5 poin deskripsi pekerjaan umum untuk jabatan ideal tersebut. | |
| - "potensi_karir": Buat sebuah array (list) berisi 3-4 jalur pengembangan karir dari posisi ini. | |
| - "kisaran_gaji": Buat sebuah objek JSON berisi estimasi gaji bulanan di Indonesia untuk level "junior", "mid_level", dan "senior". Gunakan format string seperti "Rp 6 - 9 juta". | |
| - "kelebihan_tambahan": Buat sebuah array (list) berisi 1-2 poin saran atau kelebihan unik yang dimiliki kandidat dari CV-nya. | |
| Pastikan output hanya berupa JSON saja. | |
| """ | |
| generation_config = genai.types.GenerationConfig(response_mime_type="application/json") | |
| response = model.generate_content(prompt_analisis_karir, generation_config=generation_config) | |
| response_json = json.loads(response.text) | |
| print("β Laporan karir komprehensif berhasil diterima.") | |
| # ================================================================== | |
| # LANGKAH TAMBAHAN: Gunakan hasil analisis untuk membuat link | |
| # ================================================================== | |
| print("3. Membuat tautan pencarian dari hasil analisis...") | |
| keywords_from_analysis = response_json.get("jabatan_ideal", "") | |
| search_links = generate_search_links(keywords_from_analysis) | |
| print("4. Menyusun output akhir...") | |
| final_output = format_output_markdown(response_json, search_links) | |
| 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 Analis Peluang Karir Personal") | |
| gr.Markdown("Upload CV Anda, dan biarkan AI membuat laporan komprehensif tentang jalur karir terbaik Anda, lengkap dengan tautan pencarian kerja!") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| cv_pdf = gr.File(label="Upload CV Anda (PDF)", file_types=[".pdf"]) | |
| analyze_button = gr.Button("π Analisis Karir Saya", variant="primary") | |
| with gr.Column(scale=2): | |
| output_analysis = gr.Markdown(label="Laporan Peluang Karir Anda") | |
| analyze_button.click( | |
| fn=analyze_career_path, | |
| inputs=[cv_pdf], | |
| outputs=[output_analysis], | |
| show_progress='full' | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |