DrugKnowledge commited on
Commit
fcb5af7
·
verified ·
1 Parent(s): c394db4

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +628 -0
app.py ADDED
@@ -0,0 +1,628 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ # ================== الاستيراد ==================
3
+ import gradio as gr
4
+ import pandas as pd
5
+ import numpy as np
6
+ import tempfile
7
+ import os, pickle
8
+ from datetime import datetime
9
+ import io, base64
10
+ import arabic_reshaper
11
+ from bidi.algorithm import get_display
12
+ import matplotlib.pyplot as plt
13
+ from sentence_transformers import SentenceTransformer, util
14
+ from Bio import Entrez
15
+ import gdown
16
+
17
+ # ================== دوال مساعدة ==================
18
+ def download_from_drive(file_id, output):
19
+ url = f"https://drive.google.com/uc?id={file_id}"
20
+ try:
21
+ gdown.download(url, output, quiet=True)
22
+ except Exception:
23
+ # إذا فشل التحميل من Drive نتابع لوجود ملفات محلية بالفعل
24
+ pass
25
+
26
+ # ================== إعدادات ومسارات ==================
27
+ Entrez.email = "emananter0123@gmail.com"
28
+ EMB_DIR = "embeddings_cache"
29
+ os.makedirs(EMB_DIR, exist_ok=True)
30
+
31
+ def embeddings_path(name):
32
+ return os.path.join(EMB_DIR, f"{name}_embeddings.pkl")
33
+
34
+ BOOKS_FILE, THESES_FILE, FAQ_FILE = "book.xlsx", "theses.xlsx", "faq.xlsx"
35
+
36
+ # محاولة تنزيل الملفات من Google Drive (إن أردتِ يمكنك حذف السطور التالية إذا الملفات محلية)
37
+ download_from_drive("1FElHiASfiVLeuHWYaqd2Q5foxWRlJT-O", BOOKS_FILE) # book.xlsx
38
+ download_from_drive("1K2Mtze6ZdvfKUsFMCOWlRBjDq-ZnJNrv", THESES_FILE) # theses.xlsx
39
+ download_from_drive("1ONjXtfv709BOxiIR0Qzmz6lQngnWEEso", FAQ_FILE) # faq.xlsx
40
+
41
+ # ================== تحميل البيانات المحلية ==================
42
+ def load_local_data():
43
+ if not os.path.exists(BOOKS_FILE) or not os.path.exists(THESES_FILE):
44
+ raise FileNotFoundError("تأكدي من رفع الملفات book.xlsx و theses.xlsx في مجلد المشروع.")
45
+ books = pd.read_excel(BOOKS_FILE).fillna("غير متوافر")
46
+ theses = pd.read_excel(THESES_FILE).fillna("غير متوافر")
47
+ # توحيد اسم عمود العنوان بين ملفات مختلفة
48
+ if "Title" not in books.columns and "العنوان" in books.columns:
49
+ books["Title"] = books["العنوان"].astype(str)
50
+ elif "Title" not in books.columns:
51
+ books["Title"] = books.iloc[:,0].astype(str)
52
+
53
+ if "Title" not in theses.columns and "العنوان" in theses.columns:
54
+ theses["Title"] = theses["العنوان"].astype(str)
55
+ elif "Title" not in theses.columns:
56
+ theses["Title"] = theses.iloc[:,0].astype(str)
57
+
58
+ return books, theses
59
+
60
+ books_df, theses_df = load_local_data()
61
+
62
+ # ================== نموذج Semantic ==================
63
+ MODEL_NAME = "all-MiniLM-L6-v2"
64
+ model = SentenceTransformer(MODEL_NAME)
65
+
66
+ def build_or_load_embeddings(df, name):
67
+ path = embeddings_path(name)
68
+ if os.path.exists(path):
69
+ try:
70
+ with open(path,"rb") as f:
71
+ emb = pickle.load(f)
72
+ if len(emb) == len(df):
73
+ return emb
74
+ except Exception:
75
+ pass
76
+ texts = df["Title"].astype(str).tolist()
77
+ emb = model.encode(texts, convert_to_numpy=True, show_progress_bar=True)
78
+ with open(path,"wb") as f:
79
+ pickle.dump(emb,f)
80
+ return emb
81
+
82
+ books_embeddings = build_or_load_embeddings(books_df,"books")
83
+ theses_embeddings = build_or_load_embeddings(theses_df,"theses")
84
+
85
+ # ================== FAQ & مساعد ذكي ==================
86
+ if os.path.exists(FAQ_FILE):
87
+ faq_df = pd.read_excel(FAQ_FILE).fillna("")
88
+ else:
89
+ faq_df = pd.DataFrame({
90
+ "السؤال":["مواعيد المكتبة","خدمات المكتبة","كيفية الاستعارة","التسجيل في بنك المعرفة","التصوير","أقسام المكتبة","التواصل"],
91
+ "الإجابة":[
92
+ "🕘 المكتبة تعمل من السبت إلى الخميس من 9 صباحًا حتى 2 ظهرًا",
93
+ "📌 خدمات المكتبة: الإحاطة الجارية، التسجيل على بنك المعرفة، التصوير، البحث الإلكتروني، الاستعارة الخارجية",
94
+ "✅ يتم استعارة الكتب لمدة أسبوعين (أو حسب القاعدة المحلية)",
95
+ "🔗 التسجيل في بنك المعرفة عبر موقع بنك المعرفة المصري",
96
+ "🖨️ التصوير متاح وفق القواعد",
97
+ "📚 المكتبة بها: قاعة المراجع، قاعة الكتب الدراسية، قاعة الدوريات، قاعة الرسائل الجامعية",
98
+ "☎️ للتواصل: <a href='https://www.facebook.com/share/1AuUSQUn4n/' target='_blank'>صفحة الفيسبوك</a>"
99
+ ]
100
+ })
101
+
102
+ faq_questions = faq_df["السؤال"].astype(str).tolist()
103
+ faq_answers = faq_df["الإجابة"].astype(str).tolist()
104
+
105
+ FAQ_EMB_PATH = embeddings_path("faq")
106
+ def build_or_load_faq_embeddings(questions):
107
+ if os.path.exists(FAQ_EMB_PATH):
108
+ try:
109
+ with open(FAQ_EMB_PATH,"rb") as f:
110
+ emb = pickle.load(f)
111
+ if len(emb) == len(questions):
112
+ return emb
113
+ except Exception:
114
+ pass
115
+ emb = model.encode(questions, convert_to_numpy=True, show_progress_bar=False)
116
+ with open(FAQ_EMB_PATH,"wb") as f:
117
+ pickle.dump(emb,f)
118
+ return emb
119
+
120
+ faq_embeddings = build_or_load_faq_embeddings(faq_questions)
121
+
122
+ def library_assistant_smart(question):
123
+ if not str(question).strip():
124
+ return "⚠️ من فضلك اكتب سؤالك"
125
+ q_emb = model.encode([str(question)], convert_to_numpy=True)
126
+ sims = util.cos_sim(q_emb, faq_embeddings)[0].cpu().numpy()
127
+ idx_best = int(np.argmax(sims))
128
+ best_score = sims[idx_best]
129
+ log_usage("المساعد الذكي", question, "FAQ", 1)
130
+ if best_score < 0.75:
131
+ return "❗ لم أجد إجابة مناسبة، حاول صياغة سؤالك بشكل مختلف."
132
+ return f"<b>الإجابة الأقرب:</b><br>{faq_answers[idx_best]}<br><i>درجة التشابه: {best_score:.2f}</i>"
133
+
134
+ # ================== CSS ==================
135
+ CUSTOM_CSS = """<style>
136
+ .styled-table {
137
+ border-collapse: collapse;
138
+ margin: 15px 0;
139
+ font-size: 14px;
140
+ width: 100%;
141
+ text-align: right;
142
+ direction: rtl;
143
+ }
144
+ .styled-table th, .styled-table td {
145
+ border: 1px solid #ddd;
146
+ padding: 8px;
147
+ }
148
+ .styled-table tr:nth-child(even) {background-color: #f9f9f9;}
149
+ .styled-table tr:nth-child(odd) {background-color: #ffffff;}
150
+ .styled-table th {background-color: #4da6ff; color: white;}
151
+ a {color: #0066cc; text-decoration: none;}
152
+ a:hover {text-decoration: underline;}
153
+ </style>"""
154
+
155
+ # ================== تتبع الاستخدام ==================
156
+ def log_usage(tab, query="", mode="", results_count=0):
157
+ file_path = "usage_stats.xlsx"
158
+ entry = pd.DataFrame([{
159
+ "التاريخ": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
160
+ "التب": tab,
161
+ "الكلمة المفتاحية": query,
162
+ "نوع البحث": mode,
163
+ "عدد النتائج": results_count
164
+ }])
165
+ if os.path.exists(file_path):
166
+ try:
167
+ old = pd.read_excel(file_path)
168
+ entry = pd.concat([old, entry], ignore_index=True)
169
+ except Exception:
170
+ entry = entry
171
+ entry.to_excel(file_path, index=False)
172
+
173
+ # ================== عرض إحصاءات الاستخدام ==================
174
+ def show_usage_stats():
175
+ file_path = "usage_stats.xlsx"
176
+ if not os.path.exists(file_path):
177
+ return "<p>📊 لا توجد بيانات بعد</p>"
178
+ df = pd.read_excel(file_path)
179
+ total = len(df)
180
+ by_tab = df["التب"].value_counts()
181
+ fig, ax = plt.subplots()
182
+ labels = [get_display(arabic_reshaper.reshape(str(t))) for t in by_tab.index]
183
+ ax.bar(labels, by_tab.values)
184
+ for i, v in enumerate(by_tab.values):
185
+ ax.text(i, v + 0.1, str(v), ha='center', va='bottom', fontsize=10)
186
+ ax.set_title(get_display(arabic_reshaper.reshape("عدد مرات استخدام كل تب")))
187
+ ax.set_xlabel(get_display(arabic_reshaper.reshape("التب")))
188
+ ax.set_ylabel(get_display(arabic_reshaper.reshape("عدد العمليات")))
189
+ plt.xticks(rotation=30, ha='right')
190
+ buf = io.BytesIO()
191
+ plt.tight_layout()
192
+ plt.savefig(buf, format="png")
193
+ plt.close(fig)
194
+ buf.seek(0)
195
+ img = base64.b64encode(buf.read()).decode("utf-8")
196
+ summary = f"<h3>📈 إجمالي الاستخدام: {total} عملية</h3>"
197
+ html = summary + "<img src='data:image/png;base64," + img + "'/>"
198
+ return CUSTOM_CSS + html + df.to_html(escape=False, index=False, classes="styled-table")
199
+
200
+ # ================== تحويل نتائج إلى HTML أنيق ==================
201
+ def results_to_html(df):
202
+ if df.empty:
203
+ return "<p>❌ لا توجد نتائج</p>"
204
+ html = CUSTOM_CSS + "<div style='direction:rtl;text-align:right;'>"
205
+ for i, row in df.iterrows():
206
+ html += "<table class='styled-table' style='margin-bottom:15px;'>"
207
+ html += f"<caption style='caption-side:top;text-align:right;font-weight:bold;margin-bottom:5px;'>📘 النتيجة {i+1}</caption>"
208
+ for col, val in row.items():
209
+ html += f"<tr><th>{col}</th><td>{val}</td></tr>"
210
+ html += "</table>"
211
+ html += "</div>"
212
+ return html
213
+
214
+ def df_to_html(df):
215
+ if isinstance(df, str):
216
+ return df
217
+ if df.empty:
218
+ return "<p>❌ لا توجد نتائج</p>"
219
+ return CUSTOM_CSS + df.to_html(escape=False, index=False, classes="styled-table")
220
+
221
+ # ================== حفظ النتائج إلى ملف Excel ==================
222
+ def save_to_excel(df):
223
+ if df is None or (isinstance(df, pd.DataFrame) and df.empty):
224
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
225
+ pd.DataFrame().to_excel(tmp.name, index=False)
226
+ return tmp.name
227
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
228
+ if isinstance(df, pd.DataFrame):
229
+ df.to_excel(tmp.name, index=False)
230
+ else:
231
+ pd.DataFrame({"result_html":[str(df)]}).to_excel(tmp.name, index=False)
232
+ return tmp.name
233
+
234
+ # ================== البحث المحلي ==================
235
+ def local_search_df(query, category, mode):
236
+ if not query or not str(query).strip():
237
+ return "<p>⚠️ اكتب كلمة أو جملة للبحث</p>", pd.DataFrame()
238
+
239
+ if mode == "نصي":
240
+ if category == "Books":
241
+ if "العنوان" in books_df.columns:
242
+ df = books_df[books_df["العنوان"].astype(str).str.contains(query, case=False, na=False)]
243
+ else:
244
+ df = books_df[books_df["Title"].astype(str).str.contains(query, case=False, na=False)]
245
+ else:
246
+ if "العنوان" in theses_df.columns:
247
+ df = theses_df[theses_df["العنوان"].astype(str).str.contains(query, case=False, na=False)]
248
+ else:
249
+ df = theses_df[theses_df["Title"].astype(str).str.contains(query, case=False, na=False)]
250
+ else:
251
+ q_emb = model.encode([query], convert_to_numpy=True)
252
+ if category == "Books":
253
+ scores = util.cos_sim(q_emb, books_embeddings)[0].cpu().numpy()
254
+ idx = np.argsort(-scores)
255
+ df = books_df.iloc[idx]
256
+ else:
257
+ scores = util.cos_sim(q_emb, theses_embeddings)[0].cpu().numpy()
258
+ idx = np.argsort(-scores)
259
+ df = theses_df.iloc[idx]
260
+
261
+ if df is None or df.empty:
262
+ df = pd.DataFrame([{"نتيجة":"❌ لم يتم العثور على نتائج"}])
263
+ else:
264
+ if "Title" in df.columns:
265
+ df = df.drop(columns=["Title"])
266
+
267
+ log_usage("البحث المحلي", query, mode, len(df) if isinstance(df, pd.DataFrame) else 0)
268
+ html_results = results_to_html(df)
269
+ return html_results, df
270
+
271
+ # ================== البحث في PubMed ==================
272
+ def search_pubmed_html(query, max_results=5):
273
+ if not query or not str(query).strip():
274
+ return "<p>⚠️ اكتب كلمة للبحث</p>"
275
+ try:
276
+ handle = Entrez.esearch(db="pubmed", term=query, retmax=max_results)
277
+ record = Entrez.read(handle)
278
+ handle.close()
279
+ ids = record.get("IdList", [])
280
+ if not ids:
281
+ return "<p>❌ لا توجد نتائج</p>"
282
+
283
+ handle = Entrez.efetch(db="pubmed", id=",".join(ids), rettype="xml", retmode="xml")
284
+ records = Entrez.read(handle)
285
+ handle.close()
286
+
287
+ rows = []
288
+ for rec in records.get('PubmedArticle', []):
289
+ try:
290
+ title = rec['MedlineCitation']['Article']['ArticleTitle']
291
+ pmid = rec['MedlineCitation']['PMID']
292
+ link = f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/"
293
+ rows.append({
294
+ "الموقع": "PubMed",
295
+ "الوصف": "قاعدة بيانات PubMed",
296
+ "العنوان": title,
297
+ "الرابط": f"<a href='{link}' target='_blank'>فتح</a>"
298
+ })
299
+ except Exception:
300
+ continue
301
+
302
+ if not rows:
303
+ return "<p>❌ لم يتم العثور على نتائج صالحة.</p>"
304
+
305
+ df = pd.DataFrame(rows)
306
+ log_usage("المصادر الخارجية", query, "PubMed", len(df))
307
+ return CUSTOM_CSS + df.to_html(escape=False, index=False, classes="styled-table")
308
+
309
+ except Exception as e:
310
+ return f"<p>❌ حدث خطأ أثناء جلب البيانات من PubMed:<br>{str(e)}</p>"
311
+
312
+ # ================== البحث في المواقع الخارجية ==================
313
+ EXTERNAL_LINKS = {
314
+ "Google Scholar": {"url": "https://scholar.google.com/scholar?q=", "desc": "محرك بحث للأبحاث"},
315
+ "PubMed": {"url": "https://pubmed.ncbi.nlm.nih.gov/?term=", "desc": "قاعدة بيانات PubMed"},
316
+ "Europe PMC": {"url": "https://europepmc.org/search?query=", "desc": "أبحاث Europe PMC"},
317
+ "ClinicalTrials.gov": {"url": "https://clinicaltrials.gov/search?term=", "desc": "تجارب سريرية"},
318
+ "OpenFDA": {"url": "https://open.fda.gov/", "desc": "بيانات الأدوية FDA"},
319
+ "MedlinePlus": {"url": "https://medlineplus.gov/search/?query=", "desc": "معلومات طبية"},
320
+ "DrugBank": {"url": "https://go.drugbank.com/", "desc": "قاعدة بيانات الأدوية"},
321
+ "WHO GHO": {"url": "https://www.who.int/data/gho/", "desc": "بيانات الصحة العالمية"},
322
+ "ICD-11": {"url": "https://icd.who.int/browse11/l-m/en", "desc": "التصنيف الدولي للأمراض"},
323
+ "ClinicalKey": {"url": "https://www.clinicalkey.com/search?q=", "desc": "مصادر طبية شاملة"},
324
+ "Scimago": {"url": "https://www.scimagojr.com/journalsearch.php?q=", "desc": "تصنيف الدوريات"},
325
+ "EKB": {"url": "https://www.ekb.eg/", "desc": "بنك المعرفة المصري"}
326
+ }
327
+
328
+ def external_search_html(query, site):
329
+ try:
330
+ if not query or not str(query).strip():
331
+ return "<p>⚠️ من فضلك اكتب كلمة للبحث</p>"
332
+
333
+ if not site or site not in EXTERNAL_LINKS:
334
+ return f"<p>⚠️ الموقع المحدد غير معروف أو غير موجود في القائمة.</p>"
335
+
336
+ if site == "PubMed":
337
+ return search_pubmed_html(query)
338
+
339
+ base = EXTERNAL_LINKS[site]["url"]
340
+ desc = EXTERNAL_LINKS[site]["desc"]
341
+ if "?" in base or "=" in base:
342
+ link = f"{base}{query}"
343
+ else:
344
+ link = base
345
+ df = pd.DataFrame([
346
+ {"اسم الموقع": site, "الوصف": desc, "رابط البحث": f"<a href='{link}' target='_blank'>{link}</a>"}
347
+ ])
348
+ log_usage("المصادر الخارجية", query, site, 1)
349
+ html_table = df.to_html(escape=False, index=False, classes="styled-table")
350
+ return CUSTOM_CSS + f"<h3>🔗 نتائج البحث في {site}</h3>" + html_table
351
+
352
+ except Exception as e:
353
+ return f"<p>❌ حدث خطأ أثناء تنفيذ البحث:<br>{str(e)}</p>"
354
+
355
+ # ================== البحث في الدوريات ==================
356
+ JOURNALS = {
357
+ "Elsevier Journal Finder": {"url": "https://journalfinder.elsevier.com/", "desc": "اقتراح مجلات النشر"},
358
+ "Springer Journal Suggester": {"url": "https://journalsuggester.springer.com/", "desc": "اقتراح مجلات النشر"},
359
+ "DOAJ": {"url": "https://doaj.org/search/journals?ref=homepage-box&source=", "desc": "مجلات مفتوحة الوصول"},
360
+ "Journal Citation Reports": {"url": "https://jcr.clarivate.com/jcr/browse-journals?query=", "desc": "تقييم الدوريات العلمية"},
361
+ "Scimago": {"url": "https://www.scimagojr.com/journalsearch.php?q=", "desc": "تصنيف الدوريات"},
362
+ "Google Scholar": {"url": "https://scholar.google.com/scholar?q=", "desc": "بحث عام"}
363
+ }
364
+
365
+ def journal_search_html(query, site):
366
+ try:
367
+ if not query or not str(query).strip():
368
+ return "<p>⚠️ من فضلك اكتب كلمة للبحث</p>"
369
+ if not site or site not in JOURNALS:
370
+ return f"<p>⚠️ الموقع المحدد غير معروف أو غير موجود في قائمة الدوريات.</p>"
371
+ base = JOURNALS[site]["url"]
372
+ desc = JOURNALS[site]["desc"]
373
+ if "?" in base or "=" in base:
374
+ link = f"{base}{query}"
375
+ else:
376
+ link = base
377
+ df = pd.DataFrame([{"اسم الموقع": site, "الوصف": desc, "رابط البحث": f"<a href='{link}' target='_blank'>{link}</a>"}])
378
+ log_usage("الدوريات", query, site, 1)
379
+ html_table = df.to_html(escape=False, index=False, classes="styled-table")
380
+ return CUSTOM_CSS + f"<h3>📄 نتائج البحث في {site}</h3>" + html_table
381
+ except Exception as e:
382
+ return f"<p>❌ حدث خطأ أثناء تنفيذ البحث:<br>{str(e)}</p>"
383
+
384
+ # ================== أدوات الذكاء الاصطناعي ==================
385
+ AI_TOOLS = {
386
+ "Semantic Scholar": {"url": "https://www.semanticscholar.org/search?q=", "desc": "بحث أكاديمي يعتمد على الذكاء الاصطناعي"},
387
+ "Connected Papers": {"url": "https://www.connectedpapers.com/search?q=", "desc": "خرائط علاقات بين الأبحاث"},
388
+ "Research Rabbit": {"url": "https://www.researchrabbitapp.com/", "desc": "تتبع شبكات الأبحاث"},
389
+ "Elicit": {"url": "https://elicit.org/", "desc": "مساعد بحث علمي يستخدم AI"},
390
+ "Explainpaper": {"url": "https://www.explainpaper.com/", "desc": "تبسيط الأبحاث العلمية"},
391
+ "Consensus": {"url": "https://consensus.app/search/?q=", "desc": "إجابات سريعة مدعومة بالأدلة"}
392
+ }
393
+
394
+ def ai_search_html(query, site):
395
+ try:
396
+ if not query or not str(query).strip():
397
+ return "<p>⚠️ من فضلك اكتب كلمة للبحث</p>"
398
+ if not site or site not in AI_TOOLS:
399
+ return f"<p>⚠️ الأداة المحددة غير معروفة أو غير موجودة في القائمة.</p>"
400
+ base = AI_TOOLS[site]["url"]
401
+ desc = AI_TOOLS[site]["desc"]
402
+ if "?" in base or "=" in base:
403
+ link = f"{base}{query}"
404
+ else:
405
+ link = base
406
+ df = pd.DataFrame([{"اسم الأداة": site, "الوصف": desc, "رابط البحث": f"<a href='{link}' target='_blank'>{link}</a>"}])
407
+ log_usage("أدوات الذكاء الاصطناعي", query, site, 1)
408
+ html_table = df.to_html(escape=False, index=False, classes="styled-table")
409
+ return CUSTOM_CSS + f"<h3>🤖 نتائج البحث في {site}</h3>" + html_table
410
+ except Exception as e:
411
+ return f"<p>❌ حدث خطأ أثناء تنفيذ البحث:<br>{str(e)}</p>"
412
+
413
+ # ================== واجهة Gradio ==================
414
+ with gr.Blocks() as demo:
415
+ with gr.Tab("📚 عن المكتبة"):
416
+ with gr.Tabs():
417
+ with gr.Tab("🌟 رؤية المكتبة"):
418
+ gr.Markdown("تسعى المكتبة إلى أن تكون مركزًا معرفيًا متميزًا يدعم البحث والتعليم ويُسهم في بناء مجتمع المعرفة.")
419
+ with gr.Tab("🎯 رسالة المكتبة"):
420
+ gr.Markdown("تقديم خدمات معلوماتية متكاملة تُمكّن الباحثين والطلاب من الوصول إلى مصادر المعرفة بسهولة وكفاءة.")
421
+ with gr.Tab("🎯 أهداف المكتبة"):
422
+ gr.Markdown("- دعم العملية التعليمية والبحثية.\n- تيسير الوصول إلى المعلومات والمصادر العلمية.\n- تشجيع استخدام التقنيات الحديثة في البحث.\n- تعزيز ثقافة القراءة والتعلم الذاتي.")
423
+ with gr.Tab("🧩 الخدمات"):
424
+ with gr.Tabs():
425
+ with gr.Tab("📘 الإحاطة الجارية"):
426
+ gr.Markdown("خدمة تتيح للباحثين متابعة أحدث الإصدارات والموضوعات الجديدة في مجالات اهتمامهم.")
427
+ with gr.Tab("🔗 التسجيل في بنك المعرفة"):
428
+ gr.Markdown("يرجى ملء البيانات التالية للتسجيل في بنك المعرفة المصري:")
429
+
430
+ name = gr.Textbox(label="الاسم بالكامل", placeholder="اكتب اسمك الثلاثي هنا")
431
+ nid = gr.Textbox(label="رقم البطاقة", placeholder="14 رقمًا")
432
+ birth = gr.Textbox(label="تاريخ الميلاد", placeholder="مثال: 1999-05-12")
433
+ email = gr.Textbox(label="الإيميل الجامعي", placeholder="example@university.edu.eg")
434
+ phone = gr.Textbox(label="رقم التليفون", placeholder="010XXXXXXXXX")
435
+ submit_btn = gr.Button("إرسال الطلب")
436
+ output_msg = gr.HTML()
437
+
438
+ def save_registration(name, nid, birth, email, phone):
439
+ import csv, os
440
+ from datetime import datetime
441
+ file_path = "bank_requests.csv"
442
+ file_exists = os.path.exists(file_path)
443
+ with open(file_path, mode="a", newline="", encoding="utf-8") as f:
444
+ writer = csv.writer(f)
445
+ if not file_exists:
446
+ writer.writerow(["الاسم", "رقم البطاقة", "تاريخ الميلاد", "الإيميل الجامعي", "رقم التليفون", "تاريخ التسجيل"])
447
+ writer.writerow([name, nid, birth, email, phone, datetime.now().strftime("%Y-%m-%d %H:%M:%S")])
448
+ return "<b style='color:green;'>✅ تم استلام بياناتك بنجاح، يرجى مراجعة بريدك الجامعي خلال 24 ساعة.</b>"
449
+
450
+ submit_btn.click(save_registration, inputs=[name, nid, birth, email, phone], outputs=output_msg)
451
+
452
+ with gr.Tab("🖨️ التصوير"):
453
+ gr.Markdown("توفر المكتبة خدمة تصوير المواد العلمية وفق القواعد المعمول بها داخل المكتبة.")
454
+ with gr.Tab("🔍 البحث الإلكتروني"):
455
+ gr.Markdown("يمكن للباحثين استخدام أجهزة المكتبة للبحث في قواعد البيانات الإلكترونية والمصادر العلمية.")
456
+ with gr.Tab("📖 الاطلاع الداخلي"):
457
+ gr.Markdown("تتيح المكتبة للرواد قراءة الكتب والمراجع داخل القاعات المخصصة دون الحاجة إلى استعارتها خارجًا.")
458
+ with gr.Tab("📚 الاستعارة الخارجية"):
459
+ gr.Markdown("يمكن استعارة الكتب لمدة محددة وفق سياسات المكتبة مع إمكانية التجديد إذا لم يكن عليها حجز.")
460
+ # تم إزالة تبويب "📞 التواصل" القديم كما طلبتِ
461
+
462
+ with gr.Tab("🔎 البحث المحلي"):
463
+ query_local = gr.Textbox(label="اكتب كلمة البحث")
464
+ category = gr.Radio(["Books", "Theses"], label="اختر الفئة")
465
+ mode = gr.Radio(["نصي", "دلالي (Semantic)"], label="نوع البحث")
466
+ btn_local = gr.Button("بحث")
467
+
468
+ df_local_state = gr.State()
469
+ output_local_html = gr.HTML()
470
+ file_local = gr.File(label="⬇️ تحميل النتائج", visible=False)
471
+
472
+ btn_local.click(
473
+ local_search_df,
474
+ inputs=[query_local, category, mode],
475
+ outputs=[output_local_html, df_local_state]
476
+ )
477
+
478
+ btn_save_local = gr.Button("📥 حفظ النتائج")
479
+ btn_save_local.click(
480
+ save_to_excel,
481
+ inputs=df_local_state,
482
+ outputs=file_local
483
+ )
484
+
485
+ file_local.change(lambda x: gr.update(visible=True), inputs=file_local, outputs=file_local)
486
+
487
+ with gr.Tab("🌐 المصادر الخارجية"):
488
+ query_ext = gr.Textbox(label="كلمة البحث")
489
+ site_ext = gr.Dropdown(list(EXTERNAL_LINKS.keys()), label="اختر الموقع")
490
+ btn_ext = gr.Button("بحث")
491
+ output_ext = gr.HTML()
492
+ btn_ext.click(external_search_html, inputs=[query_ext, site_ext], outputs=output_ext)
493
+
494
+ with gr.Tab("📄 الدوريات"):
495
+ query_jour = gr.Textbox(label="كلمة البحث")
496
+ site_jour = gr.Dropdown(list(JOURNALS.keys()), label="اختر الموقع")
497
+ btn_jour = gr.Button("بحث")
498
+ output_jour = gr.HTML()
499
+ btn_jour.click(journal_search_html, inputs=[query_jour, site_jour], outputs=output_jour)
500
+
501
+ with gr.Tab("🤖 أدوات الذكاء الاصطناعي"):
502
+ query_ai = gr.Textbox(label="كلمة البحث")
503
+ site_ai = gr.Dropdown(list(AI_TOOLS.keys()), label="اختر الأداة")
504
+ btn_ai = gr.Button("بحث")
505
+ output_ai = gr.HTML()
506
+ btn_ai.click(ai_search_html, inputs=[query_ai, site_ai], outputs=output_ai)
507
+
508
+ with gr.Tab("📑 برامج إدارة المراجع"):
509
+ gr.Markdown("ℹ️ **تنويه:** هذه البرامج تحتاج تنزيل أو تثبيت الإضافات الخاصة بها.")
510
+ gr.Markdown("### • **Mendeley** \n🔗 [زيارة الموقع](https://www.mendeley.com/)")
511
+ gr.Markdown("### • **Zotero** \n🔗 [زيارة الموقع](https://www.zotero.org/)")
512
+ gr.Markdown("### • **EndNote** \n🔗 [زيارة الموقع](https://endnote.com/)")
513
+
514
+ with gr.Tab("💬 المساعد الذكي"):
515
+ q_faq = gr.Textbox(label="اسأل مساعد المكتبة")
516
+ ans_faq = gr.HTML()
517
+ btn_faq = gr.Button("إجابة")
518
+ btn_faq.click(library_assistant_smart, inputs=q_faq, outputs=ans_faq)
519
+
520
+ with gr.Tab("📊 الإحصاءات"):
521
+ stats_html = gr.HTML()
522
+ btn_stats = gr.Button("عرض الإحصاءات")
523
+ btn_stats.click(lambda: show_usage_stats(), outputs=stats_html)
524
+
525
+ # -------------------- تبويب "📞 تواصل معنا" المضاف بعد الإحصاءات --------------------
526
+ with gr.Tab("📞 تواصل معنا"):
527
+
528
+ gr.Markdown("""
529
+ ## 📞 تواصل معنا
530
+
531
+ يسعدنا تواصلك مع **مكتبة كلية الصيدلة – جامعة المنصورة** عبر القنوات التالية:
532
+
533
+ 📍 **العنوان:** الدور الرابع – المبنى الإداري – كلية الصيدلة – جامعة المنصورة
534
+ 📧 **البريد الإلكتروني:** [phrlib1@mans.edu.eg](mailto:phrlib1@mans.edu.eg)
535
+ 📞 **الهاتف:** 01066937446
536
+ """)
537
+
538
+ fb_link = "https://www.facebook.com/people/%D9%85%D9%83%D8%AA%D8%A8%D8%A9-%D9%83%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%B5%D9%8A%D8%AF%D9%84%D8%A9-%D8%AC%D8%A7%D9%85%D8%B9%D8%A9-%D8%A7%D9%84%D9%85%D9%86%D8%B5%D9%88%D8%B1%D8%A9/61554318849433/"
539
+ whatsapp_link = "https://wa.me/201066937446"
540
+ email_link = "mailto:phrlib1@mans.edu.eg"
541
+
542
+ gr.Markdown("### 🌐 قنوات التواصل السريعة:")
543
+
544
+ gr.HTML(f"""
545
+ <div style='display:flex; gap:10px; flex-wrap:wrap;'>
546
+ <a href='{fb_link}' target='_blank'>
547
+ <button style='background-color:#1877f2;color:white;padding:10px 18px;border:none;border-radius:8px;font-size:16px;cursor:pointer;'>Facebook</button>
548
+ </a>
549
+ <a href='{whatsapp_link}' target='_blank'>
550
+ <button style='background-color:#25d366;color:white;padding:10px 18px;border:none;border-radius:8px;font-size:16px;cursor:pointer;'>WhatsApp</button>
551
+ </a>
552
+ <a href='{email_link}' target='_blank'>
553
+ <button style='background-color:#444;color:white;padding:10px 18px;border:none;border-radius:8px;font-size:16px;cursor:pointer;'>Email</button>
554
+ </a>
555
+ </div>
556
+ """)
557
+
558
+ gr.Markdown("### 💬 أرسل رسالة إلى المكتبة:")
559
+
560
+ name_c = gr.Textbox(label="الاسم الكامل", placeholder="اكتب اسمك هنا")
561
+ email_c = gr.Textbox(label="البريد الإلكتروني", placeholder="example@domain.com")
562
+ message_c = gr.Textbox(label="الرسالة", lines=4, placeholder="اكتب رسالتك هنا...")
563
+ send_btn = gr.Button("📨 إرسال الرسالة")
564
+ result_msg = gr.Markdown("")
565
+
566
+ def save_contact(name, email, message):
567
+ import csv, os
568
+ file_path = "contact_messages.csv"
569
+ file_exists = os.path.exists(file_path)
570
+ with open(file_path, mode="a", newline="", encoding="utf-8") as f:
571
+ writer = csv.writer(f)
572
+ if not file_exists:
573
+ writer.writerow(["الاسم", "البريد الإلكتروني", "الرسالة"])
574
+ writer.writerow([name, email, message])
575
+ return "✅ تم إرسال رسالتك بنجاح، شكرًا لتواصلك معنا."
576
+
577
+ send_btn.click(save_contact, inputs=[name_c, email_c, message_c], outputs=result_msg)
578
+
579
+ # -------------------- 📂 تبويب إدارة خدمات المكتبة --------------------
580
+ with gr.Tab("📂 إدارة خدمات المكتبة"):
581
+ gr.Markdown("### 🔐 تسجيل الدخول لإدارة خدمات المكتبة")
582
+
583
+ password = gr.Textbox(label="كلمة المرور", type="password", placeholder="أدخل كلمة المرور هنا")
584
+ login_btn = gr.Button("دخول")
585
+ login_msg = gr.Markdown("")
586
+ admin_area = gr.Group(visible=False)
587
+
588
+ with admin_area:
589
+ gr.Markdown("### عرض طلبات التسجيل في بنك المعرفة")
590
+
591
+ refresh_btn = gr.Button("🔄 تحديث البيانات")
592
+ data_table = gr.Dataframe(headers=["الاسم", "رقم البطاقة", "تاريخ الميلاد", "الإيميل الجامعي", "رقم التليفون", "تاريخ التسجيل"], interactive=False)
593
+ download_btn = gr.Button("⬇️ تنزيل الطلبات كملف Excel")
594
+ download_file = gr.File(label="ملف Excel", visible=False)
595
+
596
+ import pandas as pd, os, tempfile
597
+
598
+ def check_password(pw):
599
+ correct_pw = "admin123" # ← يمكنك تغييرها إلى أي كلمة مرور تريدينها
600
+ if pw == correct_pw:
601
+ return gr.update(visible=True), "✅ تم تسجيل الدخول بنجاح."
602
+ else:
603
+ return gr.update(visible=False), "❌ كلمة المرور غير صحيحة."
604
+
605
+ login_btn.click(check_password, inputs=password, outputs=[admin_area, login_msg])
606
+
607
+ def load_requests():
608
+ file_path = "bank_requests.csv"
609
+ if not os.path.exists(file_path):
610
+ return pd.DataFrame(columns=["الاسم", "رقم البطاقة", "تاريخ الميلاد", "الإيميل الجامعي", "رقم التليفون", "تاريخ التسجيل"])
611
+ df = pd.read_csv(file_path)
612
+ return df
613
+
614
+ def export_excel(df):
615
+ if df is None or df.empty:
616
+ return gr.update(visible=False)
617
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
618
+ df.to_excel(tmp.name, index=False)
619
+ return gr.update(value=tmp.name, visible=True)
620
+
621
+ refresh_btn.click(load_requests, outputs=data_table)
622
+ download_btn.click(export_excel, inputs=data_table, outputs=download_file)
623
+
624
+ gr.Markdown("---\n📌 *المشروع أعدته eman anter*")
625
+
626
+ # تشغيل التطبيق
627
+ if __name__ == "__main__":
628
+ demo.launch(share=True)