Asem75 commited on
Commit
a431271
·
verified ·
1 Parent(s): 9b8662e

Update upload_app.py

Browse files
Files changed (1) hide show
  1. upload_app.py +0 -413
upload_app.py CHANGED
@@ -1,413 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- print("[CHECKPOINT - UPLOAD_APP] >>> بدأ تحميل ملف upload_app.py وتحديد المسارات...", flush=True)
5
-
6
- import os
7
- import re
8
- import json
9
- import shutil
10
- import secrets
11
- from datetime import datetime as dt
12
- from datetime import timedelta
13
- from urllib.parse import unquote
14
- import gradio as gr
15
- from huggingface_hub import HfApi, list_repo_files, login, hf_hub_download, upload_file
16
-
17
- # استيراد مكاتب التحويل والـ ZIP (افترض أنها موجودة)
18
- from converter import convert_text, zip_single_file
19
-
20
- # ============================================================
21
- # مسارات التخزين المحلية والسرية للنظام
22
- # ============================================================
23
- UPLOAD_DIR = "uploads"
24
- STATUS_DIR = "system"
25
- STATUS_FILE_PATH = os.path.join(STATUS_DIR, "status.json")
26
-
27
- REPO_ID = "Asem75/aiocr_asistant"
28
- RAW_DIR = "raw_files"
29
- WATCH_DIR_1 = "process"
30
- WATCH_DIR_2 = "process_files"
31
- FINAL_DIR = "Final_Arabic_Files"
32
- MESSAGES_PATH = "system/messages.json"
33
-
34
- TOKEN = os.getenv("AIOCR_KEY")
35
- api = None
36
- if TOKEN:
37
- try:
38
- TOKEN = TOKEN.strip().split()[0]
39
- login(token=TOKEN)
40
- api = HfApi(token=TOKEN)
41
- print("[CHECKPOINT - UPLOAD_APP] تم تسجيل الدخول في HuggingFace بنجاح.", flush=True)
42
- except Exception as e:
43
- print(f"[WARNING] فشل تسجيل الدخول بالتوكن: {e}", flush=True)
44
-
45
- print(f"[CHECKPOINT - UPLOAD_APP] جاري إنشاء المجلدات المحلية إن لم تكن موجودة...", flush=True)
46
- os.makedirs(UPLOAD_DIR, exist_ok=True)
47
- os.makedirs(STATUS_DIR, exist_ok=True)
48
- print("[CHECKPOINT - UPLOAD_APP] تم إنشاء المجلدات المحلية بنجاح.", flush=True)
49
-
50
- # ============================================================
51
- # 1) دوال مساعدة وسجلات النظام السيادية
52
- # ============================================================
53
-
54
- def sanitize_filename(name: str) -> str:
55
- name = name.replace(" ", "_")
56
- return re.sub(r"[^\w\.\-]", "", name)
57
-
58
- def _write_status(entry: dict):
59
- try:
60
- with open(STATUS_FILE_PATH, "w", encoding="utf-8") as f:
61
- json.dump(entry, f, ensure_ascii=False, indent=4)
62
- if TOKEN:
63
- upload_file(
64
- path_or_fileobj=STATUS_FILE_PATH,
65
- path_in_repo="system/status.json",
66
- repo_id=REPO_ID,
67
- repo_type="dataset",
68
- token=TOKEN
69
- )
70
- except Exception as e:
71
- print(f"[CHECKPOINT ERROR] فشل كتابة السجل أو مزامنته سحابياً: {e}", flush=True)
72
-
73
- def _read_status() -> dict:
74
- if not os.path.exists(STATUS_FILE_PATH):
75
- return {}
76
- try:
77
- with open(STATUS_FILE_PATH, "r", encoding="utf-8") as f:
78
- return json.load(f)
79
- except Exception as e:
80
- print(f"[CHECKPOINT ERROR] فشل قراءة السجل المحلي status.json: {e}", flush=True)
81
- return {}
82
-
83
- def send_report_to_manager(report_text: str):
84
- try:
85
- try:
86
- local_existing = hf_hub_download(
87
- repo_id=REPO_ID,
88
- filename=MESSAGES_PATH,
89
- repo_type="dataset",
90
- token=TOKEN,
91
- )
92
- with open(local_existing, "r", encoding="utf-8") as f:
93
- messages = json.load(f)
94
- if not isinstance(messages, list): messages = []
95
- except:
96
- messages = []
97
-
98
- messages.append({
99
- "time": dt.now().strftime("%Y-%m-%d %H:%M:%S"),
100
- "from": "upload_interface",
101
- "report": report_text,
102
- })
103
-
104
- local_path = os.path.join("system", "messages.json")
105
- with open(local_path, "w", encoding="utf-8") as f:
106
- json.dump(messages, f, ensure_ascii=False, indent=2)
107
-
108
- upload_file(
109
- path_or_fileobj=local_path,
110
- path_in_repo=MESSAGES_PATH,
111
- repo_id=REPO_ID,
112
- repo_type="dataset",
113
- token=TOKEN,
114
- )
115
- except Exception as e:
116
- print(f"[REPORT SEND ERROR] {e}", flush=True)
117
-
118
- # ============================================================
119
- # 2) رادار فحص القيود للمشترك المجاني (FREE)
120
- # ============================================================
121
- def check_free_user_limits(username):
122
- try:
123
- all_files = list_repo_files(REPO_ID, repo_type="dataset")
124
- raw_prefix = f"{RAW_DIR}/{username}_"
125
- watch1_prefix = f"{WATCH_DIR_1}/{username}_"
126
- watch2_prefix = f"{WATCH_DIR_2}/{username}_"
127
-
128
- active_files = [
129
- f for f in all_files
130
- if f.startswith(raw_prefix) or f.startswith(watch1_prefix) or f.startswith(watch2_prefix)
131
- ]
132
-
133
- if active_files:
134
- return False, f"❌ الرادار رصد وجود {len(active_files)} ملفات لك قيد المعالجة حالياً. يرجى ال��نتظار."
135
- return True, "مسموح بالرفع"
136
- except Exception as e:
137
- return False, "⚠️ حدث خطأ غير متوقع أثناء فحص رادار القيود."
138
-
139
- # ============================================================
140
- # 3) دالة عرض/إخفاء لوحة الاشتراك (VIP / Free)
141
- # ============================================================
142
- def toggle_subscription_panel(user_session, panel_visible_state):
143
- new_state = not panel_visible_state
144
- if not new_state:
145
- return gr.update(visible=False), False
146
-
147
- rank = (user_session or {}).get("rank", "free")
148
- name = (user_session or {}).get("name", "زائر")
149
-
150
- if rank == "vip":
151
- html = f"""
152
- <div style='background:#1e293b; border:1px solid #d4af37; padding:14px; border-radius:10px;'>
153
- <p style='color:#d4af37; margin:0;'>🥇 أنت مشترك VIP يا {name} — لديك صلاحية كاملة لكل خيارات المعالجة المتقدمة.</p>
154
- </div>
155
- """
156
- else:
157
- html = f"""
158
- <div style='background:#1e293b; border:1px solid #38bdf8; padding:14px; border-radius:10px;'>
159
- <p style='color:#9ca3af; margin:0;'>🥈 أنت مشترك مجاني يا {name}. يمكنك الترقية لـ VIP لفتح كل خيارات المعالجة المتقدمة.</p>
160
- </div>
161
- """
162
- return gr.update(visible=True, value=html), True
163
-
164
- # ============================================================
165
- # 4) دالة استلام ومعالجة وحفظ الملفات مباشرة في مجلد raw_files
166
- # ============================================================
167
- def handle_upload(files, user_session, enable_table, enable_handwriting,
168
- enable_heavy_prep, enable_high_dpi, enable_fast, enable_tashkeel):
169
-
170
- print(f"[CHECKPOINT - UPLOAD_APP] بدأ استلام طلب رفع ملفات من الجلسة...", flush=True)
171
- if not (user_session or {}).get("authenticated"):
172
- return "<div style='color:#f87171;'>⚠️ يجب تسجيل الدخول وتوثيق الهوية أولاً قبل رفع أي ملفات.</div>"
173
-
174
- if not files:
175
- return "<div style='color:#facc15;'>⚠️ لم يتم اختيار أي ملف للرفع.</div>"
176
-
177
- username = sanitize_filename((user_session.get("name") or "unknown").strip())
178
- rank = user_session.get("rank", "free")
179
- is_vip = (rank == "vip")
180
-
181
- if not is_vip:
182
- allowed, message = check_free_user_limits(username)
183
- if not allowed:
184
- return f"<div role='alert' style='color:red; font-weight:bold;'>{message}</div>"
185
-
186
- user_folder = os.path.join(UPLOAD_DIR, username)
187
- os.makedirs(user_folder, exist_ok=True)
188
-
189
- saved_names = []
190
- prefix = f"VIP_{username}_" if is_vip else f"{username}_"
191
-
192
- for file in files:
193
- try:
194
- fname = sanitize_filename(unquote(os.path.basename(file.name)))
195
- local_dest = os.path.join(user_folder, fname)
196
-
197
- shutil.copy(file.name, local_dest)
198
-
199
- option_prefix = ""
200
- if enable_fast:
201
- option_prefix = "fast_"
202
- else:
203
- if enable_table: option_prefix += "tbl_"
204
- if enable_handwriting: option_prefix += "hw_"
205
- if enable_heavy_prep: option_prefix += "prep_"
206
- if enable_high_dpi: option_prefix += "dpi300_"
207
- if enable_tashkeel: option_prefix += "tash_"
208
-
209
- final_filename = f"{prefix}{option_prefix}{fname}"
210
-
211
- if TOKEN:
212
- print(f"[CLOUD UPLOAD] جاري إرسال الملف {final_filename} إلى مجلد raw_files السحابي...", flush=True)
213
- upload_file(
214
- path_or_fileobj=local_dest,
215
- path_in_repo=f"{RAW_DIR}/{final_filename}",
216
- repo_id=REPO_ID,
217
- repo_type="dataset",
218
- token=TOKEN
219
- )
220
- saved_names.append(final_filename)
221
-
222
- except Exception as e:
223
- print(f"[UPLOAD ERROR] فشل رفع الملف سحابياً: {e}", flush=True)
224
- continue
225
-
226
- options = {
227
- "table": bool(enable_table),
228
- "handwriting": bool(enable_handwriting),
229
- "heavy_prep": bool(enable_heavy_prep),
230
- "high_dpi": bool(enable_high_dpi),
231
- "fast": bool(enable_fast),
232
- "tashkeel": bool(enable_tashkeel),
233
- }
234
-
235
- _write_status({
236
- "user": username,
237
- "files": saved_names,
238
- "options": options,
239
- "status": "🟡 في قائمة الانتظار للمعالجة الفورية...",
240
- "queued_at": dt.now().isoformat()
241
- })
242
-
243
- if not saved_names:
244
- return "<div style='color:#f87171;'>❌ فشل حفظ ورفع الملفات المرفوعة إلى السحابة. حاول مرة أخرى.</div>"
245
-
246
- send_report_to_manager(f"المستخدم {username} ({'VIP' if is_vip else 'FREE'}) رفع {len(saved_names)} ملف/ات.")
247
-
248
- return f"<div role='status' style='color:green; font-weight:bold;'>✅ تم رفع {len(saved_names)} ملف بنجاح وإضافته إلى مجلد raw_files السحابي.</div>"
249
-
250
- def read_status_json():
251
- status = _read_status()
252
- if not status:
253
- return "<div role='status' aria-live='polite'>بانتظار تحديث الحالة من system/status.json...</div>"
254
- files_list = "، ".join(status.get("files", [])) or "—"
255
- return f"""
256
- <div role='status' aria-live='polite' style='line-height:1.6;'>
257
- 👤 المستخدم: <b>{status.get('user', '—')}</b><br>
258
- 📄 الملفات: {files_list}<br>
259
- 📌 الحالة: {status.get('status', '—')}<br>
260
- 🕒 آخر تحديث: {status.get('queued_at', '—')}
261
- </div>
262
- """
263
-
264
- # ============================================================
265
- # 5) دوال قسم ملفاتي الأصلي والمصحح والمحمي
266
- # ============================================================
267
-
268
- def list_final_files():
269
- """جلب قائمة الملفات الجاهزة من Final_Arabic_Files في المستودع"""
270
- try:
271
- files = list_repo_files(REPO_ID, repo_type="dataset")
272
- final_files = [f.split("/", 1)[1] for f in files if f.startswith(FINAL_DIR + "/")]
273
- return sorted(final_files) if final_files else []
274
- except Exception as e:
275
- print(f"[FINAL LIST ERROR] {e}", flush=True)
276
- return []
277
-
278
- def load_file_content(selected_name: str):
279
- if not selected_name:
280
- return "", "", ""
281
- try:
282
- remote_path = f"{FINAL_DIR}/{selected_name}"
283
- local_path = hf_hub_download(
284
- repo_id=REPO_ID,
285
- filename=remote_path,
286
- repo_type="dataset",
287
- token=TOKEN
288
- )
289
- with open(local_path, "r", encoding="utf-8") as f:
290
- text = f.read()
291
- return text, os.path.splitext(selected_name)[0], selected_name
292
- except Exception as e:
293
- print(f"[FINAL LOAD ERROR] {selected_name} — {e}", flush=True)
294
- return "❌ فشل تحميل الملف", "", ""
295
-
296
- def convert_and_upload(text, base_name, target_ext, font_name, font_size):
297
- if not text.strip(): return "❌ لا يوجد نص", None
298
- try:
299
- out_path = convert_text(text, base_name, target_ext, font_name, font_size)
300
- new_name = os.path.basename(out_path)
301
- upload_file(
302
- path_or_fileobj=out_path,
303
- path_in_repo=f"{FINAL_DIR}/{new_name}",
304
- repo_id=REPO_ID, repo_type="dataset", token=TOKEN
305
- )
306
- return f"✅ تم التحويل والرفع باسم {new_name}", out_path
307
- except Exception as e:
308
- print(f"[CONVERT/UPLOAD ERROR] {e}", flush=True)
309
- return f"⚠️ فشل: {e}", None
310
-
311
- def do_zip(text, base_name):
312
- if not text.strip(): return "❌ لا يوجد نص", None
313
- try:
314
- txt_path = convert_text(text, base_name, "txt")
315
- return "✅ تم إنشاء ZIP", zip_single_file(txt_path, base_name)
316
- except Exception as e:
317
- return "⚠️ فشل إنشاء ZIP", None
318
-
319
- # ============================================================
320
- # 6) بناء الواجهة الرادارية الرسمية المحصنة
321
- # ============================================================
322
-
323
- def build_upload_ui():
324
- print("[CHECKPOINT - UPLOAD_APP] >>> دخلنا الآن داخل دالة build_upload_ui لبناء عتاد الواجهات...", flush=True)
325
-
326
- # -------- أ) واجهة الرفع المباشر --------
327
- with gr.Column() as upload_layout:
328
- gr.Markdown("<h1 style='text-align:center;'>📤 بوابة الرفع ودورة حياة الملفات الموثقة</h1>")
329
-
330
- user_session = gr.State({"authenticated": False, "name": "", "rank": "free"})
331
- panel_visible_state = gr.State(False)
332
-
333
- welcome_box = gr.HTML("<p role='status'>🟢 الجلسة السيادية الموحدة نشطة وجاهزة لاستلام العتاد.</p>")
334
-
335
- with gr.Row():
336
- rank_icon_btn = gr.Button("🥈 FREE", elem_id="rank_icon_btn", scale=0, min_width=110, visible=True)
337
-
338
- subscription_panel = gr.HTML(visible=False)
339
-
340
- with gr.Group() as upload_area:
341
- uploader = gr.File(label="اختر ملفاتك", file_count="multiple")
342
- upload_btn = gr.Button("رفع الملفات", variant="primary")
343
- status_box = gr.HTML("<div>بانتظار رفع الملفات...</div>")
344
-
345
- with gr.Group() as options_area:
346
- gr.Markdown("### ⚙️ خيارات معالجة العتاد المتقدمة")
347
- with gr.Row():
348
- enable_table = gr.Checkbox(label="📊 كشف الجداول", value=False)
349
- enable_handwriting = gr.Checkbox(label="🖐️ خط يدوي", value=False)
350
- enable_heavy_prep = gr.Checkbox(label="🔧 معالجة ثقيلة", value=False)
351
- enable_high_dpi = gr.Checkbox(label="📐 دقة عالية (300 DPI)", value=False)
352
- enable_fast = gr.Checkbox(label="⚡ الوضع السريع (EasyOCR)", value=False)
353
- enable_tashkeel = gr.Checkbox(label="🔤 تشكيل النص", value=False)
354
-
355
- gr.Markdown("### 📜 حالة التجميع والمعالجة الفورية")
356
- system_status = gr.HTML("<div role='status' aria-live='polite'>بانتظار تحديث الحالة من system/status.json...</div>")
357
-
358
- rank_icon_btn.click(
359
- fn=toggle_subscription_panel,
360
- inputs=[user_session, panel_visible_state],
361
- outputs=[subscription_panel, panel_visible_state]
362
- )
363
-
364
- upload_btn.click(
365
- fn=handle_upload,
366
- inputs=[uploader, user_session, enable_table, enable_handwriting, enable_heavy_prep, enable_high_dpi, enable_fast, enable_tashkeel],
367
- outputs=status_box
368
- ).then(read_status_json, None, system_status)
369
-
370
- # -------- ب) واجهة "ملفاتي" الكاملة --------
371
- with gr.Column() as files_layout:
372
- gr.Markdown("<h1 style='text-align:center;'>📁 الملفات الجاهزة للتحويل</h1>")
373
- files_list = gr.Dropdown(label="اختر ملفاً من Final_Arabic_Files", choices=[], interactive=True)
374
- refresh_btn = gr.Button("تحديث القائمة")
375
-
376
- editor = gr.Textbox(label="مربع التعديل", lines=15)
377
- filename_box = gr.Textbox(label="اسم الملف (بدون امتداد)")
378
- ext_dropdown = gr.Dropdown(label="الامتداد", choices=["txt", "pdf", "docx", "xlsx", "csv", "html"], value="pdf")
379
- font_dropdown = gr.Dropdown(label="الخط", choices=["Arial", "Tahoma", "Amiri-Regular", "Cairo-Regular"], value="Amiri-Regular")
380
- font_size_dropdown = gr.Slider(label="حجم الخط", minimum=10, maximum=40, value=18, step=1)
381
-
382
- convert_btn = gr.Button("تحويل")
383
- download_file = gr.File(label="تحميل الملف")
384
- convert_status = gr.HTML()
385
-
386
- zip_btn = gr.Button("إنشاء ZIP")
387
- zip_file = gr.File(label="تحميل ZIP")
388
- zip_status = gr.HTML()
389
-
390
- def refresh_files():
391
- return gr.update(choices=list_final_files())
392
-
393
- refresh_btn.click(fn=refresh_files, outputs=files_list)
394
- files_list.change(
395
- fn=load_file_content,
396
- inputs=files_list,
397
- outputs=[editor, filename_box, files_list]
398
- )
399
- convert_btn.click(
400
- fn=convert_and_upload,
401
- inputs=[editor, filename_box, ext_dropdown, font_dropdown, font_size_dropdown],
402
- outputs=[convert_status, download_file]
403
- )
404
- zip_btn.click(
405
- fn=do_zip,
406
- inputs=[editor, filename_box],
407
- outputs=[zip_status, zip_file]
408
- )
409
-
410
- print("[CHECKPOINT - UPLOAD_APP] <<< انتهى بناء وتأمين دمج الواجهتين بنجاح وخرجنا من الدالة.", flush=True)
411
- return upload_layout, files_layout, user_session, welcome_box, rank_icon_btn, files_list
412
-
413
- print("[CHECKPOINT - UPLOAD_APP] <<< انتهى ملف upload_app.py بالكامل وهو جاهز للمطابقة السيادية.", flush=True)