File size: 20,693 Bytes
b14496c
 
 
 
 
c93a052
d7c999c
 
365e16f
93fdff4
6d59268
cf0a188
 
83a70ce
36e0c32
3577a12
ab4cd3f
d9b382b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b14496c
5dae4c9
b14496c
 
 
 
 
 
 
 
d9b382b
5dae4c9
b14496c
 
 
 
3577a12
 
1afb483
 
 
3577a12
 
 
1afb483
3577a12
ab4cd3f
b14496c
f9f33fc
77d971f
b14496c
1afb483
 
 
 
 
e424366
1afb483
 
 
5fce78b
1afb483
 
5fce78b
 
1afb483
 
5fce78b
 
1afb483
 
5fce78b
8dea28a
5fce78b
8dea28a
1afb483
 
5fce78b
 
1afb483
 
8dea28a
 
1afb483
 
 
 
 
 
 
5fce78b
 
 
1afb483
5fce78b
 
 
1afb483
 
5fce78b
 
 
 
 
1afb483
9947b29
5fce78b
1afb483
 
5fce78b
1afb483
 
 
 
 
 
 
 
5fce78b
1afb483
 
 
5fce78b
 
8dea28a
1afb483
 
5fce78b
 
1afb483
 
 
36e0c32
 
 
2c980f5
1afb483
2c980f5
1afb483
 
 
36e0c32
1afb483
36e0c32
 
1afb483
2c980f5
 
 
 
 
36e0c32
2c980f5
36e0c32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b16468e
1afb483
 
8a7e328
 
5fc32ab
fac6fbc
 
9df4a7b
 
1afb483
 
 
8a7e328
2637922
1afb483
 
 
b14496c
d9b382b
f9f33fc
36e0c32
91919c3
36e0c32
cf0a188
91919c3
36e0c32
8664d07
cf0a188
c036f4a
 
36e0c32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
04e5c11
 
 
 
 
 
 
 
 
 
2c980f5
 
 
 
04e5c11
36e0c32
 
 
 
a6a52ce
36e0c32
77d971f
cf0a188
77d971f
cf0a188
 
83a70ce
cf0a188
 
2637922
cf0a188
 
 
 
 
 
 
 
 
 
d9b382b
cf0a188
 
caf5e05
83a70ce
cf0a188
83e3607
 
cf0a188
d9b382b
cf0a188
 
b14496c
9ce4500
d9b382b
9ce4500
cf0a188
d9b382b
 
9ce4500
cf0a188
 
 
8664d07
d9b382b
 
 
 
 
 
 
 
 
 
f9f33fc
b14496c
d9b382b
b14496c
f7daf2e
 
 
 
 
 
 
f720f34
f7daf2e
 
 
 
 
f720f34
b14496c
 
 
 
 
f7daf2e
 
d9b382b
3577a12
 
cf0a188
 
6d59268
d9b382b
c0dfddb
 
143d251
 
8a7e328
c0dfddb
143d251
 
 
 
c0dfddb
 
 
 
 
 
 
d9b382b
54e78d8
 
b14496c
 
f7daf2e
b14496c
99a3af4
2637922
fce9cbb
 
 
 
 
 
bb438b6
 
 
 
 
 
 
 
 
 
 
 
f9f33fc
 
 
 
 
5dae4c9
 
b5581dc
5dae4c9
1afb483
6e7765f
5dae4c9
 
d9b382b
ac0c572
d9b382b
 
 
54e78d8
2c980f5
99a3af4
529a353
99a3af4
36e0c32
99a3af4
8a7e328
99a3af4
529a353
54e78d8
 
dc5888d
a6a52ce
d9b382b
 
 
f9f33fc
54e78d8
d9b382b
b14496c
5dae4c9
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
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
import time
import random
from google.genai.types import GenerateContentConfig
import json
from PIL import ImageEnhance, ImageFilter


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

# Function to process a list of PDF files and convert them to images
def process_pdfs(pdf_files, dpi):
    """
    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=dpi)
        all_images.extend(images)

    return all_images

def preprocess_image(img):
    # Enhance contrast and sharpness
    img = ImageEnhance.Contrast(img).enhance(1.75)
    img = ImageEnhance.Sharpness(img).enhance(2.25)

    # Resize for optimal resolution (maintain aspect ratio)
    max_size = (1600, 1600)
    img.thumbnail(max_size, Image.LANCZOS)

    return img

# Function to analyze the extracted image using Google GenAI
def gemini_analysis(images, tanggal_berangkat, tanggal_pulang, tanggal_biometrik):
    client = genai.Client(api_key='AIzaSyBpviFHkaEF-GAjMMl28dIS1poikhBqq_w')
    # Define your prompt
    prompt = '''Anda adalah petugas pemeriksaan kelengkapan dan kesesuaian dokumen untuk pengajuan Visa. Berikut ini adalah alur kerja anda:
1. Ekstrak metadata dari setiap file/gambar yang diberikan berdasarkan data yang dibutuhkan masing-masing dokumen.
2. Dari setiap data yang anda dapat, bandingkan dengan persyaratan per dokumen dan periksa konsistensi data kunci antar dokumen.
3. Berikan analisis per dokumen termasuk kelengkapan dan konsistensi data antar dokumen.
4. Output pemrosesan berupa JSON dengan template di bawah.
5. Data boleh berupa fotokopi (bukan gambar asli).
---
List Dokumen wajib diserahkan seluruh peserta:
KTP:
Wajib ada untuk setiap peserta. Nama harus sama dengan paspor rekening koran, surat referensi bank, bukti kelahiran dan tertera di Kartu Keluarga. Jika ada perbedaan nama, wajib ada surat keterangan beda nama.

Paspor
Wajib ada untuk setiap peserta. Nama harus sama dengan KTP, rekening koran, surat referensi bank, bukti kelahiran dan tertera di kartu keluarga. Jika ada perbedaan nama, wajib ada surat keterangan beda nama.
Menampilkan nama, nomor paspor dan detail identitas lainnya, serta masa aktif minimal 6 bulan setelah tanggal pulang, dan sudah ada tanda tangan.

Kartu Keluarga (KK)
Wajib ada untuk setiap peserta. Nama harus sama dengan KTP, paspor, bukti kelahiran & dokumen lain. Jika ada perbedaan nama, wajib ada surat keterangan beda nama.
Kartu keluarga harus sudah terdapat barcode, dan terbit minimal tahun 2019.

REKENING KORAN PESERTA
Wajib ada untuk setiap peserta. Nama nasabah (pemilik) harus sama dengan KTP, paspor & dokumen lain. Jika ada perbedaan nama, wajib ada surat keterangan beda nama.
Sudah tertera cap dan logo bank, nama pserta dan nomor rekening, saldo minimal Rp 40 juta/orang.
Berikan peringatan di bagian analisis jika ada transaksi mencurigakan dengan nominal yang tidak wajar.
Wajib terupdate hingga 7 hari sebelum tanggal biometrik.

SURAT REFERENSI BANK
Wajib ada untuk setiap peserta. Nama nasabah (pemilik) harus sama dengan KTP, paspor dan dokumen lain.
Harus berupa surat resmi dari bank yang menjelaskan bahwa nama peserta tersebut adalah nasabah bank tersebut. Bukan rekening koran dan tidak bisa digantikan dengan rekening koran

PAS FOTO
Background putih, wajah terlihat 80%, alis terlihat, tidak berbayang. Pastikan anda tidak mendeteksi tempat meletakan foto sebagai Background.
Pasfoto bisa saja difoto dengan diletakkan di atas meja atau objek berwarnai lainnya.

BUKTI KELAHIRAN
Wajib ada untuk setiap peserta. Nama yang tertulis harus sama dengan KTP, PASPOR dan terdapat di Kartu Keluarga (KK).
Dapat digantikan dengan ijazah. Nama yang tertulis harus sama dengan KTP, PASPOR dan terdapat di Kartu Keluarga (KK).
---
Dokumen lain yang sifatnya kondisional
BUKTI NIKAH/CERAI
Jika status perkawinan di KTP tertulis "Kawin" maka wajib ada Bukti Nikah (misal buku nikah/akta nikah).
Jika status perkawinan di KTP tertulis "Cerai" maka wajib ada bukti cerai yang menyatakan cerai mati atau cerai hidup.
Jika status perkawinan di KTP tertulis "Belum Kawin" maka tidak perlu.

GUARANTEE LETTER
Merupakan surat yang berisi jaminan terkait perjalanan peserta antara lain tujuan perjalanan, tanggal berangkat, tanggal pulang, penanggung biaya, dan jaminan akan kembali ke Indonesia.
Guarantee Letter bisa berasal dari institusi pendidikan seperti sekolah/universitas, tempat peserta bekerja atau dari keluarga seperti suami atau orang tua.
Surat harus mencantumkan nama peserta yang sama dengan KTP, dalam bahasa Inggris, berisi tujuan, tanggal trip, siapa penanggung biaya, dan jaminan kembali ke Indonesia.

SURAT SPONSOR
Berbeda dengan Guarantee Letter. Merupakan surat yang menyatakan bahwa peserta disponsori oleh pihak ketiga khususnya sebagai penanggung biaya. Laporkan data pemberi sponsor.
Bisa berasal dari perusahaan tempat peserta bekerja atau sekolah/universitas. Jika surat sponsor dari sekolah/universitas,maka wajib ada KARTU PELAJAR/MAHASISWA
Surat harus mencantumkan nama peserta yang sama dengan KTP, dalam bahasa Inggris, berisi tujuan, tanggal trip, identitas penanggung biaya, dan jaminan kembali ke Indonesia.

SLIP GAJI
Jika peserta menyertakan SURAT SPONSOR / GUARANTEE LETTER dari perusahaan tempat peserta bekerja, peserta WAJIB menyertakan SLIP GAJI 3 BULAN terakhir sebelum tanggal berangkat.
Berupa pembayaran GAJI dari tempat kerja ke peserta. Pastikan NAMA PERUSAHAAN PEMBERI GAJI sama dengan Guarantee Letter dan/atau Surat Sponsor.

NIB/SIUP
Jika peserta merupakan pemilik usaha, wajib tertera nama pemilik usaha, nama usaha, alamat usaha, dan nomor NIB. Nama pemilik usaha harus sama dengan KTP, paspor dan dokumen lain.

KONTRAK FREELANCE
Jika peserta merupakan freelancer, maka wajib mencantumkan surat yang menunjukan bukti kontrak freelance. Harus tertera nama peserta yang sama dengan KTP dan dilengkapi bukti transaksi.

BUKTI TRANSAKSI
Jika peserta merupakan pekerja freelance, selain wajib menyertakan bukti KONTRAK FREELANCE, wajib menyertakan BUKTI TRANSAKSI. Bisa berupa invoice project atau dokumen terkait.

SURAT PENSIUN
Merupakan surat yang menyatakan sudah pensiun. Jika peserta menyertakan surat pensiun, wajib ada Guarantee Letter dari Keluarga.
maka wajib menyertakan surat pensiun yang berisi nama peserta, dan menyatakan sudah pensiun.

REKENING KORAN SPONSOR
Hanya wajib jika peserta disponsori dengan menyertakan SURAT SPONSOR.
Pastikan nama di rekening koran sponsor sama dengan pemberi sponsor yang tertulis di surat sponsor sebagai pemberi sponsor.
Wajib sudah tertera cap & logo bank, nama & nomor rekening, saldo minimal Rp 40 juta, wajib terupdate hingga. 7 hari sebelum tanggal

SURAT REFERENSI BANK SPONSOR
Hanya wajib jika peserta disponsori dengan menyertakan SURAT SPONSOR dan rekening koran dari pemberi sponsor.
Surat Bank Reference/Referensi Bank, merupakan surat yang menyatakan orang tersebut selaku pemberi sponsor merupakan nasabah bank terkait.

---
TEMPLATE OUTPUT JAWABAN DALAM FORMAT JSON:
    {
    "analysis":
        {
        "nama_dokumen_1": {"status": "VALID / INVALID / Tidak Perlu",
                           "description": "penjelasan detail",
        "nama_dokumen_2": {"status": "VALID / INVALID / Tidak Perlu",
                           "description": "penjelasan detail",
        "nama_dokumen_1": {"status": "VALID / INVALID / Tidak Perlu",
                           "description": "penjelasan detail",
        },
    "summary": "...",
    "invalid_item": ["nama_dokumen_1", "nama_dokumen_2"],
    "notice_msg": "...",
        "form_filling": {"Surname":"...",
                     "First Name":"...",
                     "Date of Birth":"...",
                     "Place of Birth":"...",
                     "Nationality":"...",
                     "Sex":"...",
                     "Mariage Status":"...",
                     "Passport Number":"...",
                     "Passport Expiry Date":"...",
                     "National Identity Number":"...",
                     "Travel Document Type":"...",
                     "Travel Document Number":"...",
                     "Date Of Issue":"...",
                     "Valid Until":"...",
                     "Issued Country":"...",
                     "Applicant's Home Address":"...",
                     "Applicant's Telephone Number":"...",
                     "Applicant's Email Address":"...",
                     "Current Occupation":"...",
                     "Employer/Educational Address":"...",
                     "Journey Purpose":"...",
                     "Destination":"...",
                     "Duration":"...",
                     "Number of Entries":"...", # single or multiple
                     "already has fingerprint":"...",
                     "inviting person from each destination":"...",
                     "inviting person email address":"...",
                     "Traveling and living cost covered by":"..."
                     } # Hanya isi yang ada di dokumen saja. Jika tidak ada, tidak usah diisi atau dimunculkan. Anda juga bisa menambahkan data penting lain yang mungkin belum ditulis di atas.
    }
---
    TEMPLATE PESAN PEMBERITAHUAN (notice_msg):
    Berikut kami informasikan kekurangan dokumen yang *WAJIB* dibawa saat biometric visa schengen nanti ya :
    1. Pas Foto (Wajib berukuran 3,5 x 4,5 cm dan diserahkan di lokasi biometrik)
    2. ...
    3. ...
    4. ...
    5. ...
---
    ## cantumkan dokumen lain yang masih Invalid beserta perbaikan yang diperlukan.
---
    TEMPLATE SUMMARY:
    List Dokumen yang sudah valid: .... \n
    List Dokumen yang invalid : ... \n
    Analisis detail keseluruhan: ... \n  # sebutkan detail analisa masing-masing dokumen, apa yang menyebabkan dokumen-dokumen tersebut invalid
    Rangkuman data peserta: ... # jelaskan rangkuman data peserta yang ada di dokumen seperti nama peserta, apakah disponsori, siapa yang mensponsori, tujuan, tanggal berangkat dan pulang dan data penting lainnya.
    '''
    # Perform document analysis
    prompt_with_date = f'Tanggal Berangkat={tanggal_berangkat}. Tanggal pulang={tanggal_pulang}. Tanggal Biometrik={tanggal_biometrik}\n\n{prompt}'
    response = client.models.generate_content(
        model="gemini-2.0-flash-lite",
        contents=[prompt_with_date] + images,
        config=GenerateContentConfig(
              temperature=0.1,
              response_mime_type="application/json"
            )
      )
    pre_token_usage = response.usage_metadata.total_token_count
    token_usage = pre_token_usage*5/1000
    raw_output = response.text
    # βœ… Inisialisasi variabel default
    analysis = {}
    summary = ""
    invalid_list = []
    notice_msg = ""
    form_filling = {}
    try:
        parsed_output = json.loads(response.text)
        analysis = parsed_output.get("analysis", {})
        analysis_str = json.dumps(analysis, indent=2, ensure_ascii=False)
        summary = parsed_output.get("summary", "")
        invalid_list = parsed_output.get("invalid_item", [])
        invalid_list_str = json.dumps(invalid_list, indent=2, ensure_ascii=False)
        notice_msg = parsed_output.get("notice_msg", "")
        notice_msg_ending = '''
Note :
β–ͺ️ Semua dokumen di atas mohon difotokan terlebih dahulu agar dapat diperiksa kembali yaa kak πŸ™πŸ»
β–ͺ️ Selama visa belum keluar, rekening harus tetap stabil dengan saldo minimal 40juta rupiah/ orang. Tidak berlaku rekening deposito. Rekening koran wajib tertera nama, nomor rekening, nama dan logo/ stamp bank.

Demikian yang kami sampaikan. Terima kasih atas kerja samanya πŸ€—

Wishtravelers WT
PT WISATA IMPIAN UNIVERSAL
'''
        notice_msg = '''🌻 *(WISH TRAVELERS) UPDATE DOKUMEN PENGAJUAN VISA* 🌻
Penting, mohon di baca sampai habis ya kak πŸ™πŸ»
Ini adalah nomor sistem, tidak dapat membalas pesan. Apabila ada pertanyaan, silahkan hubungi mimin WT wa.me/6282123038484
Halo Kak selamat malam 😊
''' + notice_msg + notice_msg_ending
        form_filling = parsed_output.get("form_filling", "")
        form_filling_str = json.dumps(form_filling, indent=2, ensure_ascii=False)
    except Exception as e:
        print(f"Error parsing JSON: {e}")
    return raw_output, analysis_str, summary, invalid_list_str, notice_msg, form_filling_str, token_usage

def process_and_zip_all_images(images, zip_name="All_PDF_Docs.zip"):
    # Inisialisasi Gemini client
    client = genai.Client(api_key='AIzaSyBpviFHkaEF-GAjMMl28dIS1poikhBqq_w')

    # Prompt untuk klasifikasi nama file
    prompt_3 = '''Anda adalah asisten yang membantu menamai file gambar dokumen.
Tugas Anda adalah mengidentifikasi jenis dokumen pada gambar ini dan memberikan nama file yang sesuai.
Jawaban Anda *harus* berupa *salah satu* nama file dari daftar berikut:
['paspor', 'fotokopipaspor', 'pasfoto', 'kartukeluarga', 'buktinikah', 'bukticerai', 'ktp', 'buktikelahiran', 'suratsponsor', 'guaranteeletter', 'suratkerja', 'NIB' 'SIUP', 'suratjaminanstaff', 'suratsekolah', 'kontrakkerja', 'suratpensiun', 'rekeningkoran', 'slipgaji', 'suratreferensibank']
Jawaban Anda *hanya* boleh berupa teks yang *persis sama* dengan salah satu item dalam daftar tersebut.
Jangan tambahkan penjelasan, tanda kutip, titik, atau teks tambahan lainnya.
Contoh:
Gambar : [tampak gambar KTP]
Output: KTP
Gambar: [gambar sebenarnya]
Output:
'''

    # Step 1: Klasifikasi & Penamaan
    renamed_images = []
    for image in images:
        response = client.models.generate_content(
            model="gemini-2.0-flash-lite",
            contents=[prompt_3, image],
            config=GenerateContentConfig(
              temperature=0.1,
              top_p=0.1
            )
        )
        filename = response.text.strip().lower()
        renamed_images.append({"image": image, "filename": filename})

    # Step 2: Kelompokkan berdasarkan nama file (tanpa ekstensi)
    grouped = {}
    for item in renamed_images:
        name = os.path.splitext(item["filename"])[0]
        grouped.setdefault(name, []).append(item["image"])

    # Step 3: Simpan ke PDF dan masukkan ke ZIP
    temp_dir = tempfile.mkdtemp()
    zip_path = os.path.join(tempfile.gettempdir(), zip_name)

    with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zipf:
        for doc_name, images in grouped.items():
            images_rgb = [img.convert("RGB") for img in images]
            pdf_path = os.path.join(temp_dir, f"{doc_name}.pdf")
            if len(images_rgb) == 1:
                images_rgb[0].save(pdf_path, save_all=True)
            else:
                images_rgb[0].save(pdf_path, save_all=True, append_images=images_rgb[1:])
            zipf.write(pdf_path, arcname=f"{doc_name}.pdf")
    return zip_path

def main_process(files, tanggal_berangkat, tanggal_pulang, tanggal_biometrik, dpi):
    all_images = []
    image_paths_for_zip = []

    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], dpi)
                    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], dpi)
            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.")

    # Generate summary from images
    preprocessed_images = [preprocess_image(img) for img in all_images]
    raw_output, analysis_str, summary, invalid_list_str, notice_msg, form_filling_str, token_usage = gemini_analysis(preprocessed_images, tanggal_berangkat, tanggal_pulang, tanggal_biometrik)
    rdf = random.randint(5, 10)
    time.sleep(rdf)

    # Create DOCX for summary output
    doc = Document()
    doc.add_heading("Visa Document Check Summary", level=1)
    doc.add_paragraph(f"Tanggal Berangkat: {tanggal_berangkat}")
    doc.add_paragraph(f"Tanggal Pulang: {tanggal_pulang}")
    for line in analysis_str.split("\n"):
        doc.add_paragraph(line)
    doc.add_paragraph(f"Summary: {summary}\n\n")
    doc.add_paragraph(f"Invalid List: {invalid_list_str}\n\n")
    doc.add_paragraph(f"Notice Message: {notice_msg}\n\n")
    doc.add_paragraph(f"Form Filling: {form_filling_str}\n\n")
    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)

    # Filtering the file
    # zip_file_path = process_and_zip_all_images(all_images, zip_name=f'All_PDF_Docs_{base_name}.zip')
    return temp_docx_path, form_filling_str, invalid_list_str, analysis_str, summary, notice_msg, token_usage


# Gradio UI update: add ".zip" to accepted file types
with gr.Blocks() as demo:
    gr.Markdown("# 🧠 Noura the Document Checker ✈️ ")
    gr.Markdown("Last Updated: June 10 2025")

    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"
        )
        tanggal_biometrik = gr.Textbox(
            label="Tanggal Biometrik",
            placeholder="Masukan Tanggal Biometrik",
            type="text"
        )
        dpi_slider = gr.Slider(
            minimum=100,
            maximum=400,
            step=25,
            label="Adjust DPI (100 - 400, Ξ”=25, default=350)",
            value=350
        )


    run_btn = gr.Button("πŸƒ Run Analysis")

    with gr.Row():
        download_output_docx = gr.File(label="πŸ“₯ Download Summary as DOCX", visible=True)
        # download_valid_zip = gr.File(label="πŸ“₯ Download all PDF document in zip", visible=True)

    gr.Markdown("## πŸ“ FORM FILLING RESULT")
    form_filling_output = gr.Textbox(label="πŸ“ FORM FILLING RESULT", lines=20)
    gr.Markdown("## πŸ“ INVALID DOCUMENT LIST")
    invalid_list_output = gr.Textbox(label="πŸ“ INVALID DOCUMENT LIST", lines=5)
    gr.Markdown("## πŸ“ SUMMARY")
    summary_output = gr.Textbox(label="πŸ“ SUMMARY OUTPUT", lines=5)
    gr.Markdown("## πŸ“ NOTIFICATION MESSAGE")
    notice_msg = gr.Textbox(label="πŸ“ NOTIFICATION MSG", lines=10)
    gr.Markdown("## πŸ“ PER DOCUMENT ANALYSIS")
    raw_output = gr.Textbox(label="πŸ“ PER DOCUMENT ANALYSIS", lines=20)
    gr.Markdown("Token cost in IDR")
    token_usage = gr.Textbox(label="Token cost in IDR", lines=5)

    run_btn.click(
        fn=main_process,
        inputs=[file_input, tanggal_berangkat, tanggal_pulang, tanggal_biometrik, dpi_slider],
        outputs=[download_output_docx, form_filling_output, invalid_list_output, raw_output, summary_output, notice_msg, token_usage]
    )

demo.launch(debug=True)