import gradio as gr import subprocess import html def clean_uri(raw: str) -> str: """تنظيف URI بإزالة زوايا < > وأي بقايا مثل ;http:""" uri = raw.strip() # إزالة الزوايا إن وُجدت if uri.startswith("<") and uri.endswith(">"): uri = uri[1:-1].strip() # إزالة أي بقايا مسار تالفة تأتي أحياناً من البيانات if ";http" in uri: uri = uri.split(";http")[0] # إزالة فواصل/فراغات زائدة uri = uri.rstrip(" ,;") return uri def parse_annif_output(text: str) -> str: """ يحول خرج annif (TSV: uri \t label \t score) إلى HTML مرتب بروابط قابلة للنقر. """ lines = [ln for ln in text.strip().split("\n") if ln.strip()] if not lines: return "

لا توجد نتائج.

" items = [] for ln in lines: # تجاهل أي سطور تعليق محتملة if ln.lstrip().startswith("#"): continue parts = ln.split("\t") if len(parts) < 3: continue raw_uri, raw_label, raw_score = parts[0], parts[1], parts[2] uri = clean_uri(raw_uri) label = html.escape(raw_label.strip().strip('"')) # تنسيق الدرجة try: score = float(raw_score) score_str = f"{score:.3f}" except Exception: score_str = html.escape(raw_score) # نبني عنصر HTML مع رابط مطلق وفتح في تبويب جديد # ملاحظة: إذا الـ URI لا يبدأ بـ http(s)، المتصفح قد يعامل الرابط كنسبي. items.append( f'' f'{html.escape(uri)}' f'{label}' f'{score_str}' f'' ) if not items: return "

لا توجد نتائج.

" table = ( "" "" "" "" "" "" "" "" "" + "".join(items) + "" "
URIالمصطلحالدرجة
" ) return table def suggest_subjects(input_text: str, limit: int): if not input_text or not input_text.strip(): return "

من فضلك أدخل نصًا.

" try: # نستدعي annif بصيغة TSV بدون --json proc = subprocess.run( ["annif", "suggest", "ar-annif", "-", "--limit", str(int(limit))], input=input_text, capture_output=True, text=True, check=True, ) return parse_annif_output(proc.stdout) except subprocess.CalledProcessError as e: # نعرض stderr للمساعدة في التشخيص err = html.escape(e.stderr or "") out = html.escape(e.stdout or "") return f"
خطأ من annif suggest:\nSTDERR:\n{err}\n\nSTDOUT:\n{out}
" except Exception as ex: return f"
خطأ: {html.escape(str(ex))}
" with gr.Blocks(css=""" .gradio-container { direction: rtl; font-family: 'Noto Naskh Arabic', system-ui, sans-serif; } """) as demo: gr.Markdown("## 🔎 استخراج رؤوس الموضوعات (Annif)") with gr.Row(): text = gr.Textbox(label="النص", lines=6, placeholder="ألصق هنا نصًا عربيًا...") with gr.Row(): limit = gr.Slider(1, 20, value=5, step=1, label="عدد النتائج") btn = gr.Button("اقتراح الرؤوس", variant="primary") out = gr.HTML(label="النتائج") btn.click(fn=suggest_subjects, inputs=[text, limit], outputs=out) if __name__ == "__main__": # على Spaces لا حاجة لـ share=True عادةً demo.launch(server_name="0.0.0.0", server_port=7860)