Leen172 commited on
Commit
085322d
·
verified ·
1 Parent(s): 32ef794

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -26
app.py CHANGED
@@ -1,5 +1,5 @@
1
  # -*- coding: utf-8 -*-
2
- # صفحتان ثابتتان + Submit لكل سؤال يعمل فعليًا + منع تغيّر أبعاد صفحة الإدخال
3
 
4
  import os, json, uuid, random, unicodedata
5
  from dataclasses import dataclass
@@ -70,8 +70,7 @@ def is_good(t: str, min_chars=250, min_alpha=0.15) -> bool:
70
  def file_to_text(path: str, model_id=DEFAULT_TROCR_MODEL, zoom=DEFAULT_TROCR_ZOOM) -> Tuple[str,str]:
71
  ext = Path(path).suffix.lower()
72
  if ext == ".txt":
73
- with open(path,"r",encoding="utf-8",errors="ignore") as f:
74
- return f.read(), "plain text"
75
  raw = extract_text_with_pypdf(path)
76
  if is_good(raw): return raw, "embedded (pypdf)"
77
  return extract_text_with_ocr(path, model_id, zoom), "OCR (TrOCR)"
@@ -82,7 +81,7 @@ def strip_headers(t:str)->str:
82
  out=[]
83
  for ln in t.splitlines():
84
  if re2.match(r"^\s*--- \[Page \d+\] ---\s*$", ln): continue
85
- if re2.match(r"^\s*(Page\s*\d+|صفحة\s*\d+)\s*$", ln): continue
86
  if re2.match(r"^\s*[-–—_*]{3,}\s*$", ln): continue
87
  out.append(ln)
88
  return "\n".join(out)
@@ -226,7 +225,7 @@ def build_quiz(text_area, file_path, n, model_id, zoom):
226
  recs = to_records(items)
227
  return render_quiz_html(recs), gr.update(visible=False), gr.update(visible=True), ""
228
 
229
- # ------------------ CSS ------------------
230
  CSS = """
231
  :root{
232
  --bg:#0e0e11; --panel:#15161a; --card:#1a1b20; --muted:#a7b0be;
@@ -236,16 +235,65 @@ body{direction:rtl; font-family:system-ui,'Cairo','IBM Plex Arabic',sans-serif;
236
  .gradio-container{max-width:980px;margin:0 auto;padding:12px 12px 40px;}
237
  h2.top{color:#eaeaf2;margin:6px 0 16px}
238
 
239
- /* صفحة الإدخال ثابتة الارتفاع ولا تتغير بعد الرفع */
240
- .input-panel{background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:16px;
241
- box-shadow:0 16px 38px rgba(0,0,0,.35); min-height:360px; display:flex; flex-direction:column; gap:12px;}
242
- .small{opacity:.9;color:#d9dee8}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
- /* منع لوحة المعاينة الخاصة بالملفات التي تغيّر التخطيط */
245
- [data-testid="file"] .file-preview, [data-testid="file"] .file-preview * { display:none !important; }
246
- [data-testid="file"] .grid-wrap { display:block !important; }
247
- .upload-like{border:2px dashed #3b3f52;background:#121318;border-radius:12px;padding:12px;color:#cfd5e3;min-height:90px}
 
 
248
 
 
 
249
  .button-primary>button{background:linear-gradient(180deg,var(--accent),var(--accent2));border:none;color:#0b0d10;font-weight:800}
250
  .button-primary>button:hover{filter:brightness(.95)}
251
  textarea{min-height:120px}
@@ -273,13 +321,12 @@ textarea{min-height:120px}
273
  }
274
  .q-actions .q-submit:disabled{opacity:.5;cursor:not-allowed}
275
  .q-note{color:#ffd1d6}
276
- .q-note.warn{color:#ffd1d6}
277
  """
278
 
279
- # ------------------ JS: ربط Submit بعد الرندر (مع Output مخفي لضمان التنفيذ) ------------------
280
  ATTACH_LISTENERS_JS = """
281
  () => {
282
- // اربط مرة واحدة فقط
283
  if (window.__q_submit_bound_multi2) { return 'already'; }
284
  window.__q_submit_bound_multi2 = true;
285
 
@@ -302,30 +349,26 @@ ATTACH_LISTENERS_JS = """
302
 
303
  const chosenLabel = chosen.closest('.opt');
304
 
305
- // حالة صحيحة: لوّن أخضر وأقفل السؤال كاملاً
306
  if (chosen.value === correct) {
307
  chosenLabel.classList.add('ok');
308
  if (badge){ badge.hidden=false; badge.className='q-badge ok'; badge.textContent='Correct!'; }
309
- // أقفل هذا السؤال فقط بعد الصح
310
  card.querySelectorAll('input[type="radio"]').forEach(i => i.disabled = true);
311
  e.target.disabled = true;
312
  if (note) note.textContent = '';
313
  return;
314
  }
315
 
316
- // حالة خاطئة: لوّن أحمر فقط، ولا تعطل أي شيءليقدر يجرّب خيار آخر
317
- chosenLabel.classList.add('err'); // اتركه أحمر
318
  if (badge){ badge.hidden=false; badge.className='q-badge err'; badge.textContent='Incorrect.'; }
319
  if (note) note.textContent = '';
320
- // مهم: لا تعطّل الراديو ولا الزر
321
  });
322
 
323
  return 'wired-multi2';
324
  }
325
  """
326
 
327
-
328
-
329
  # ------------------ واجهة Gradio ------------------
330
  with gr.Blocks(title="Question Generator", css=CSS) as demo:
331
  gr.Markdown("<h2 class='top'>Question Generator</h2>")
@@ -335,13 +378,23 @@ with gr.Blocks(title="Question Generator", css=CSS) as demo:
335
  with page1:
336
  gr.Markdown("اختر **أحد** الخيارين ثم اضغط الزر.", elem_classes=["small"])
337
  text_area = gr.Textbox(lines=6, placeholder="ألصق نصك هنا...", label="لصق نص")
338
- file_comp = gr.File(label="أو ارفع ملف (PDF / TXT)", file_count="single",
339
- file_types=[".pdf",".txt"], type="filepath", elem_classes=["upload-like"])
 
 
 
 
 
 
 
 
 
 
340
  num_q = gr.Slider(4, 20, value=DEFAULT_NUM_QUESTIONS, step=1, label="عدد الأسئلة")
341
  with gr.Accordion("خيارات PDF المصوّر (اختياري)", open=False):
342
  trocr_model = gr.Dropdown(
343
  choices=[
344
- "microsoft/trocr-base-printed",
345
  "microsoft/trocr-large-printed",
346
  "microsoft/trocr-base-handwritten",
347
  "microsoft/trocr-large-handwritten",
 
1
  # -*- coding: utf-8 -*-
2
+ # صفحتان ثابتتان + Submit لكل سؤال (محاولات متعددة) + واجهة إدخال لا تتغير أبعادها بعد رفع الملف
3
 
4
  import os, json, uuid, random, unicodedata
5
  from dataclasses import dataclass
 
70
  def file_to_text(path: str, model_id=DEFAULT_TROCR_MODEL, zoom=DEFAULT_TROCR_ZOOM) -> Tuple[str,str]:
71
  ext = Path(path).suffix.lower()
72
  if ext == ".txt":
73
+ with open(path,"r",encoding="utf-8",errors="ignore") as f: return f.read(), "plain text"
 
74
  raw = extract_text_with_pypdf(path)
75
  if is_good(raw): return raw, "embedded (pypdf)"
76
  return extract_text_with_ocr(path, model_id, zoom), "OCR (TrOCR)"
 
81
  out=[]
82
  for ln in t.splitlines():
83
  if re2.match(r"^\s*--- \[Page \d+\] ---\s*$", ln): continue
84
+ if re2.match(r"^\s*(Page\s*\d+|صفحة\s*\د+)\s*$", ln): continue
85
  if re2.match(r"^\s*[-–—_*]{3,}\s*$", ln): continue
86
  out.append(ln)
87
  return "\n".join(out)
 
225
  recs = to_records(items)
226
  return render_quiz_html(recs), gr.update(visible=False), gr.update(visible=True), ""
227
 
228
+ # ------------------ CSS (ثبات واجهة الإدخال + منع تمدد مكوّن الملف) ------------------
229
  CSS = """
230
  :root{
231
  --bg:#0e0e11; --panel:#15161a; --card:#1a1b20; --muted:#a7b0be;
 
235
  .gradio-container{max-width:980px;margin:0 auto;padding:12px 12px 40px;}
236
  h2.top{color:#eaeaf2;margin:6px 0 16px}
237
 
238
+ /* 1) لوحة الإدخال ثابتة الارتفاع */
239
+ .input-panel{
240
+ background:var(--panel);
241
+ border:1px solid var(--border);
242
+ border-radius:14px;
243
+ padding:16px;
244
+ box-shadow:0 16px 38px rgba(0,0,0,.35);
245
+ min-height:420px; /* ارتفاع ثابت مريح */
246
+ display:flex; flex-direction:column; gap:12px;
247
+ }
248
+
249
+ /* 2) حاوية إسقاط ثابتة الارتفاع تمنع أي تمدد */
250
+ .drop-wrap{
251
+ position:relative;
252
+ height:120px; /* ثبّت ارتفاع منطقة الرفع */
253
+ min-height:120px; max-height:120px;
254
+ overflow:hidden; /* قصّ أي معاينات */
255
+ border:2px dashed #3b3f52;
256
+ background:#121318;
257
+ border-radius:12px;
258
+ padding:10px;
259
+ color:#cfd5e3;
260
+ }
261
+
262
+ /* 3) امنع مكوّن الملف من تغيير الحجم داخليًا مهما أضاف معاينات */
263
+ .drop-wrap [data-testid="file"]{
264
+ position:absolute; inset:0;
265
+ height:100%; width:100%;
266
+ overflow:hidden !important;
267
+ }
268
+ .drop-wrap [data-testid="file"] *{ max-height:100% !important; }
269
+
270
+ /* 4) إخفاء أي معاينة/شبكة/اسم ملف طويلة قد تضيفها إصدارات مختلفة من Gradio */
271
+ .drop-wrap [class*="preview"],
272
+ .drop-wrap [class*="file-preview"],
273
+ .drop-wrap .file-preview,
274
+ .drop-wrap .file-preview * ,
275
+ .drop-wrap .upload-preview,
276
+ .drop-wrap .grid,
277
+ .drop-wrap .grid-wrap,
278
+ .drop-wrap .thumbnail,
279
+ .drop-wrap .thumbnails,
280
+ .drop-wrap .label,
281
+ .drop-wrap .hidden,
282
+ .drop-wrap .file-name,
283
+ .drop-wrap .file-info,
284
+ .drop-wrap .file-size {
285
+ display:none !important;
286
+ }
287
 
288
+ /* 5) اترك منطقة النقر على الرفع فعّالة على المساحة كلها */
289
+ .drop-wrap input[type="file"]{
290
+ position:absolute; inset:0;
291
+ height:100%; width:100%;
292
+ opacity:0; cursor:pointer;
293
+ }
294
 
295
+ /* عناصر أخرى */
296
+ .small{opacity:.9;color:#d9dee8}
297
  .button-primary>button{background:linear-gradient(180deg,var(--accent),var(--accent2));border:none;color:#0b0d10;font-weight:800}
298
  .button-primary>button:hover{filter:brightness(.95)}
299
  textarea{min-height:120px}
 
321
  }
322
  .q-actions .q-submit:disabled{opacity:.5;cursor:not-allowed}
323
  .q-note{color:#ffd1d6}
324
+ .q-note.warn{color:#ffd1د6}
325
  """
326
 
327
+ # ------------------ JS: Submit (محاولات متعددة) ------------------
328
  ATTACH_LISTENERS_JS = """
329
  () => {
 
330
  if (window.__q_submit_bound_multi2) { return 'already'; }
331
  window.__q_submit_bound_multi2 = true;
332
 
 
349
 
350
  const chosenLabel = chosen.closest('.opt');
351
 
352
+ // صحيح: اخضر + قفل السؤال كاملاً
353
  if (chosen.value === correct) {
354
  chosenLabel.classList.add('ok');
355
  if (badge){ badge.hidden=false; badge.className='q-badge ok'; badge.textContent='Correct!'; }
 
356
  card.querySelectorAll('input[type="radio"]').forEach(i => i.disabled = true);
357
  e.target.disabled = true;
358
  if (note) note.textContent = '';
359
  return;
360
  }
361
 
362
+ // خطأ: أحمر فقط، ولا تعطيل لأي عنصرتسمح بمحاولات غير محدودة
363
+ chosenLabel.classList.add('err');
364
  if (badge){ badge.hidden=false; badge.className='q-badge err'; badge.textContent='Incorrect.'; }
365
  if (note) note.textContent = '';
 
366
  });
367
 
368
  return 'wired-multi2';
369
  }
370
  """
371
 
 
 
372
  # ------------------ واجهة Gradio ------------------
373
  with gr.Blocks(title="Question Generator", css=CSS) as demo:
374
  gr.Markdown("<h2 class='top'>Question Generator</h2>")
 
378
  with page1:
379
  gr.Markdown("اختر **أحد** الخيارين ثم اضغط الزر.", elem_classes=["small"])
380
  text_area = gr.Textbox(lines=6, placeholder="ألصق نصك هنا...", label="لصق نص")
381
+
382
+ # مكوّن الملف داخل حاوية drop-wrap لتثبيت الارتفاع
383
+ with gr.Box(elem_classes=["drop-wrap"]):
384
+ file_comp = gr.File(
385
+ label="أو ارفع ملف (PDF / TXT)",
386
+ file_count="single",
387
+ file_types=[".pdf", ".txt"],
388
+ type="filepath",
389
+ elem_classes=[],
390
+ show_label=False
391
+ )
392
+
393
  num_q = gr.Slider(4, 20, value=DEFAULT_NUM_QUESTIONS, step=1, label="عدد الأسئلة")
394
  with gr.Accordion("خيارات PDF المصوّر (اختياري)", open=False):
395
  trocr_model = gr.Dropdown(
396
  choices=[
397
+ "مicrosoft/trocr-base-printed",
398
  "microsoft/trocr-large-printed",
399
  "microsoft/trocr-base-handwritten",
400
  "microsoft/trocr-large-handwritten",