Spaces:
Sleeping
Sleeping
| import os | |
| from pdf2image import convert_from_path | |
| from datetime import date | |
| from PIL import Image | |
| import gradio as gr | |
| from google import genai | |
| import zipfile | |
| import tempfile | |
| os.system("apt-get install poppler-utils") | |
| import datetime | |
| from docx import Document | |
| # Function to process a list of PDF files and convert them to images | |
| def process_pdfs(pdf_files): | |
| """ | |
| Process a list of PDF files, convert each to images, and return all images. | |
| """ | |
| all_images = [] | |
| for pdf_file in pdf_files: | |
| if not os.path.isfile(pdf_file): | |
| raise ValueError(f"File {pdf_file} does not exist.") | |
| images = convert_from_path(pdf_file, dpi=200) # Convert PDF pages to images | |
| all_images.extend(images) | |
| return all_images | |
| # Function to analyze the extracted image using Google GenAI | |
| def gemini_analysis(images, tanggal_berangkat, tanggal_pulang): | |
| """ | |
| Analyze the extracted image using Google GenAI. | |
| """ | |
| # Initialize the GenAI client (make sure the API key is set properly) | |
| today = date.today() | |
| client = genai.Client(api_key=os.getenv("GOOGLE_API_KEY")) | |
| # Define your prompt | |
| prompt = f'''Anda bertugas memvalidasi kesesuaian dan konsistensi data dari dokumen individual berdasarkan syarat Visa di bawah. Cukup fokus pada syarat yang diberikan tanpa mengambil referensi lain. | |
| Gunakan tanggal hari ini ({today}) sebagai tanggal pemeriksaan visa (bukan pengajuan). Gunakan {tanggal_berangkat} sebagai tanggal berangkat, dan {tanggal_pulang} sebagai tanggal kepulangan. | |
| Pastikan data seperti nama, tanggal, tujuan dan lainnya sama/konsisten antar dokumen. Di akhir, berikan kesimpulan dari pemeriksaan dokumen. Jika ada yang kurang lengkap atau tidak valid, berikan pesan pemberitahuan untuk pemilik data tersebut terkait data yang kurang atau tidak sesuai. | |
| --- | |
| DAFTAR SYARAT DOKUMEN YANG WAJIB DIPERIKSA: | |
| 1. Paspor | |
| Asli, aktif min. 6 bulan setelah tanggal kepulangan | |
| Ada tanda tangan pemilik | |
| Lampirkan paspor lama jika ada visa perjalanan sebelumnya | |
| Copy paspor sponsor jika disponsori | |
| 2. Fotokopi Paspor | |
| Halaman depan & tanda tangan | |
| Semua visa perjalanan sebelumnya jika ada (terutama 5 tahun terakhir) | |
| Jika tidak ada, cukup beritahu bahwa Fotokopi Paspor belum ada. | |
| 3. Pas Foto | |
| Ukuran kurang lebih 3.5 x 4.5 cm, background putih | |
| Wajah terlihat 80%, alis tidak tertutup, tidak pakai softlens, tidak berbayang | |
| 4. Kartu Keluarga (KK) | |
| Minimal versi 2019 atau berbarcode | |
| Harus sesuai status (nikah/cerai/anak-anak β lampirkan dokumen pendukung) | |
| Harus ditranslate untuk VFS Germany | |
| 5. Akte Nikah/Bukti nikah | |
| Halaman biodata suami dan istri saja | |
| Jika istri ikut, wajib melampirkan surat izin suami | |
| 6. KTP | |
| Nama harus sama dengan paspor dan tercantum pada Kartu Keluarga (KK) | |
| Jika nama berbeda maka wajib lampirkan surat beda nama | |
| 7. Akta Kelahiran / Surat Kelahiran / Ijazah | |
| Wajib jika anak-anak atau peserta disponsori oranglain | |
| 8. Surat Sponsor (Guarantee Letter) | |
| Dalam Bahasa Inggris | |
| Tujuan negara, tanggal trip | |
| Siapa yang menanggung biaya | |
| Wajib ada tertulis menjamin akan kembali ke Indonesia | |
| 9. Status Pekerjaan | |
| Pegawai: Surat kerja + Slip gaji 3 bulan | |
| Pemilik usaha: NIB/SIUP + Surat jaminan staf | |
| Pelajar: Surat sekolah/universitas + kartu pelajar | |
| Freelancer/onlineshop: Kontrak kerja & 5 bukti transaksi | |
| Pensiun: Surat pensiun + guarantee dari keluarga | |
| 10. Rekening Koran (3 bulan) | |
| Atas nama pribadi & sponsor | |
| Cap & logo bank, nama, nomor rekening | |
| Saldo stabil min. Rp 35 juta/orang | |
| 11. Slip Gaji | |
| 3 bulan terakhir | |
| Jika suami lengkap β istri cukup lampirkan rekening koran suami | |
| --- | |
| FORMAT JAWABAN UNTUK SETIAP DOKUMEN YANG DIUPLOAD: | |
| - Jenis Dokumen : (jenis dokumen) | |
| - Status : (Valid / Perlu cek ulang / Tidak diperlukan / Tidak ada) | |
| - Catatan : (Catatan singkat terkait isi dokumen) | |
| --- | |
| FORMAT KESIMPULAN: | |
| - List dokumen yang sudah valid: ... | |
| - List dokumen yang perlu pemeriksaan ulang: ... | |
| - List dokumen yang tidak ada/belum lengkap: ... | |
| --- | |
| TEMPLATE PESAN PEMBERITAHUAN: | |
| P, dokumen lau masih kurang .... | |
| ''' | |
| # Perform content generation using Google Gemini (images passed as files) | |
| response = client.models.generate_content( | |
| model="gemini-1.5-flash", | |
| contents=[prompt] + images # Pass prompt and image files | |
| ) | |
| return response.text | |
| def extract_zip_and_collect_files(zip_file_path): | |
| """ | |
| Extract zip file to a temp directory and return list of pdf/image file paths inside. | |
| """ | |
| temp_dir = tempfile.mkdtemp() | |
| with zipfile.ZipFile(zip_file_path, 'r') as zip_ref: | |
| zip_ref.extractall(temp_dir) | |
| # Collect all pdf and image files in extracted folder recursively | |
| collected_files = [] | |
| for root, _, files in os.walk(temp_dir): | |
| for f in files: | |
| if f.lower().endswith(('.pdf', '.jpg', '.jpeg', '.png')): | |
| collected_files.append(os.path.join(root, f)) | |
| return collected_files | |
| def main_process(files, tanggal_berangkat, tanggal_pulang): | |
| all_images = [] | |
| for file in files: | |
| file_path = file.name if hasattr(file, 'name') else file | |
| if file_path.lower().endswith('.zip'): | |
| extracted_files = extract_zip_and_collect_files(file_path) | |
| for extracted_file in extracted_files: | |
| if extracted_file.lower().endswith('.pdf'): | |
| images = process_pdfs([extracted_file]) | |
| all_images.extend(images) | |
| elif extracted_file.lower().endswith(('.jpg', '.jpeg', '.png')): | |
| image = Image.open(extracted_file) | |
| all_images.append(image) | |
| elif file_path.lower().endswith('.pdf'): | |
| images = process_pdfs([file_path]) | |
| all_images.extend(images) | |
| elif file_path.lower().endswith(('.jpg', '.jpeg', '.png')): | |
| image = Image.open(file_path) | |
| all_images.append(image) | |
| else: | |
| raise ValueError(f"File {file_path} is not a valid image, PDF, or ZIP.") | |
| summary = gemini_analysis(all_images, tanggal_berangkat, tanggal_pulang) | |
| # π Create DOCX with custom filename | |
| doc = Document() | |
| doc.add_heading("Visa Document Check Summary", level=1) | |
| for line in summary.split("\n"): | |
| doc.add_paragraph(line) | |
| # Use first input file name for filename | |
| first_file = files[0] | |
| first_filename = os.path.basename(first_file.name if hasattr(first_file, 'name') else first_file) | |
| base_name = os.path.splitext(first_filename)[0] | |
| docx_filename = f"summary_{base_name}.docx" | |
| temp_docx_path = os.path.join(tempfile.gettempdir(), docx_filename) | |
| doc.save(temp_docx_path) | |
| return summary, temp_docx_path | |
| # Save to DOCX | |
| doc = Document() | |
| doc.add_heading("Visa Document Check Summary", level=1) | |
| for line in summary.split("\n"): | |
| doc.add_paragraph(line) | |
| temp_docx_path = os.path.join(tempfile.gettempdir(), f"summary_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.docx") | |
| doc.save(temp_docx_path) | |
| return summary, temp_docx_path | |
| # Gradio UI update: add ".zip" to accepted file types | |
| with gr.Blocks() as demo: | |
| gr.Markdown("## π§ Document Analyzer using Gemini AI for Visa Document Checking βοΈ ") | |
| file_input = gr.File( | |
| label="Upload PDFs, Images or ZIP files (Multiple Supported)", | |
| file_types=[".pdf", ".jpg", ".jpeg", ".png", ".zip"], | |
| file_count="multiple" | |
| ) | |
| with gr.Row(): | |
| tanggal_berangkat = gr.Textbox( | |
| label="Tanggal Keberangkatan", | |
| placeholder="Masukan Tanggal Keberangkatan", | |
| type="text" | |
| ) | |
| tanggal_pulang = gr.Textbox( | |
| label="Tanggal Kepulangan", | |
| placeholder="Masukan Tanggal Kepulangan", | |
| type="text" | |
| ) | |
| with gr.Row(): | |
| run_btn = gr.Button("π Run Analysis") | |
| download_output = gr.File(label="π₯ Download Summary as DOCX", visible=True) | |
| output = gr.Textbox(label="π Summary Result", lines=20) | |
| run_btn.click(fn=main_process, | |
| inputs=[file_input, tanggal_berangkat, tanggal_pulang], | |
| outputs=[output, download_output]) | |
| demo.launch() |