Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -13,7 +13,7 @@ import time
|
|
| 13 |
import sqlite3
|
| 14 |
import hashlib
|
| 15 |
|
| 16 |
-
print("Initializing OmniParse AI Enterprise Terminal v5.
|
| 17 |
|
| 18 |
# ─── DATABASE INITIALIZATION ────────────────────────────────────────────────
|
| 19 |
DB_FILE = "omniparse.db"
|
|
@@ -59,6 +59,9 @@ def db_register(username, password):
|
|
| 59 |
return json.dumps({"status": "ERROR", "message": "Zadej jméno i heslo, nesmí to bejt prázdný!"})
|
| 60 |
|
| 61 |
username = username.strip().lower()
|
|
|
|
|
|
|
|
|
|
| 62 |
try:
|
| 63 |
conn = sqlite3.connect(DB_FILE)
|
| 64 |
cursor = conn.cursor()
|
|
@@ -92,7 +95,7 @@ def db_login(username, password):
|
|
| 92 |
|
| 93 |
user_id, stored_hash, salt = row
|
| 94 |
|
| 95 |
-
# Podpora pro session refresh
|
| 96 |
if password == "SESSION_REFRESH":
|
| 97 |
cursor.execute("SELECT COUNT(*), SUM(fields_count) FROM invoices WHERE user_id = ?", (user_id,))
|
| 98 |
stats = cursor.fetchone()
|
|
@@ -215,9 +218,11 @@ def parse_invoice(image_b64: str, username: str):
|
|
| 215 |
|
| 216 |
invoice_fp = extracted_fields.get("INVOICE_NUMBER", extracted_fields.get("TOTAL", "UNKNOWN"))
|
| 217 |
|
| 218 |
-
# Kontrola duplicity v rámci přihlášeného uživatele
|
| 219 |
is_duplicate = False
|
| 220 |
-
|
|
|
|
|
|
|
| 221 |
try:
|
| 222 |
conn = sqlite3.connect(DB_FILE)
|
| 223 |
cursor = conn.cursor()
|
|
@@ -253,10 +258,9 @@ def parse_invoice(image_b64: str, username: str):
|
|
| 253 |
"extracted_data": extracted_fields,
|
| 254 |
}
|
| 255 |
|
| 256 |
-
# Zápis do DB
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
if username:
|
| 260 |
try:
|
| 261 |
conn = sqlite3.connect(DB_FILE)
|
| 262 |
cursor = conn.cursor()
|
|
@@ -270,19 +274,21 @@ def parse_invoice(image_b64: str, username: str):
|
|
| 270 |
)
|
| 271 |
conn.commit()
|
| 272 |
|
| 273 |
-
# Vytáhnout aktualizované kumulativní statistiky
|
| 274 |
cursor.execute("SELECT COUNT(*), SUM(fields_count) FROM invoices WHERE user_id = ?", (user_id,))
|
| 275 |
count_row = cursor.fetchone()
|
| 276 |
-
|
| 277 |
-
|
|
|
|
|
|
|
| 278 |
conn.close()
|
| 279 |
except Exception as db_err:
|
| 280 |
print(f"DB Insert failure: {db_err}")
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
"
|
| 284 |
-
|
| 285 |
-
|
|
|
|
| 286 |
|
| 287 |
# Generování CSV exportu
|
| 288 |
rows = [["Field", "Value"]]
|
|
@@ -425,8 +431,9 @@ body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);}
|
|
| 425 |
|
| 426 |
<div id="toast-root" style="position:fixed;top:16px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;"></div>
|
| 427 |
|
|
|
|
| 428 |
<div id="auth-screen" class="fixed inset-0 z-50 flex items-center justify-center bg-[#09090b] p-4">
|
| 429 |
-
<div class="card w-full max-w-md p-8 space-y-
|
| 430 |
<div class="text-center space-y-2">
|
| 431 |
<div class="mx-auto w-12 h-12 rounded-xl bg-gradient-to-br from-blue-500 to-blue-700 flex items-center justify-center shadow-lg shadow-blue-500/25">
|
| 432 |
<svg width="22" height="22" viewBox="0 0 16 16" fill="none"><path d="M2 3h12M2 6h8M2 9h10M2 12h6" stroke="white" stroke-width="1.5" stroke-linecap="round"/><circle cx="13" cy="11" r="2.5" stroke="white" stroke-width="1.2"/><path d="M15 13l1.5 1.5" stroke="white" stroke-width="1.2" stroke-linecap="round"/></svg>
|
|
@@ -435,29 +442,40 @@ body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);}
|
|
| 435 |
<p class="text-xs text-zinc-400">Multi-User Document Data-Mining Terminal</p>
|
| 436 |
</div>
|
| 437 |
|
| 438 |
-
<div class="space-y-
|
| 439 |
<div>
|
| 440 |
-
<label class="block text-
|
| 441 |
-
<input type="text" id="auth-username" class="chat-input" placeholder="Zadej login...">
|
| 442 |
</div>
|
| 443 |
<div>
|
| 444 |
-
<label class="block text-
|
| 445 |
-
<input type="password" id="auth-password" class="chat-input" placeholder="••••••••">
|
| 446 |
</div>
|
| 447 |
</div>
|
| 448 |
|
| 449 |
-
<div class="flex flex-col gap-2 pt-
|
| 450 |
-
<button onclick="handleAuthAction('login')" class="btn-primary w-full py-
|
| 451 |
Přihlásit se do systému ⚡
|
| 452 |
</button>
|
| 453 |
-
<button onclick="handleAuthAction('register')" class="btn-secondary w-full py-
|
| 454 |
-
Registrovat nový účet
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
</button>
|
| 456 |
</div>
|
| 457 |
</div>
|
| 458 |
</div>
|
| 459 |
|
| 460 |
|
|
|
|
| 461 |
<div id="app-screen" style="display: none;">
|
| 462 |
<header style="border-bottom:1px solid var(--border);background:rgba(9,9,11,.92);backdrop-filter:blur(16px); position:sticky; top:0; z-index:40;">
|
| 463 |
<div style="max-width:1280px;margin:0 auto;padding:0 24px;height:56px;display:flex;align-items:center;justify-content:space-between;">
|
|
@@ -473,17 +491,18 @@ body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);}
|
|
| 473 |
<span id="status-dot" style="width:8px;height:8px;border-radius:50%;background:#34d399;"></span>
|
| 474 |
<span id="status-text" style="font-size:12px;color:var(--text2);font-weight:500;">System Ready</span>
|
| 475 |
</div>
|
| 476 |
-
<span id="user-display" class="text-sm font-medium
|
| 477 |
-
<button onclick="handleLogout()" class="text-xs text-red-400 hover:underline">
|
| 478 |
</div>
|
| 479 |
</div>
|
| 480 |
</header>
|
| 481 |
|
| 482 |
<main style="max-width:1280px;margin:0 auto;padding:24px;">
|
|
|
|
| 483 |
<div class="grid grid-cols-4 gap-3 mb-5">
|
| 484 |
<div class="card p-4 flex items-center gap-3">
|
| 485 |
<div class="text-2xl">📄</div>
|
| 486 |
-
<div><p class="text-[10px] text-zinc-500 uppercase font-bold">
|
| 487 |
</div>
|
| 488 |
<div class="card p-4 flex items-center gap-3">
|
| 489 |
<div class="text-2xl">⚡</div>
|
|
@@ -499,7 +518,9 @@ body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);}
|
|
| 499 |
</div>
|
| 500 |
</div>
|
| 501 |
|
|
|
|
| 502 |
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
|
|
|
|
| 503 |
<div class="space-y-4">
|
| 504 |
<div class="card p-5 space-y-4">
|
| 505 |
<div class="flex justify-between items-center">
|
|
@@ -544,6 +565,7 @@ body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);}
|
|
| 544 |
</div>
|
| 545 |
</div>
|
| 546 |
|
|
|
|
| 547 |
<div class="space-y-4">
|
| 548 |
<div class="card p-5 space-y-3">
|
| 549 |
<div class="flex justify-between items-center"><span class="text-sm font-semibold">Live Extraction Terminal</span><span class="section-label">Output JSON</span></div>
|
|
@@ -580,6 +602,7 @@ body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);}
|
|
| 580 |
</main>
|
| 581 |
</div>
|
| 582 |
|
|
|
|
| 583 |
<script>
|
| 584 |
let currentBase64 = "";
|
| 585 |
let latestExtractedJson = "";
|
|
@@ -588,6 +611,10 @@ let authPolling = null;
|
|
| 588 |
let parsePolling = null;
|
| 589 |
let chatPolling = null;
|
| 590 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 591 |
function showToast(msg, type='info') {
|
| 592 |
const root = document.getElementById("toast-root");
|
| 593 |
const toast = document.createElement("div");
|
|
@@ -605,26 +632,41 @@ function checkLoginState() {
|
|
| 605 |
if (user) {
|
| 606 |
document.getElementById("auth-screen").style.display = "none";
|
| 607 |
document.getElementById("app-screen").style.display = "block";
|
| 608 |
-
document.getElementById("user-display").innerText = `👤 ${user}`;
|
| 609 |
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 619 |
}
|
| 620 |
-
if(authPolling) clearInterval(authPolling);
|
| 621 |
-
authPolling = setInterval(checkAuthResponse, 150);
|
| 622 |
} else {
|
| 623 |
document.getElementById("auth-screen").style.display = "flex";
|
| 624 |
document.getElementById("app-screen").style.display = "none";
|
| 625 |
}
|
| 626 |
}
|
| 627 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 628 |
function handleAuthAction(action) {
|
| 629 |
const userVal = document.getElementById("auth-username").value.trim();
|
| 630 |
const passVal = document.getElementById("auth-password").value;
|
|
@@ -671,6 +713,7 @@ function checkAuthResponse() {
|
|
| 671 |
showToast("Přihlášení úspěšné!");
|
| 672 |
document.getElementById("auth-screen").style.display = "none";
|
| 673 |
document.getElementById("app-screen").style.display = "block";
|
|
|
|
| 674 |
document.getElementById("user-display").innerText = `👤 ${data.username}`;
|
| 675 |
if (data.stats) {
|
| 676 |
document.getElementById("st-docs").innerText = data.stats.docs_parsed;
|
|
@@ -719,7 +762,7 @@ function executeExtraction() {
|
|
| 719 |
document.getElementById("btn-loading").style.display = "flex";
|
| 720 |
document.getElementById("status-text").innerText = "Těžím data...";
|
| 721 |
|
| 722 |
-
const user = sessionStorage.getItem("omniparse_user") || "";
|
| 723 |
const grImg = document.querySelector("#gr-image-input textarea");
|
| 724 |
const grUser = document.querySelector("#gr-current-user textarea");
|
| 725 |
|
|
@@ -756,12 +799,27 @@ function checkParseResponse() {
|
|
| 756 |
document.getElementById("st-time").innerText = `${res.parse_time_seconds}s`;
|
| 757 |
document.getElementById("st-conf").innerText = `${Math.round(res.security_and_compliance.confidence_score * 100)}%`;
|
| 758 |
document.getElementById("comp-val").innerText = res.security_and_compliance.cross_field_validation;
|
| 759 |
-
document.getElementById("comp-dup").innerText = res.security_and_compliance.duplicate_detected ? "⚠️ POTENCIÁLNÍ DUPLICITA" : "✅ OK (Unikátní)";
|
| 760 |
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
document.getElementById("
|
|
|
|
|
|
|
| 764 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 765 |
if (res._csv_b64) {
|
| 766 |
csvB64Data = res._csv_b64;
|
| 767 |
document.getElementById("export-actions").style.display = "block";
|
|
@@ -834,7 +892,6 @@ function checkChatResponse() {
|
|
| 834 |
}
|
| 835 |
}
|
| 836 |
|
| 837 |
-
// Inicializační kontrola session při bootu
|
| 838 |
setTimeout(checkLoginState, 500);
|
| 839 |
</script>
|
| 840 |
</body>
|
|
@@ -843,10 +900,9 @@ setTimeout(checkLoginState, 500);
|
|
| 843 |
|
| 844 |
# ─── GRADIO APPLICATION CONTAINER ───────────────────────────────────────────
|
| 845 |
with gr.Blocks(css=CUSTOM_CSS, title="OmniParse AI Enterprise Terminal") as demo:
|
| 846 |
-
# Vstřikování custom HTML frontendu s kompletní logikou
|
| 847 |
gr.HTML(HTML_UI)
|
| 848 |
|
| 849 |
-
# Skryté ovládací prvky (Bridge)
|
| 850 |
with gr.Row(elem_id="hidden-controls", visible=False):
|
| 851 |
with gr.Column():
|
| 852 |
gr_image_input = gr.Textbox(label="image_b64", elem_id="gr-image-input", visible=False)
|
|
@@ -859,14 +915,12 @@ with gr.Blocks(css=CUSTOM_CSS, title="OmniParse AI Enterprise Terminal") as demo
|
|
| 859 |
gr_chat_btn = gr.Button("chat", elem_id="gr-chat-btn", visible=False)
|
| 860 |
gr_chat_out = gr.Textbox(label="chat_out", elem_id="gr-chat-out", visible=False)
|
| 861 |
|
| 862 |
-
# Auth kanály pro přihlašování a registraci
|
| 863 |
gr_auth_user = gr.Textbox(label="auth_user", elem_id="gr-auth-user", visible=False)
|
| 864 |
gr_auth_pass = gr.Textbox(label="auth_pass", elem_id="gr-auth-pass", visible=False)
|
| 865 |
gr_login_btn = gr.Button("login", elem_id="gr-login-btn", visible=False)
|
| 866 |
gr_register_btn = gr.Button("register", elem_id="gr-register-btn", visible=False)
|
| 867 |
gr_auth_response = gr.Textbox(label="auth_response", elem_id="gr-auth-response", visible=False)
|
| 868 |
|
| 869 |
-
# Propojení frontend-backend eventů
|
| 870 |
gr_parse_btn.click(
|
| 871 |
fn=gr_parse,
|
| 872 |
inputs=[gr_image_input, gr_current_user],
|
|
|
|
| 13 |
import sqlite3
|
| 14 |
import hashlib
|
| 15 |
|
| 16 |
+
print("Initializing OmniParse AI Enterprise Terminal v5.5 with Optional Free Plan...")
|
| 17 |
|
| 18 |
# ─── DATABASE INITIALIZATION ────────────────────────────────────────────────
|
| 19 |
DB_FILE = "omniparse.db"
|
|
|
|
| 59 |
return json.dumps({"status": "ERROR", "message": "Zadej jméno i heslo, nesmí to bejt prázdný!"})
|
| 60 |
|
| 61 |
username = username.strip().lower()
|
| 62 |
+
if username == "guest":
|
| 63 |
+
return json.dumps({"status": "ERROR", "message": "Jméno 'guest' je rezervované pro Free Plan!"})
|
| 64 |
+
|
| 65 |
try:
|
| 66 |
conn = sqlite3.connect(DB_FILE)
|
| 67 |
cursor = conn.cursor()
|
|
|
|
| 95 |
|
| 96 |
user_id, stored_hash, salt = row
|
| 97 |
|
| 98 |
+
# Podpora pro session refresh
|
| 99 |
if password == "SESSION_REFRESH":
|
| 100 |
cursor.execute("SELECT COUNT(*), SUM(fields_count) FROM invoices WHERE user_id = ?", (user_id,))
|
| 101 |
stats = cursor.fetchone()
|
|
|
|
| 218 |
|
| 219 |
invoice_fp = extracted_fields.get("INVOICE_NUMBER", extracted_fields.get("TOTAL", "UNKNOWN"))
|
| 220 |
|
| 221 |
+
# Kontrola duplicity v rámci přihlášeného uživatele (pokud to není host)
|
| 222 |
is_duplicate = False
|
| 223 |
+
is_guest = (not username or username.strip().lower() == "guest")
|
| 224 |
+
|
| 225 |
+
if not is_guest and invoice_fp != "UNKNOWN":
|
| 226 |
try:
|
| 227 |
conn = sqlite3.connect(DB_FILE)
|
| 228 |
cursor = conn.cursor()
|
|
|
|
| 258 |
"extracted_data": extracted_fields,
|
| 259 |
}
|
| 260 |
|
| 261 |
+
# Zápis do DB pouze pokud uživatel NENÍ host
|
| 262 |
+
if not is_guest:
|
| 263 |
+
fields_count = len(extracted_fields)
|
|
|
|
| 264 |
try:
|
| 265 |
conn = sqlite3.connect(DB_FILE)
|
| 266 |
cursor = conn.cursor()
|
|
|
|
| 274 |
)
|
| 275 |
conn.commit()
|
| 276 |
|
|
|
|
| 277 |
cursor.execute("SELECT COUNT(*), SUM(fields_count) FROM invoices WHERE user_id = ?", (user_id,))
|
| 278 |
count_row = cursor.fetchone()
|
| 279 |
+
result["user_stats"] = {
|
| 280 |
+
"docs_parsed": count_row[0] or 0,
|
| 281 |
+
"fields_extracted": count_row[1] or 0
|
| 282 |
+
}
|
| 283 |
conn.close()
|
| 284 |
except Exception as db_err:
|
| 285 |
print(f"DB Insert failure: {db_err}")
|
| 286 |
+
else:
|
| 287 |
+
# Pro hosta vrátíme flag, JS si spočítá lokální session statistiky sám
|
| 288 |
+
result["user_stats"] = {
|
| 289 |
+
"docs_parsed": "GUEST_MODE",
|
| 290 |
+
"fields_extracted": "GUEST_MODE"
|
| 291 |
+
}
|
| 292 |
|
| 293 |
# Generování CSV exportu
|
| 294 |
rows = [["Field", "Value"]]
|
|
|
|
| 431 |
|
| 432 |
<div id="toast-root" style="position:fixed;top:16px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;"></div>
|
| 433 |
|
| 434 |
+
<!-- ═══ 1. AUTHENTICATION OVERLAY SCREEN ═══ -->
|
| 435 |
<div id="auth-screen" class="fixed inset-0 z-50 flex items-center justify-center bg-[#09090b] p-4">
|
| 436 |
+
<div class="card w-full max-w-md p-8 space-y-5">
|
| 437 |
<div class="text-center space-y-2">
|
| 438 |
<div class="mx-auto w-12 h-12 rounded-xl bg-gradient-to-br from-blue-500 to-blue-700 flex items-center justify-center shadow-lg shadow-blue-500/25">
|
| 439 |
<svg width="22" height="22" viewBox="0 0 16 16" fill="none"><path d="M2 3h12M2 6h8M2 9h10M2 12h6" stroke="white" stroke-width="1.5" stroke-linecap="round"/><circle cx="13" cy="11" r="2.5" stroke="white" stroke-width="1.2"/><path d="M15 13l1.5 1.5" stroke="white" stroke-width="1.2" stroke-linecap="round"/></svg>
|
|
|
|
| 442 |
<p class="text-xs text-zinc-400">Multi-User Document Data-Mining Terminal</p>
|
| 443 |
</div>
|
| 444 |
|
| 445 |
+
<div class="space-y-3">
|
| 446 |
<div>
|
| 447 |
+
<label class="block text-[10px] font-bold uppercase tracking-wider text-zinc-500 mb-1">Uživatelské jméno</label>
|
| 448 |
+
<input type="text" id="auth-username" class="chat-input text-xs" placeholder="Zadej login...">
|
| 449 |
</div>
|
| 450 |
<div>
|
| 451 |
+
<label class="block text-[10px] font-bold uppercase tracking-wider text-zinc-500 mb-1">Heslo</label>
|
| 452 |
+
<input type="password" id="auth-password" class="chat-input text-xs" placeholder="••••••••">
|
| 453 |
</div>
|
| 454 |
</div>
|
| 455 |
|
| 456 |
+
<div class="flex flex-col gap-2 pt-1">
|
| 457 |
+
<button onclick="handleAuthAction('login')" class="btn-primary w-full py-2.5 font-semibold text-xs">
|
| 458 |
Přihlásit se do systému ⚡
|
| 459 |
</button>
|
| 460 |
+
<button onclick="handleAuthAction('register')" class="btn-secondary w-full py-2.5 text-xs">
|
| 461 |
+
Registrovat nový cloudový účet
|
| 462 |
+
</button>
|
| 463 |
+
|
| 464 |
+
<div class="relative flex py-2 items-center">
|
| 465 |
+
<div class="flex-grow border-t border-zinc-800"></div>
|
| 466 |
+
<span class="flex-shrink mx-3 text-[10px] text-zinc-600 font-bold uppercase">Nebo</span>
|
| 467 |
+
<div class="flex-grow border-t border-zinc-800"></div>
|
| 468 |
+
</div>
|
| 469 |
+
|
| 470 |
+
<button onclick="bypassAuthAsGuest()" class="w-full py-2 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-zinc-300 rounded-xl text-xs font-semibold transition-all">
|
| 471 |
+
Pokračovat bez registrace (Free Plan) →
|
| 472 |
</button>
|
| 473 |
</div>
|
| 474 |
</div>
|
| 475 |
</div>
|
| 476 |
|
| 477 |
|
| 478 |
+
<!-- ═══ 2. MAIN APPLICATION WORKSPACE ═══ -->
|
| 479 |
<div id="app-screen" style="display: none;">
|
| 480 |
<header style="border-bottom:1px solid var(--border);background:rgba(9,9,11,.92);backdrop-filter:blur(16px); position:sticky; top:0; z-index:40;">
|
| 481 |
<div style="max-width:1280px;margin:0 auto;padding:0 24px;height:56px;display:flex;align-items:center;justify-content:space-between;">
|
|
|
|
| 491 |
<span id="status-dot" style="width:8px;height:8px;border-radius:50%;background:#34d399;"></span>
|
| 492 |
<span id="status-text" style="font-size:12px;color:var(--text2);font-weight:500;">System Ready</span>
|
| 493 |
</div>
|
| 494 |
+
<span id="user-display" class="text-sm font-medium"></span>
|
| 495 |
+
<button onclick="handleLogout()" class="text-xs text-red-400 hover:underline">Zpět na login / Odhlásit</button>
|
| 496 |
</div>
|
| 497 |
</div>
|
| 498 |
</header>
|
| 499 |
|
| 500 |
<main style="max-width:1280px;margin:0 auto;padding:24px;">
|
| 501 |
+
<!-- Stats Banner -->
|
| 502 |
<div class="grid grid-cols-4 gap-3 mb-5">
|
| 503 |
<div class="card p-4 flex items-center gap-3">
|
| 504 |
<div class="text-2xl">📄</div>
|
| 505 |
+
<div><p class="text-[10px] text-zinc-500 uppercase font-bold">Zpracované dokumenty</p><p id="st-docs" class="text-lg font-bold text-white">0</p></div>
|
| 506 |
</div>
|
| 507 |
<div class="card p-4 flex items-center gap-3">
|
| 508 |
<div class="text-2xl">⚡</div>
|
|
|
|
| 518 |
</div>
|
| 519 |
</div>
|
| 520 |
|
| 521 |
+
<!-- Workspace Grid -->
|
| 522 |
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
|
| 523 |
+
<!-- Left Controls -->
|
| 524 |
<div class="space-y-4">
|
| 525 |
<div class="card p-5 space-y-4">
|
| 526 |
<div class="flex justify-between items-center">
|
|
|
|
| 565 |
</div>
|
| 566 |
</div>
|
| 567 |
|
| 568 |
+
<!-- Right Terminal & Terminal Assist -->
|
| 569 |
<div class="space-y-4">
|
| 570 |
<div class="card p-5 space-y-3">
|
| 571 |
<div class="flex justify-between items-center"><span class="text-sm font-semibold">Live Extraction Terminal</span><span class="section-label">Output JSON</span></div>
|
|
|
|
| 602 |
</main>
|
| 603 |
</div>
|
| 604 |
|
| 605 |
+
<!-- ═══ 3. GRADIO PIPELINE JAVASCRIPT BRIDGE ═══ -->
|
| 606 |
<script>
|
| 607 |
let currentBase64 = "";
|
| 608 |
let latestExtractedJson = "";
|
|
|
|
| 611 |
let parsePolling = null;
|
| 612 |
let chatPolling = null;
|
| 613 |
|
| 614 |
+
// Lokální paměť statistik pro uživatele na Free Planu (neukládá se do DB)
|
| 615 |
+
let guestDocsCount = 0;
|
| 616 |
+
let guestFieldsCount = 0;
|
| 617 |
+
|
| 618 |
function showToast(msg, type='info') {
|
| 619 |
const root = document.getElementById("toast-root");
|
| 620 |
const toast = document.createElement("div");
|
|
|
|
| 632 |
if (user) {
|
| 633 |
document.getElementById("auth-screen").style.display = "none";
|
| 634 |
document.getElementById("app-screen").style.display = "block";
|
|
|
|
| 635 |
|
| 636 |
+
if (user.toLowerCase() === "guest") {
|
| 637 |
+
document.getElementById("user-display").className = "text-sm font-medium text-amber-400";
|
| 638 |
+
document.getElementById("user-display").innerText = "👤 Free Plan (Neregistrován)";
|
| 639 |
+
document.getElementById("st-docs").innerText = guestDocsCount + " (Free)";
|
| 640 |
+
document.getElementById("st-fields").innerText = guestFieldsCount + " (Místní)";
|
| 641 |
+
} else {
|
| 642 |
+
document.getElementById("user-display").className = "text-sm font-medium text-blue-400";
|
| 643 |
+
document.getElementById("user-display").innerText = `👤 ${user}`;
|
| 644 |
+
|
| 645 |
+
// Refresh cloudu z DB pro registrované
|
| 646 |
+
const grUser = document.querySelector("#gr-auth-user textarea");
|
| 647 |
+
const grPass = document.querySelector("#gr-auth-pass textarea");
|
| 648 |
+
if (grUser && grPass) {
|
| 649 |
+
grUser.value = user;
|
| 650 |
+
grUser.dispatchEvent(new Event("input", { bubbles: true }));
|
| 651 |
+
grPass.value = "SESSION_REFRESH";
|
| 652 |
+
grPass.dispatchEvent(new Event("input", { bubbles: true }));
|
| 653 |
+
document.querySelector("#gr-login-btn").click();
|
| 654 |
+
}
|
| 655 |
+
if(authPolling) clearInterval(authPolling);
|
| 656 |
+
authPolling = setInterval(checkAuthResponse, 150);
|
| 657 |
}
|
|
|
|
|
|
|
| 658 |
} else {
|
| 659 |
document.getElementById("auth-screen").style.display = "flex";
|
| 660 |
document.getElementById("app-screen").style.display = "none";
|
| 661 |
}
|
| 662 |
}
|
| 663 |
|
| 664 |
+
function bypassAuthAsGuest() {
|
| 665 |
+
sessionStorage.setItem("omniparse_user", "Guest");
|
| 666 |
+
showToast("Vstupuješ jako host v bezplatném režimu.");
|
| 667 |
+
checkLoginState();
|
| 668 |
+
}
|
| 669 |
+
|
| 670 |
function handleAuthAction(action) {
|
| 671 |
const userVal = document.getElementById("auth-username").value.trim();
|
| 672 |
const passVal = document.getElementById("auth-password").value;
|
|
|
|
| 713 |
showToast("Přihlášení úspěšné!");
|
| 714 |
document.getElementById("auth-screen").style.display = "none";
|
| 715 |
document.getElementById("app-screen").style.display = "block";
|
| 716 |
+
document.getElementById("user-display").className = "text-sm font-medium text-blue-400";
|
| 717 |
document.getElementById("user-display").innerText = `👤 ${data.username}`;
|
| 718 |
if (data.stats) {
|
| 719 |
document.getElementById("st-docs").innerText = data.stats.docs_parsed;
|
|
|
|
| 762 |
document.getElementById("btn-loading").style.display = "flex";
|
| 763 |
document.getElementById("status-text").innerText = "Těžím data...";
|
| 764 |
|
| 765 |
+
const user = sessionStorage.getItem("omniparse_user") || "Guest";
|
| 766 |
const grImg = document.querySelector("#gr-image-input textarea");
|
| 767 |
const grUser = document.querySelector("#gr-current-user textarea");
|
| 768 |
|
|
|
|
| 799 |
document.getElementById("st-time").innerText = `${res.parse_time_seconds}s`;
|
| 800 |
document.getElementById("st-conf").innerText = `${Math.round(res.security_and_compliance.confidence_score * 100)}%`;
|
| 801 |
document.getElementById("comp-val").innerText = res.security_and_compliance.cross_field_validation;
|
|
|
|
| 802 |
|
| 803 |
+
// Zobrazení duplicity
|
| 804 |
+
if (sessionStorage.getItem("omniparse_user").toLowerCase() === "guest") {
|
| 805 |
+
document.getElementById("comp-dup").innerText = "🔒 Vyžaduje registraci";
|
| 806 |
+
} else {
|
| 807 |
+
document.getElementById("comp-dup").innerText = res.security_and_compliance.duplicate_detected ? "⚠️ POTENCIÁLNÍ DUPLICITA" : "✅ OK (Unikátní)";
|
| 808 |
}
|
| 809 |
+
|
| 810 |
+
// Počítání a update statistik na panelu
|
| 811 |
+
if (sessionStorage.getItem("omniparse_user").toLowerCase() === "guest") {
|
| 812 |
+
guestDocsCount++;
|
| 813 |
+
guestFieldsCount += Object.keys(res.extracted_data).length;
|
| 814 |
+
document.getElementById("st-docs").innerText = guestDocsCount + " (Free)";
|
| 815 |
+
document.getElementById("st-fields").innerText = guestFieldsCount + " (Místní)";
|
| 816 |
+
} else {
|
| 817 |
+
if (res.user_stats) {
|
| 818 |
+
document.getElementById("st-docs").innerText = res.user_stats.docs_parsed;
|
| 819 |
+
document.getElementById("st-fields").innerText = res.user_stats.fields_extracted;
|
| 820 |
+
}
|
| 821 |
+
}
|
| 822 |
+
|
| 823 |
if (res._csv_b64) {
|
| 824 |
csvB64Data = res._csv_b64;
|
| 825 |
document.getElementById("export-actions").style.display = "block";
|
|
|
|
| 892 |
}
|
| 893 |
}
|
| 894 |
|
|
|
|
| 895 |
setTimeout(checkLoginState, 500);
|
| 896 |
</script>
|
| 897 |
</body>
|
|
|
|
| 900 |
|
| 901 |
# ─── GRADIO APPLICATION CONTAINER ───────────────────────────────────────────
|
| 902 |
with gr.Blocks(css=CUSTOM_CSS, title="OmniParse AI Enterprise Terminal") as demo:
|
|
|
|
| 903 |
gr.HTML(HTML_UI)
|
| 904 |
|
| 905 |
+
# Skryté ovládací prvky (Bridge)
|
| 906 |
with gr.Row(elem_id="hidden-controls", visible=False):
|
| 907 |
with gr.Column():
|
| 908 |
gr_image_input = gr.Textbox(label="image_b64", elem_id="gr-image-input", visible=False)
|
|
|
|
| 915 |
gr_chat_btn = gr.Button("chat", elem_id="gr-chat-btn", visible=False)
|
| 916 |
gr_chat_out = gr.Textbox(label="chat_out", elem_id="gr-chat-out", visible=False)
|
| 917 |
|
|
|
|
| 918 |
gr_auth_user = gr.Textbox(label="auth_user", elem_id="gr-auth-user", visible=False)
|
| 919 |
gr_auth_pass = gr.Textbox(label="auth_pass", elem_id="gr-auth-pass", visible=False)
|
| 920 |
gr_login_btn = gr.Button("login", elem_id="gr-login-btn", visible=False)
|
| 921 |
gr_register_btn = gr.Button("register", elem_id="gr-register-btn", visible=False)
|
| 922 |
gr_auth_response = gr.Textbox(label="auth_response", elem_id="gr-auth-response", visible=False)
|
| 923 |
|
|
|
|
| 924 |
gr_parse_btn.click(
|
| 925 |
fn=gr_parse,
|
| 926 |
inputs=[gr_image_input, gr_current_user],
|