Carson commited on
Commit
76ab94d
Β·
1 Parent(s): 9fec855

enabled multi document upload

Browse files
Files changed (1) hide show
  1. app.py +83 -28
app.py CHANGED
@@ -229,14 +229,14 @@ def create_app() -> gr.Blocks:
229
 
230
  with gr.Tab("πŸ“ Upload file"):
231
  file_input = gr.File(
232
- label="Upload .txt, .pdf, or .docx",
233
  file_types=[".txt", ".pdf", ".docx"],
234
- file_count="single",
235
  )
236
  gr.Markdown(
237
- "<small style='color:#888'>PDF and DOCX are parsed locally; "
238
- "only the extracted text is sent to the backend. "
239
- "Indexing starts automatically once a file is selected.</small>"
240
  )
241
  upload_status = gr.Markdown("")
242
 
@@ -340,37 +340,92 @@ def create_app() -> gr.Blocks:
340
  outputs=[load_paste_btn, paste_status] + INDEX_OUTPUTS,
341
  )
342
 
343
- def _upload_action(file_obj):
344
- yield "πŸ“– Parsing file…", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
345
  path = resolve_file_path(file_obj)
 
346
  if not path:
347
- result = _after_index(False, None, None, "", "❌ No file selected.")
348
- yield ("❌ No file selected.",) + result
349
- return
350
  if not os.path.exists(path):
351
- result = _after_index(False, None, None, "", "❌ File not found.")
352
- yield ("❌ File not found.",) + result
353
- return
354
- file_size = os.path.getsize(path)
355
- if file_size > MAX_FILE_SIZE:
356
- msg = f"❌ File too large. Max {format_file_size(MAX_FILE_SIZE)}."
357
- result = _after_index(False, None, None, "", msg)
358
- yield (msg,) + result
359
- return
360
  text_content, err = extract_text_from_file(path)
361
  if err or not text_content or not text_content.strip():
362
- err_msg = err or "❌ File is empty or no extractable text found."
363
- result = _after_index(False, None, None, "", err_msg)
364
- yield (err_msg,) + result
365
- return
366
- yield "πŸ“€ Uploading & indexing…", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
367
- base = os.path.basename(path)
368
  stem, _ext = os.path.splitext(base)
369
  filename = f"{stem}.txt"
370
  success, doc_id, char_count, msg = upload_text_to_backend(text_content, filename)
371
- result = _after_index(success, doc_id, char_count, text_content if success else "", msg)
372
- final_status = f"βœ… Indexed **{doc_id}** ({char_count:,} chars)." if success else msg
373
- yield (final_status,) + result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
 
375
  file_input.upload(
376
  fn=_upload_action,
 
229
 
230
  with gr.Tab("πŸ“ Upload file"):
231
  file_input = gr.File(
232
+ label="Upload .txt, .pdf, or .docx (one or many)",
233
  file_types=[".txt", ".pdf", ".docx"],
234
+ file_count="multiple",
235
  )
236
  gr.Markdown(
237
+ "<small style='color:#888'>Drop one or more files at once. "
238
+ "PDF and DOCX are parsed locally; only the extracted text is sent. "
239
+ "Indexing starts automatically.</small>"
240
  )
241
  upload_status = gr.Markdown("")
242
 
 
340
  outputs=[load_paste_btn, paste_status] + INDEX_OUTPUTS,
341
  )
342
 
343
+ def _process_one_file(file_obj):
344
+ """Parse + upload one file. Returns (ok, doc_id, char_count, text, msg)."""
345
  path = resolve_file_path(file_obj)
346
+ base = os.path.basename(path) if path else "?"
347
  if not path:
348
+ return False, None, None, "", f"❌ {base}: no file selected."
 
 
349
  if not os.path.exists(path):
350
+ return False, None, None, "", f"❌ {base}: not found."
351
+ if os.path.getsize(path) > MAX_FILE_SIZE:
352
+ return False, None, None, "", f"❌ {base}: too large (max {format_file_size(MAX_FILE_SIZE)})."
 
 
 
 
 
 
353
  text_content, err = extract_text_from_file(path)
354
  if err or not text_content or not text_content.strip():
355
+ return False, None, None, "", err or f"❌ {base}: empty or unreadable."
 
 
 
 
 
356
  stem, _ext = os.path.splitext(base)
357
  filename = f"{stem}.txt"
358
  success, doc_id, char_count, msg = upload_text_to_backend(text_content, filename)
359
+ if success:
360
+ return True, doc_id, char_count, text_content, f"βœ… {doc_id} ({char_count:,} chars)"
361
+ return False, None, None, "", f"❌ {filename}: {msg}"
362
+
363
+ def _upload_action(file_objs):
364
+ """Handle one or many files. Yields progress then a final summary."""
365
+ # Normalise to a list β€” Gradio may pass a single file or a list.
366
+ if file_objs is None:
367
+ files = []
368
+ elif isinstance(file_objs, list):
369
+ files = file_objs
370
+ else:
371
+ files = [file_objs]
372
+
373
+ if not files:
374
+ yield ("❌ No files selected.",) + _after_index(
375
+ False, None, None, "", "❌ No files selected.",
376
+ )
377
+ return
378
+
379
+ total = len(files)
380
+ line_log: list = []
381
+ newly_indexed: list = [] # doc_ids of successful uploads
382
+ last_text = ""
383
+
384
+ for i, fobj in enumerate(files, start=1):
385
+ base = os.path.basename(resolve_file_path(fobj) or "?")
386
+ yield (
387
+ f"πŸ“– [{i}/{total}] Parsing **{base}**…\n\n" + "\n".join(line_log),
388
+ gr.update(), gr.update(), gr.update(),
389
+ gr.update(), gr.update(), gr.update(),
390
+ )
391
+ ok, doc_id, _cc, text_content, msg = _process_one_file(fobj)
392
+ line_log.append(msg)
393
+ if ok:
394
+ newly_indexed.append(doc_id)
395
+ last_text = text_content
396
+ yield (
397
+ f"πŸ“€ [{i}/{total}] Done.\n\n" + "\n".join(line_log),
398
+ gr.update(), gr.update(), gr.update(),
399
+ gr.update(), gr.update(), gr.update(),
400
+ )
401
+
402
+ # Final state β€” select ALL successfully uploaded docs (not just the last).
403
+ ok_count = len(newly_indexed)
404
+ fail_count = total - ok_count
405
+
406
+ if ok_count:
407
+ all_docs = fetch_indexed_docs()
408
+ selected = [d for d in newly_indexed if d in all_docs]
409
+ if not selected:
410
+ selected = all_docs[:] # fallback
411
+ final_tuple = (
412
+ True,
413
+ all_docs,
414
+ selected,
415
+ update_index_display(all_docs),
416
+ last_text,
417
+ gr.update(choices=all_docs, value=selected),
418
+ )
419
+ else:
420
+ final_tuple = _after_index(False, None, None, "", "❌ All uploads failed.")
421
+
422
+ header = (
423
+ f"βœ… Indexed {ok_count}/{total} file{'s' if total != 1 else ''}."
424
+ if fail_count == 0
425
+ else f"⚠️ Indexed {ok_count}/{total} ({fail_count} failed)."
426
+ )
427
+ final_status = header + "\n\n" + "\n".join(line_log)
428
+ yield (final_status,) + final_tuple
429
 
430
  file_input.upload(
431
  fn=_upload_action,