AI_LibExplorer / app.py
DrugKnowledge's picture
Upload app.py
1ba1a28 verified
# app.py
# ================== الاستيراد ==================
import gradio as gr
import pandas as pd
import numpy as np
import tempfile
import os, pickle
from datetime import datetime
import io, base64
import arabic_reshaper
from bidi.algorithm import get_display
import matplotlib.pyplot as plt
from sentence_transformers import SentenceTransformer, util
from Bio import Entrez
import gdown
# --- إضافات خاصة بالـ Hugging Face dataset sync ---
from huggingface_hub import HfApi, hf_hub_download, upload_file
# ----------------------------------------------------------------
# ------------------ Data store & Google Drive sync (background sync) ------------------
import shutil, time, threading
from datetime import datetime
import os
DATA_FOLDER = "data_store"
DRIVE_FOLDER_ID = "1znvCrr63iSN2MWveAZvcXuGT3o5TfZc2" # Library_Backups folder ID on your Drive
def ensure_data_folder():
os.makedirs(DATA_FOLDER, exist_ok=True)
return DATA_FOLDER
def get_data_path(filename):
ensure_data_folder()
return os.path.join(DATA_FOLDER, filename)
# Attempt to import PyDrive2 and set up authentication functions.
try:
from pydrive2.auth import GoogleAuth
from pydrive2.drive import GoogleDrive
from oauth2client.service_account import ServiceAccountCredentials
_GD_AVAILABLE = True
except Exception:
_GD_AVAILABLE = False
def ensure_drive_auth():
"""
Authenticate to Google Drive using a Service Account JSON file (client_secrets.json)
placed inside data_store/. Works automatically on Spaces without manual login.
"""
if not _GD_AVAILABLE:
print("❌ PyDrive2 not available.")
return None
try:
service_account_path = os.path.join(DATA_FOLDER, "client_secrets.json")
if not os.path.exists(service_account_path):
print("⚠️ لم يتم العثور على ملف client_secrets.json في data_store/")
return None
# Create credentials from service account JSON
scope = ['https://www.googleapis.com/auth/drive']
credentials = ServiceAccountCredentials.from_json_keyfile_name(service_account_path, scope)
gauth = GoogleAuth()
gauth.credentials = credentials
drive = GoogleDrive(gauth)
print("✅ تم الاتصال بـ Google Drive باستخدام Service Account بنجاح.")
return drive
except Exception as e:
print("❌ فشل الاتصال بـ Google Drive:", e)
return None
def upload_to_drive(local_path, remote_name=None):
"""
Uploads a local file to Google Drive inside the folder defined by DRIVE_FOLDER_ID.
"""
try:
drive = ensure_drive_auth()
if drive is None:
print("⚠️ لم يتم الاتصال بـ Google Drive، لم يتم رفع الملف:", local_path)
return False
if remote_name is None:
remote_name = os.path.basename(local_path)
q = f"'{DRIVE_FOLDER_ID}' in parents and title = '{remote_name}' and trashed=false"
file_list = drive.ListFile({'q': q}).GetList()
if file_list:
f = file_list[0]
f.SetContentFile(local_path)
f.Upload()
print(f"🔁 تم تحديث الملف الموجود في Google Drive: {remote_name}")
else:
file_metadata = {'title': remote_name, 'parents': [{'id': DRIVE_FOLDER_ID}]}
f = drive.CreateFile(file_metadata)
f.SetContentFile(local_path)
f.Upload()
print(f"✅ تم رفع ملف جديد إلى Google Drive: {remote_name}")
return True
except Exception as e:
print("❌ خطأ أثناء رفع الملف إلى Google Drive:", e)
return False
def _background_sync(interval_seconds=30):
"""
Background thread to watch for new or modified CSV/XLSX files and upload them to Google Drive automatically.
"""
def _worker():
seen = {}
while True:
try:
# scan for csv and xlsx files in project root
for fname in os.listdir():
if fname.lower().endswith(('.csv', '.xlsx')) and fname not in (os.path.basename(__file__),):
try:
src_path = os.path.join(os.getcwd(), fname)
dst_path = get_data_path(fname)
# check modification time
mtime = os.path.getmtime(src_path)
if fname not in seen or seen[fname] < mtime:
shutil.copy2(src_path, dst_path)
seen[fname] = mtime
success = upload_to_drive(dst_path, os.path.basename(dst_path))
if success:
print(f"📤 تمت مزامنة الملف مع Google Drive: {fname} ({datetime.now().strftime('%Y-%m-%d %H:%M:%S')})")
else:
print(f"⚠️ فشل رفع الملف: {fname}")
except Exception as e:
print("❌ خطأ أثناء المزامنة:", e)
except Exception:
pass
time.sleep(interval_seconds)
t = threading.Thread(target=_worker, daemon=True)
t.start()
# تشغيل المزامنة التلقائية كل 20 ثانية
try:
_background_sync(interval_seconds=20)
except Exception as e:
print("⚠️ لم يتم تشغيل المزامنة التلقائية:", e)
# ------------------ end background sync ------------------
# ================== HF dataset sync configuration ==================
DATASET_REPO = "pharma-library/AI_LibExplorer"
HF_TOKEN = os.getenv("HF_TOKEN") # يجب وضع هذا التوكن في Secrets باسم HF_TOKEN
ADMIN_PASS = os.getenv("ADMIN_PASS", "pharmacy1") # ضع Secret باسم ADMIN_PASS بقيمة pharmacy1
# قائمة الملفات التي نريد التأكد من توافرها ومزامنتها
LOCAL_FILES = [
"usage_stats.xlsx",
"questions.csv",
"suggestions.csv",
"user_logs.csv",
"faculty_borrow.csv",
"bank_requests.csv"
]
def download_data_files():
"""Download listed files from dataset repo to local project root if present."""
for file_name in LOCAL_FILES:
try:
# hf_hub_download سيحفظ الملف في local_dir كما طلبنا
hf_hub_download(
repo_id=DATASET_REPO,
filename=file_name,
repo_type="dataset",
local_dir=".",
token=HF_TOKEN
)
print(f"✅ تم تنزيل {file_name} من الـ Dataset.")
except Exception as e:
# الملف غير موجود بعد في الـ dataset — ليس خطأ، سيتم إنشاؤه عند الحاجة
print(f"ℹ️ {file_name} غير موجود على الـ Dataset حالياً ({e}). سيُنشأ لاحقًا عند كتابة البيانات.")
def upload_data_file(file_path):
"""Upload given file_path (relative path) to the dataset repo (overwrites/creates)."""
try:
# upload_file من huggingface_hub سيرفع الملف إلى المسار path_in_repo المحدد
upload_file(
path_or_fileobj=file_path,
path_in_repo=os.path.basename(file_path),
repo_id=DATASET_REPO,
repo_type="dataset",
token=HF_TOKEN
)
print(f"✅ Uploaded {file_path} to {DATASET_REPO}")
except Exception as e:
print(f"⚠️ خطأ في رفع {file_path} إلى HF dataset: {e}")
# تحميل الملفات عند بداية تشغيل التطبيق
download_data_files()
# ================== end HF dataset sync ==================
# ================== دوال مساعدة ==================
def download_from_drive(file_id, output):
url = f"https://drive.google.com/uc?id={file_id}"
try:
gdown.download(url, output, quiet=True)
except Exception:
# إذا فشل التحميل من Drive نتابع لوجود ملفات محلية بالفعل
pass
# ================== إعدادات ومسارات ==================
Entrez.email = "emananter0123@gmail.com"
EMB_DIR = "embeddings_cache"
os.makedirs(EMB_DIR, exist_ok=True)
def embeddings_path(name):
return os.path.join(EMB_DIR, f"{name}_embeddings.pkl")
BOOKS_FILE, THESES_FILE, FAQ_FILE = "book.xlsx", "theses.xlsx", "faq.xlsx"
QUESTIONS_FILE = "questions.csv" # ملف حفظ الأسئلة
FACULTY_BORROW_FILE = "faculty_borrow.csv" # طلبات أعضاء هيئة التدريس للاستعارة
# محاولة تنزيل الملفات من Google Drive (إن أردتِ يمكنك حذف السطور التالية إذا الملفات محلية)
download_from_drive("1FElHiASfiVLeuHWYaqd2Q5foxWRlJT-O", BOOKS_FILE) # book.xlsx
download_from_drive("1K2Mtze6ZdvfKUsFMCOWlRBjDq-ZnJNrv", THESES_FILE) # theses.xlsx
download_from_drive("1ONjXtfv709BOxiIR0Qzmz6lQngnWEEso", FAQ_FILE) # faq.xlsx
# ================== تحميل البيانات المحلية ==================
def load_local_data():
if not os.path.exists(BOOKS_FILE) or not os.path.exists(THESES_FILE):
raise FileNotFoundError("تأكدي من رفع الملفات book.xlsx و theses.xlsx في مجلد المشروع.")
books = pd.read_excel(BOOKS_FILE).fillna("غير متوافر")
theses = pd.read_excel(THESES_FILE).fillna("غير متوافر")
# توحيد اسم عمود العنوان بين ملفات مختلفة
if "Title" not in books.columns and "العنوان" in books.columns:
books["Title"] = books["العنوان"].astype(str)
elif "Title" not in books.columns:
books["Title"] = books.iloc[:,0].astype(str)
if "Title" not in theses.columns and "العنوان" in theses.columns:
theses["Title"] = theses["العنوان"].astype(str)
elif "Title" not in theses.columns:
theses["Title"] = theses.iloc[:,0].astype(str)
return books, theses
books_df, theses_df = load_local_data()
# ================== نموذج Semantic ==================
MODEL_NAME = "all-MiniLM-L6-v2"
model = SentenceTransformer(MODEL_NAME)
def build_or_load_embeddings(df, name):
path = embeddings_path(name)
if os.path.exists(path):
try:
with open(path,"rb") as f:
emb = pickle.load(f)
if len(emb) == len(df):
return emb
except Exception:
pass
texts = df["Title"].astype(str).tolist()
emb = model.encode(texts, convert_to_numpy=True, show_progress_bar=True)
with open(path,"wb") as f:
pickle.dump(emb,f)
return emb
books_embeddings = build_or_load_embeddings(books_df,"books")
theses_embeddings = build_or_load_embeddings(theses_df,"theses")
# ================== FAQ & مساعد ذكي ==================
if os.path.exists(FAQ_FILE):
faq_df = pd.read_excel(FAQ_FILE).fillna("")
else:
faq_df = pd.DataFrame({
"السؤال":["مواعيد المكتبة","خدمات المكتبة","كيفية الاستعارة","التسجيل في بنك المعرفة","التصوير","أقسام المكتبة","التواصل"],
"الإجابة":[
"🕘 المكتبة تعمل من السبت إلى الخميس من 9 صباحًا حتى 2 ظهرًا",
"📌 خدمات المكتبة: الإحاطة الجارية، التسجيل على بنك المعرفة، التصوير، البحث الإلكتروني، الاستعارة الخارجية",
"✅ يتم استعارة الكتب لمدة أسبوعين (أو حسب القاعدة المحلية)",
"🔗 التسجيل في بنك المعرفة عبر موقع بنك المعرفة المصري",
"🖨️ التصوير متاح وفق القواعد",
"📚 المكتبة بها: قاعة المراجع، قاعة الكتب الدراسية، قاعة الدوريات، قاعة الرسائل الجامعية",
"☎️ للتواصل: <a href='https://www.facebook.com/share/1AuUSQUn4n/' target='_blank'>صفحة الفيسبوك</a>"
]
})
faq_questions = faq_df["السؤال"].astype(str).tolist()
faq_answers = faq_df["الإجابة"].astype(str).tolist()
FAQ_EMB_PATH = embeddings_path("faq")
def build_or_load_faq_embeddings(questions):
if os.path.exists(FAQ_EMB_PATH):
try:
with open(FAQ_EMB_PATH,"rb") as f:
emb = pickle.load(f)
if len(emb) == len(questions):
return emb
except Exception:
pass
emb = model.encode(questions, convert_to_numpy=True, show_progress_bar=False)
with open(FAQ_EMB_PATH,"wb") as f:
pickle.dump(emb,f)
return emb
faq_embeddings = build_or_load_faq_embeddings(faq_questions)
def library_assistant_smart(question):
if not str(question).strip():
return "⚠️ من فضلك اكتب سؤالك"
q_emb = model.encode([str(question)], convert_to_numpy=True)
sims = util.cos_sim(q_emb, faq_embeddings)[0].cpu().numpy()
idx_best = int(np.argmax(sims))
best_score = sims[idx_best]
log_usage("المساعد الذكي", question, "FAQ", 1)
if best_score < 0.75:
return "❗ لم أجد إجابة مناسبة، حاول صياغة سؤالك بشكل مختلف."
return f"<b>الإجابة الأقرب:</b><br>{faq_answers[idx_best]}<br><i>درجة التشابه: {best_score:.2f}</i>"
# ================== CSS ==================
CUSTOM_CSS = """
<style>
.styled-table{border-collapse:collapse;margin:15px 0;font-size:14px;width:100%;text-align:right;direction:rtl;}
.styled-table th,.styled-table td{border:1px solid #ddd;padding:8px;}
.styled-table tr:nth-child(even){background-color:#f9f9f9;}
.styled-table tr:nth-child(odd){background-color:#fff;}
.styled-table th{background-color:#4da6ff;color:white;}
a{color:#0066cc;text-decoration:none;}
a:hover{text-decoration:underline;}
.question-card{border:1px solid #ddd;padding:10px;margin-bottom:12px;border-radius:8px;background:#fff;direction:rtl;}
.question-meta{font-size:13px;color:#666;margin-bottom:6px;}
.answer{margin-top:8px;padding:8px;border-radius:6px;background:#f7f7f7;}
.anon{color:#999;font-style:italic;}
.tabs.svelte-1ipelgc{display:flex!important;flex-direction:row!important;flex-wrap:nowrap;overflow-x:auto;gap:6px;scrollbar-width:thin;scrollbar-color:#4da6ff #eee;background:#f5f8ff;padding:8px;border-radius:10px;}
.tabs.svelte-1ipelgc::-webkit-scrollbar{height:6px;}
.tabs.svelte-1ipelgc::-webkit-scrollbar-thumb{background:#4da6ff;border-radius:4px;}
.tabitem.svelte-1ipelgc{background:white;color:#333;border:1px solid #ccc;border-radius:8px;padding:8px 14px;cursor:pointer;transition:all .2s ease;font-size:15px;white-space:nowrap;flex-shrink:0;}
.tabitem.svelte-1ipelgc:hover{background:#e7f0ff;border-color:#4da6ff;}
.tabitem.svelte-1ipelgc[aria-selected='true']{background-color:#4da6ff!important;color:white!important;font-weight:bold;box-shadow:0 2px 5px rgba(0,0,0,.1);}
@media(max-width:768px){
.tabs.svelte-1ipelgc{flex-wrap:wrap;justify-content:center;}
.tabitem.svelte-1ipelgc{flex:1 1 45%;text-align:center;margin-bottom:4px;font-size:15px;}
}
</style>
"""
# ================== تتبع الاستخدام ==================
def log_usage(tab, query="", mode="", results_count=0):
file_path = "usage_stats.xlsx"
entry = pd.DataFrame([{
"التاريخ": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"التب": tab,
"الكلمة المفتاحية": query,
"نوع البحث": mode,
"عدد النتائج": results_count
}])
if os.path.exists(file_path):
try:
old = pd.read_excel(file_path)
entry = pd.concat([old, entry], ignore_index=True)
except Exception:
entry = entry
entry.to_excel(file_path, index=False)
# upload to HF dataset (auto backup)
try:
upload_data_file(file_path)
except Exception as e:
print("⚠️ خطأ أثناء رفع usage_stats:", e)
# ================== عرض إحصاءات الاستخدام ==================
def show_usage_stats():
file_path = "usage_stats.xlsx"
if not os.path.exists(file_path):
return "<p>📊 لا توجد بيانات بعد</p>"
df = pd.read_excel(file_path)
total = len(df)
by_tab = df["التب"].value_counts()
fig, ax = plt.subplots()
labels = [get_display(arabic_reshaper.reshape(str(t))) for t in by_tab.index]
ax.bar(labels, by_tab.values)
for i, v in enumerate(by_tab.values):
ax.text(i, v + 0.1, str(v), ha='center', va='bottom', fontsize=10)
ax.set_title(get_display(arabic_reshaper.reshape("عدد مرات استخدام كل تب")))
ax.set_xlabel(get_display(arabic_reshaper.reshape("التب")))
ax.set_ylabel(get_display(arabic_reshaper.reshape("عدد العمليات")))
plt.xticks(rotation=30, ha='right')
buf = io.BytesIO()
plt.tight_layout()
plt.savefig(buf, format="png")
plt.close(fig)
buf.seek(0)
img = base64.b64encode(buf.read()).decode("utf-8")
summary = f"<h3>📈 إجمالي الاستخدام: {total} عملية</h3>"
html = summary + "<img src='data:image/png;base64," + img + "'/>"
return CUSTOM_CSS + html + df.to_html(escape=False, index=False, classes="styled-table")
# ================== تحويل نتائج إلى HTML أنيق ==================
def results_to_html(df):
if df.empty:
return "<p>❌ لا توجد نتائج</p>"
html = CUSTOM_CSS + "<div style='direction:rtl;text-align:right;'>"
for i, row in df.iterrows():
html += "<table class='styled-table' style='margin-bottom:15px;'>"
html += f"<caption style='caption-side:top;text-align:right;font-weight:bold;margin-bottom:5px;'>📘 النتيجة {i+1}</caption>"
for col, val in row.items():
html += f"<tr><th>{col}</th><td>{val}</td></tr>"
html += "</table>"
html += "</div>"
return html
def df_to_html(df):
if isinstance(df, str):
return df
if df.empty:
return "<p>❌ لا توجد نتائج</p>"
return CUSTOM_CSS + df.to_html(escape=False, index=False, classes="styled-table")
# ================== حفظ النتائج إلى ملف Excel ==================
def save_to_excel(df):
if df is None or (isinstance(df, pd.DataFrame) and df.empty):
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
pd.DataFrame().to_excel(tmp.name, index=False)
return tmp.name
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
if isinstance(df, pd.DataFrame):
df.to_excel(tmp.name, index=False)
else:
pd.DataFrame({"result_html":[str(df)]}).to_excel(tmp.name, index=False)
return tmp.name
# ================== البحث المحلي ==================
def local_search_df(query, category, mode):
if not query or not str(query).strip():
return "<p>⚠️ اكتب كلمة أو جملة للبحث</p>", pd.DataFrame()
if mode == "نصي":
if category == "Books":
if "العنوان" in books_df.columns:
df = books_df[books_df["العنوان"].astype(str).str.contains(query, case=False, na=False)]
else:
df = books_df[books_df["Title"].astype(str).str.contains(query, case=False, na=False)]
else:
if "العنوان" in theses_df.columns:
df = theses_df[theses_df["العنوان"].astype(str).str.contains(query, case=False, na=False)]
else:
df = theses_df[theses_df["Title"].astype(str).str.contains(query, case=False, na=False)]
else:
q_emb = model.encode([query], convert_to_numpy=True)
if category == "Books":
scores = util.cos_sim(q_emb, books_embeddings)[0].cpu().numpy()
idx = np.argsort(-scores)
df = books_df.iloc[idx]
else:
scores = util.cos_sim(q_emb, theses_embeddings)[0].cpu().numpy()
idx = np.argsort(-scores)
df = theses_df.iloc[idx]
if df is None or df.empty:
df = pd.DataFrame([{"نتيجة":"❌ لم يتم العثور على نتائج"}])
else:
if "Title" in df.columns:
df = df.drop(columns=["Title"])
log_usage("البحث المحلي", query, mode, len(df) if isinstance(df, pd.DataFrame) else 0)
html_results = results_to_html(df)
return html_results, df
# ================== البحث في PubMed ==================
def search_pubmed_html(query, max_results=5):
if not query or not str(query).strip():
return "<p>⚠️ اكتب كلمة للبحث</p>"
try:
handle = Entrez.esearch(db="pubmed", term=query, retmax=max_results)
record = Entrez.read(handle)
handle.close()
ids = record.get("IdList", [])
if not ids:
return "<p>❌ لا توجد نتائج</p>"
handle = Entrez.efetch(db="pubmed", id=",".join(ids), rettype="xml", retmode="xml")
records = Entrez.read(handle)
handle.close()
rows = []
for rec in records.get('PubmedArticle', []):
try:
title = rec['MedlineCitation']['Article']['ArticleTitle']
pmid = rec['MedlineCitation']['PMID']
link = f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/"
rows.append({
"الموقع": "PubMed",
"الوصف": "قاعدة بيانات PubMed",
"العنوان": title,
"الرابط": f"<a href='{link}' target='_blank'>فتح</a>"
})
except Exception:
continue
if not rows:
return "<p>❌ لم يتم العثور على نتائج صالحة.</p>"
df = pd.DataFrame(rows)
log_usage("المصادر الخارجية", query, "PubMed", len(df))
return CUSTOM_CSS + df.to_html(escape=False, index=False, classes="styled-table")
except Exception as e:
return f"<p>❌ حدث خطأ أثناء جلب البيانات من PubMed:<br>{str(e)}</p>"
# ================== البحث في المواقع الخارجية ==================
EXTERNAL_LINKS = {
"Google Scholar": {"url": "https://scholar.google.com/scholar?q=", "desc": "محرك بحث للأبحاث"},
"PubMed": {"url": "https://pubmed.ncbi.nlm.nih.gov/?term=", "desc": "قاعدة بيانات PubMed"},
"Europe PMC": {"url": "https://europepmc.org/search?query=", "desc": "أبحاث Europe PMC"},
"ClinicalTrials.gov": {"url": "https://clinicaltrials.gov/search?term=", "desc": "تجارب سريرية"},
"OpenFDA": {"url": "https://open.fda.gov/", "desc": "بيانات الأدوية FDA"},
"MedlinePlus": {"url": "https://medlineplus.gov/search/?query=", "desc": "معلومات طبية"},
"DrugBank": {"url": "https://go.drugbank.com/", "desc": "قاعدة بيانات الأدوية"},
"WHO GHO": {"url": "https://www.who.int/data/gho/", "desc": "بيانات الصحة العالمية"},
"ICD-11": {"url": "https://icd.who.int/browse11/l-m/en", "desc": "التصنيف الدولي للأمراض"},
"ClinicalKey": {"url": "https://www.clinicalkey.com/search?q=", "desc": "مصادر طبية شاملة"},
"Scimago": {"url": "https://www.scimagojr.com/journalsearch.php?q=", "desc": "تصنيف الدوريات"},
"EKB": {"url": "https://www.ekb.eg/", "desc": "بنك المعرفة المصري"}
}
def external_search_html(query, site):
try:
if not query or not str(query).strip():
return "<p>⚠️ من فضلك اكتب كلمة للبحث</p>"
if not site or site not in EXTERNAL_LINKS:
return f"<p>⚠️ الموقع المحدد غير معروف أو غير موجود في القائمة.</p>"
if site == "PubMed":
return search_pubmed_html(query)
base = EXTERNAL_LINKS[site]["url"]
desc = EXTERNAL_LINKS[site]["desc"]
if "?" in base or "=" in base:
link = f"{base}{query}"
else:
link = base
df = pd.DataFrame([
{"اسم الموقع": site, "الوصف": desc, "رابط البحث": f"<a href='{link}' target='_blank'>للوصول إلى نتيجة البحث</a>"}
])
log_usage("المصادر الخارجية", query, site, 1)
html_table = df.to_html(escape=False, index=False, classes="styled-table")
return CUSTOM_CSS + f"<h3>🔗 نتائج البحث في {site}</h3>" + html_table
except Exception as e:
return f"<p>❌ حدث خطأ أثناء تنفيذ البحث:<br>{str(e)}</p>"
# ================== البحث في الدوريات ==================
JOURNALS = {
"Elsevier Journal Finder": {"url": "https://journalfinder.elsevier.com/", "desc": "اقتراح مجلات النشر"},
"Springer Journal Suggester": {"url": "https://journalsuggester.springer.com/", "desc": "اقتراح مجلات النشر"},
"DOAJ": {"url": "https://doaj.org/search/journals?ref=homepage-box&source=", "desc": "مجلات مفتوحة الوصول"},
"Journal Citation Reports": {"url": "https://jcr.clarivate.com/jcr/browse-journals?query=", "desc": "تقييم الدوريات العلمية"},
"Scimago": {"url": "https://www.scimagojr.com/journalsearch.php?q=", "desc": "تصنيف الدوريات"},
"Google Scholar": {"url": "https://scholar.google.com/scholar?q=", "desc": "بحث عام"}
}
def journal_search_html(query, site):
try:
if not query or not str(query).strip():
return "<p>⚠️ من فضلك اكتب كلمة للبحث</p>"
if not site or site not in JOURNALS:
return f"<p>⚠️ الموقع المحدد غير معروف أو غير موجود في قائمة الدوريات.</p>"
base = JOURNALS[site]["url"]
desc = JOURNALS[site]["desc"]
if "?" in base or "=" in base:
link = f"{base}{query}"
else:
link = base
df = pd.DataFrame([{"اسم الموقع": site, "الوصف": desc, "رابط البحث": f"<a href='{link}' target='_blank'>للوصول إلى نتيجة البحث</a>"}])
log_usage("الدوريات", query, site, 1)
html_table = df.to_html(escape=False, index=False, classes="styled-table")
return CUSTOM_CSS + f"<h3>📄 نتائج البحث في {site}</h3>" + html_table
except Exception as e:
return f"<p>❌ حدث خطأ أثناء تنفيذ البحث:<br>{str(e)}</p>"
# ================== أدوات الذكاء الاصطناعي ==================
AI_TOOLS = {
"Semantic Scholar": {"url": "https://www.semanticscholar.org/search?q=", "desc": "بحث أكاديمي يعتمد على الذكاء الاصطناعي"},
"Connected Papers": {"url": "https://www.connectedpapers.com/search?q=", "desc": "خرائط علاقات بين الأبحاث"},
"Research Rabbit": {"url": "https://www.researchrabbitapp.com/", "desc": "تتبع شبكات الأبحاث"},
"Elicit": {"url": "https://elicit.org/", "desc": "مساعد بحث علمي يستخدم AI"},
"Explainpaper": {"url": "https://www.explainpaper.com/", "desc": "تبسيط الأبحاث العلمية"},
"Consensus": {"url": "https://consensus.app/search/?q=", "desc": "إجابات سريعة مدعومة بالأدلة"}
}
def ai_search_html(query, site):
try:
if not query or not str(query).strip():
return "<p>⚠️ من فضلك اكتب كلمة للبحث</p>"
if not site or site not in AI_TOOLS:
return f"<p>⚠️ الأداة المحددة غير معروفة أو غير موجودة في القائمة.</p>"
base = AI_TOOLS[site]["url"]
desc = AI_TOOLS[site]["desc"]
if "?" in base or "=" in base:
link = f"{base}{query}"
else:
link = base
df = pd.DataFrame([{"اسم الأداة": site, "الوصف": desc, "رابط البحث": f"<a href='{link}' target='_blank'>للوصول إلى نتيجة البحث</a>"}])
log_usage("أدوات الذكاء الاصطناعي", query, site, 1)
html_table = df.to_html(escape=False, index=False, classes="styled-table")
return CUSTOM_CSS + f"<h3>🤖 نتائج البحث في {site}</h3>" + html_table
except Exception as e:
return f"<p>❌ حدث خطأ أثناء تنفيذ البحث:<br>{str(e)}</p>"
# ================== وظائف إدارة الأسئلة (CSV) ==================
def ensure_questions_file():
if not os.path.exists(QUESTIONS_FILE):
df = pd.DataFrame(columns=["id","name","question","answer","status","datetime"])
df.to_csv(QUESTIONS_FILE, index=False, encoding="utf-8-sig")
def read_questions_df():
ensure_questions_file()
try:
df = pd.read_csv(QUESTIONS_FILE, encoding="utf-8-sig")
except Exception:
df = pd.DataFrame(columns=["id","name","question","answer","status","datetime"])
return df
def save_question_to_csv(name, question):
ensure_questions_file()
df = read_questions_df()
if df.empty:
next_id = 1
else:
try:
next_id = int(df["id"].max()) + 1
except Exception:
next_id = len(df) + 1
row = {
"id": next_id,
"name": name if name and str(name).strip() else "👤 طالب مجهول",
"question": question,
"answer": "",
"status": "لم يتم الرد",
"datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
df = pd.concat([df, pd.DataFrame([row])], ignore_index=True)
df.to_csv(QUESTIONS_FILE, index=False, encoding="utf-8-sig")
# رفع تلقائي للملف بعد الحفظ
try:
upload_data_file(QUESTIONS_FILE)
except Exception as e:
print("⚠️ خطأ أثناء رفع questions.csv:", e)
return next_id
def save_answer_to_csv(qid, answer_text):
ensure_questions_file()
df = read_questions_df()
if df.empty:
return False, "لا توجد أسئلة."
try:
qid = int(qid)
except Exception:
return False, "معرّف السؤال غير صالح."
if qid not in df["id"].values:
return False, "لم يتم إيجاد السؤال بالمعرّف المحدد."
df.loc[df["id"] == qid, "answer"] = answer_text
df.loc[df["id"] == qid, "status"] = "تم الرد"
df.to_csv(QUESTIONS_FILE, index=False, encoding="utf-8-sig")
# رفع تلقائي بعد الحفظ
try:
upload_data_file(QUESTIONS_FILE)
except Exception as e:
print("⚠️ خطأ أثناء رفع questions.csv بعد الرد:", e)
return True, "تم حفظ الرد بنجاح."
def questions_to_html(df):
if df is None or df.empty:
return "<p>❌ لا توجد أسئلة حتى الآن.</p>"
html = CUSTOM_CSS + "<div style='direction:rtl;text-align:right;'>"
df_sorted = df.sort_values(by="id", ascending=False).reset_index(drop=True)
for _, row in df_sorted.iterrows():
name = row.get("name","👤 طالب مجهول")
qtext = row.get("question","")
answer = row.get("answer","")
status = row.get("status","لم يتم الرد")
qid = row.get("id","")
dt = row.get("datetime","")
html += f"<div class='question-card'><div class='question-meta'># {qid}{dt} — <b>{name}</b></div>"
html += f"<div><b>السؤال:</b> {qtext}</div>"
if status == "تم الرد" and str(answer).strip():
html += f"<div class='answer'><b>الرد:</b> {answer} <div class='question-meta'>حالة: {status}</div></div>"
else:
html += f"<div class='answer'><i>لم يتم الرد بعد</i> <div class='question-meta'>حالة: {status}</div></div>"
html += "</div>"
html += "</div>"
return html
# ================== وظائف إدارة طلبات الاستعارة (أعضاء هيئة التدريس) ==================
def ensure_faculty_file():
if not os.path.exists(FACULTY_BORROW_FILE):
df = pd.DataFrame(columns=["id","name","phone","nid","email","position","birthdate","status","datetime"])
df.to_csv(FACULTY_BORROW_FILE, index=False, encoding="utf-8-sig")
def read_faculty_df():
ensure_faculty_file()
try:
df = pd.read_csv(FACULTY_BORROW_FILE, encoding="utf-8-sig")
except Exception:
df = pd.DataFrame(columns=["id","name","phone","nid","email","position","birthdate","status","datetime"])
return df
def save_faculty_request(name, phone, nid, email, position, birthdate):
ensure_faculty_file()
df = read_faculty_df()
if df.empty:
next_id = 1
else:
try:
next_id = int(df["id"].max()) + 1
except Exception:
next_id = len(df) + 1
row = {
"id": next_id,
"name": name,
"phone": phone,
"nid": nid,
"email": email,
"position": position,
"birthdate": birthdate,
"status": "قيد المراجعة",
"datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
df = pd.concat([df, pd.DataFrame([row])], ignore_index=True)
df.to_csv(FACULTY_BORROW_FILE, index=False, encoding="utf-8-sig")
# رفع تلقائي بعد الحفظ
try:
upload_data_file(FACULTY_BORROW_FILE)
except Exception as e:
print("⚠️ خطأ أثناء رفع faculty_borrow.csv:", e)
return next_id
# ================== واجهة Gradio ==================
with gr.Blocks() as demo:
with gr.Tab("📚 عن المكتبة"):
with gr.Tabs():
with gr.Tab("🌟 رؤية المكتبة"):
gr.Markdown("تسعى المكتبة إلى أن تكون مركزًا معرفيًا متميزًا يدعم البحث والتعليم ويُسهم في بناء مجتمع المعرفة.")
with gr.Tab("🎯 رسالة المكتبة"):
gr.Markdown("تقديم خدمات معلوماتية متكاملة تُمكّن الباحثين والطلاب من الوصول إلى مصادر المعرفة بسهولة وكفاءة.")
with gr.Tab("🎯 أهداف المكتبة"):
gr.Markdown("- دعم العملية التعليمية والبحثية.\n- تيسير الوصول إلى المعلومات والمصادر العلمية.\n- تشجيع استخدام التقنيات الحديثة في البحث.\n- تعزيز ثقافة القراءة والتعلم الذاتي.")
with gr.Tab("🧩 الخدمات"):
with gr.Tabs():
with gr.Tab("📘 الإحاطة الجارية"):
gr.Markdown("خدمة تتيح للباحثين متابعة أحدث الإصدارات والموضوعات الجديدة في مجالات اهتمامهم.")
with gr.Tab("🔗 التسجيل في بنك المعرفة"):
gr.Markdown("يرجى ملء البيانات التالية للتسجيل في بنك المعرفة المصري:")
name = gr.Textbox(label="الاسم بالكامل", placeholder="اكتب اسمك الثلاثي هنا")
nid = gr.Textbox(label="رقم البطاقة", placeholder="14 رقمًا")
birth = gr.Textbox(label="تاريخ الميلاد", placeholder="مثال: 1999-05-12")
email = gr.Textbox(label="الإيميل الجامعي", placeholder="example@university.edu.eg")
phone = gr.Textbox(label="رقم التليفون", placeholder="010XXXXXXXXX")
submit_btn = gr.Button("إرسال الطلب")
output_msg = gr.HTML()
def save_registration(name, nid, birth, email, phone):
import csv, os
from datetime import datetime
file_path = "bank_requests.csv"
file_exists = os.path.exists(file_path)
with open(file_path, mode="a", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
if not file_exists:
writer.writerow(["الاسم", "رقم البطاقة", "تاريخ الميلاد", "الإيميل الجامعي", "رقم التليفون", "تاريخ التسجيل"])
writer.writerow([name, nid, birth, email, phone, datetime.now().strftime("%Y-%m-%d %H:%M:%S")])
# رفع تلقائي للملف
try:
upload_data_file(file_path)
except Exception as e:
print("⚠️ خطأ أثناء رفع bank_requests.csv:", e)
return "<b style='color:green;'>✅ تم استلام بياناتك بنجاح، يرجى مراجعة بريدك الجامعي خلال 24 ساعة.</b>"
submit_btn.click(save_registration, inputs=[name, nid, birth, email, phone], outputs=output_msg)
with gr.Tab("🖨️ التصوير"):
gr.Markdown("توفر المكتبة خدمة تصوير المواد العلمية وفق القواعد المعمول بها داخل المكتبة.")
with gr.Tab("🔍 البحث الإلكتروني"):
gr.Markdown("يمكن للباحثين استخدام أجهزة المكتبة للبحث في قواعد البيانات الإلكترونية والمصادر العلمية.")
with gr.Tab("📖 الاطلاع الداخلي"):
gr.Markdown("تتيح المكتبة للرواد قراءة الكتب والمراجع داخل القاعات المخصصة دون الحاجة إلى استعارتها خارجًا.")
# ------------------ تبويب الاستعارة الخارجية (معدّل) ------------------
with gr.Tab("📚 الاستعارة الخارجية"):
gr.Markdown("""
تقدّم المكتبة خدمة الاستعارة الخارجية للكتب تبعًا لعدد النسخ، حيث أنه غير مسموح بالاستعارة الخارجية للكتب والمراجع ذات النسخة الواحدة أو ذات الطابع الخاص أو الدوريات أو الرسائل العلمية.
""")
with gr.Tabs():
with gr.Tab("📌 الاستعارة للطلاب"):
gr.Markdown("""
📌 يستخرج الطالب استمارة الاستعارة عن طريق ملء استمارة ضمان يقوم بختمها من شؤون الطلاب،
مع ختمها بختم النسر الخاص بالجهة الحكومية التابع لها الضامن.
""")
gr.Markdown("### 📄 تحميل الاستمارات المطلوبة:")
# روابط ملفات Google Drive
student_form_url = "https://drive.google.com/uc?export=download&id=18BBlUYnKu37TEPqup8CeJGPv5UEOy1tY"
guarantor_form_url = "https://drive.google.com/uc?export=download&id=1th9cJm0xxI16_YfLQ148l94zvkSqJmLD"
gr.HTML(f"""
<div style='display:flex; gap:15px; flex-wrap:wrap; margin-top:10px;'>
<a href='{student_form_url}' target='_blank'>
<button style='background-color:#4CAF50; color:white; padding:10px 18px; border:none; border-radius:8px; font-size:16px; cursor:pointer;'>
📄 تحميل بيانات الطالب
</button>
</a>
<a href='{guarantor_form_url}' target='_blank'>
<button style='background-color:#2196F3; color:white; padding:10px 18px; border:none; border-radius:8px; font-size:16px; cursor:pointer;'>
📄 تحميل بيانات الضامن
</button>
</a>
</div>
""")
gr.Markdown("برجاء طباعة الاستمارتين وتوقيعهما قبل التوجه إلى المكتبة")
with gr.Tab("📌 الاستعارة لأعضاء هيئة التدريس"):
gr.Markdown("أعضاء هيئة التدريس يرجى تعبئة البيانات التالية لطلب الاستعارة:")
fac_name = gr.Textbox(label="الاسم", placeholder="الاسم الكامل")
fac_phone = gr.Textbox(label="رقم التليفون", placeholder="010XXXXXXXX")
fac_nid = gr.Textbox(label="رقم البطاقة", placeholder="14 رقمًا")
fac_email = gr.Textbox(label="الإيميل الجامعي", placeholder="example@university.edu.eg")
fac_position = gr.Textbox(label="التوصيف الوظيفي", placeholder="مثال: أستاذ مساعد – قسم الصيدلة")
fac_birth = gr.Textbox(label="تاريخ الميلاد", placeholder="مثال: 1980-05-12")
fac_submit = gr.Button("إرسال الطلب")
fac_msg = gr.Markdown("")
def submit_faculty_request(name, phone, nid, email, position, birthdate):
if not name or not str(name).strip():
return "<b style='color:red;'>الاسم مطلوب.</b>"
if not phone or not str(phone).strip():
return "<b style='color:red;'>رقم التليفون مطلوب.</b>"
if not nid or not str(nid).strip():
return "<b style='color:red;'>رقم البطاقة مطلوب.</b>"
if not email or not str(email).strip():
return "<b style='color:red;'>الإيميل الجامعي مطلوب.</b>"
# حفظ البيانات
qid = save_faculty_request(name, phone, nid, email, position, birthdate)
return f"✅ تم إرسال طلب الاستعارة (#{qid}) وسيتم مراجعته."
fac_submit.click(submit_faculty_request, inputs=[fac_name, fac_phone, fac_nid, fac_email, fac_position, fac_birth], outputs=fac_msg)
with gr.Tab("🔎 البحث المحلي"):
query_local = gr.Textbox(label="اكتب كلمة البحث")
category = gr.Radio(["Books", "Theses"], label="اختر الفئة")
mode = gr.Radio(["نصي", "دلالي (Semantic)"], label="نوع البحث")
btn_local = gr.Button("بحث")
df_local_state = gr.State()
output_local_html = gr.HTML()
file_local = gr.File(label="⬇️ تحميل النتائج", visible=False)
btn_local.click(
local_search_df,
inputs=[query_local, category, mode],
outputs=[output_local_html, df_local_state]
)
btn_save_local = gr.Button("📥 حفظ النتائج")
btn_save_local.click(
save_to_excel,
inputs=df_local_state,
outputs=file_local
)
file_local.change(lambda x: gr.update(visible=True), inputs=file_local, outputs=file_local)
with gr.Tab("🌐 المصادر الخارجية"):
query_ext = gr.Textbox(label="كلمة البحث")
site_ext = gr.Dropdown(list(EXTERNAL_LINKS.keys()), label="اختر الموقع")
btn_ext = gr.Button("بحث")
output_ext = gr.HTML()
btn_ext.click(external_search_html, inputs=[query_ext, site_ext], outputs=output_ext)
with gr.Tab("📄 الدوريات"):
query_jour = gr.Textbox(label="كلمة البحث")
site_jour = gr.Dropdown(list(JOURNALS.keys()), label="اختر الموقع")
btn_jour = gr.Button("بحث")
output_jour = gr.HTML()
btn_jour.click(journal_search_html, inputs=[query_jour, site_jour], outputs=output_jour)
with gr.Tab("🤖 أدوات الذكاء الاصطناعي"):
query_ai = gr.Textbox(label="كلمة البحث")
site_ai = gr.Dropdown(list(AI_TOOLS.keys()), label="اختر الأداة")
btn_ai = gr.Button("بحث")
output_ai = gr.HTML()
btn_ai.click(ai_search_html, inputs=[query_ai, site_ai], outputs=output_ai)
with gr.Tab("📑 برامج إدارة المراجع"):
gr.Markdown("ℹ️ **تنويه:** هذه البرامج تحتاج تنزيل أو تثبيت الإضافات الخاصة بها.")
gr.Markdown("### • **Mendeley** \n🔗 [زيارة الموقع](https://www.mendeley.com/)")
gr.Markdown("### • **Zotero** \n🔗 [زيارة الموقع](https://www.zotero.org/)")
gr.Markdown("### • **EndNote** \n🔗 [زيارة الموقع](https://endnote.com/)")
with gr.Tab("💬 المساعد الذكي"):
q_faq = gr.Textbox(label="اسأل مساعد المكتبة")
ans_faq = gr.HTML()
btn_faq = gr.Button("إجابة")
btn_faq.click(library_assistant_smart, inputs=q_faq, outputs=ans_faq)
with gr.Tab("📊 الإحصاءات"):
stats_html = gr.HTML()
btn_stats = gr.Button("عرض الإحصاءات")
btn_stats.click(lambda: show_usage_stats(), outputs=stats_html)
# -------------------- تبويب "📞 تواصل معنا" المضاف سابقًا --------------------
with gr.Tab("📞 تواصل معنا"):
gr.Markdown("""
## 📞 تواصل معنا
يسعدنا تواصلك مع **مكتبة كلية الصيدلة – جامعة المنصورة** عبر القنوات التالية:
📍 **العنوان:** الدور الرابع – المبنى الإداري – كلية الصيدلة – جامعة المنصورة
📧 **البريد الإلكتروني:** [phrlib1@mans.edu.eg](mailto:phrlib1@mans.edu.eg)
""")
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/"
youtube_link = "https://www.youtube.com/@%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"
email_link = "https://mail.google.com/mail/?view=cm&fs=1&to=phrlib1@mans.edu.eg"
gr.Markdown("### 🌐 قنوات التواصل السريعة:")
gr.HTML(f"""
<div style='display:flex; gap:10px; flex-wrap:wrap;'>
<!-- Facebook -->
<a href='{fb_link}' target='_blank' style='text-decoration:none;'>
<button style='background-color:#1877f2;color:white;padding:10px 18px;
border:none;border-radius:8px;font-size:16px;cursor:pointer;display:flex;
align-items:center;gap:8px;'>
<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20'
fill='white' viewBox='0 0 24 24'>
<path d='M22.675 0h-21.35C.597 0 0 .597 0 1.333v21.334C0 23.403.597 24
1.325 24h11.495v-9.294H9.691v-3.622h3.129V8.413c0-3.1 1.894-4.788
4.659-4.788 1.325 0 2.464.099 2.797.143v3.243h-1.921c-1.506 0-1.798.716-
1.798 1.767v2.317h3.595l-.468 3.622h-3.127V24h6.127C23.403 24 24
23.403 24 22.667V1.333C24 .597 23.403 0 22.675 0z'/>
</svg>
Facebook
</button>
</a>
<!-- YouTube -->
<a href='{youtube_link}' target='_blank' style='text-decoration:none;'>
<button style='background-color:#FF0000;color:white;padding:10px 18px;
border:none;border-radius:8px;font-size:16px;cursor:pointer;display:flex;
align-items:center;gap:8px;'>
<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20'
fill='white' viewBox='0 0 24 24'>
<path d='M23.498 6.186a2.993 2.993 0 0 0-2.107-2.12C19.4 3.5 12
3.5 12 3.5s-7.4 0-9.391.566A2.993 2.993 0 0 0 .502 6.186
31.04 31.04 0 0 0 0 12a31.04 31.04 0 0 0 .502 5.814
2.993 2.993 0 0 0 2.107 2.12C4.6 20.5 12 20.5 12
20.5s7.4 0 9.391-.566a2.993 2.993 0 0 0 2.107-2.12A31.04
31.04 0 0 0 24 12a31.04 31.04 0 0 0-.502-5.814zM9.75
15.568V8.432L15.818 12 9.75 15.568z'/>
</svg>
YouTube
</button>
</a>
<!-- Email -->
<a href='{email_link}' target='_blank' style='text-decoration:none;'>
<button style='background-color:#444;color:white;padding:10px 18px;
border:none;border-radius:8px;font-size:16px;cursor:pointer;display:flex;
align-items:center;gap:8px;'>
<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20'
fill='white' viewBox='0 0 24 24'>
<path d='M12 13.065L.015 6h23.97L12 13.065zm0 2.248L24 8v13H0V8l12
7.313z'/>
</svg>
Email
</button>
</a>
</div>
""")
# -------------------- تبويب "❓ اسأل أخصائي المكتبة" (التبويب الجديد للطلاب) --------------------
with gr.Tab("❓ اسأل أخصائي المكتبة"):
gr.Markdown("### اسأل أخصائي المكتبة\nاكتب سؤالك وسيظهر في نظام المكتبة ليرد عليه الأخصائي لاحقًا.")
student_name = gr.Textbox(label="الاسم (اختياري)", placeholder="يمكن تركه فارغًا")
student_question = gr.Textbox(label="السؤال", lines=4, placeholder="اكتب سؤالك هنا...", interactive=True)
student_send = gr.Button("إرسال السؤال")
student_result = gr.Markdown("")
student_questions_html = gr.HTML()
def load_questions_html():
df = read_questions_df()
return questions_to_html(df)
def student_submit_question(name, question):
if not question or not str(question).strip():
return "⚠️ اكتب سؤالك قبل الإرسال.", questions_to_html(read_questions_df())
qid = save_question_to_csv(name, question)
df = read_questions_df()
return f"✅ تم إرسال سؤالك (#{qid})، سنقوم بالرد في أقرب وقت ممكن.", questions_to_html(df)
student_send.click(student_submit_question, inputs=[student_name, student_question], outputs=[student_result, student_questions_html])
btn_load_questions = gr.Button("تحديث عرض الأسئلة")
btn_load_questions.click(lambda: load_questions_html(), outputs=student_questions_html)
# -------------------- 📂 تبويب إدارة خدمات المكتبة --------------------
with gr.Tab("📂 إدارة خدمات المكتبة"):
gr.Markdown("### 🔐 تسجيل الدخول لإدارة خدمات المكتبة")
password = gr.Textbox(label="كلمة المرور", type="password", placeholder="أدخل كلمة المرور هنا")
login_btn = gr.Button("دخول")
login_msg = gr.Markdown("")
admin_area = gr.Group(visible=False)
with admin_area:
gr.Markdown("### عرض طلبات التسجيل في بنك المعرفة")
refresh_btn = gr.Button("🔄 تحديث البيانات")
data_table = gr.Dataframe(headers=["الاسم", "رقم البطاقة", "تاريخ الميلاد", "الإيميل الجامعي", "رقم التليفون", "تاريخ التسجيل"], interactive=False)
download_btn = gr.Button("⬇️ تنزيل الطلبات كملف Excel")
download_file = gr.File(label="ملف Excel", visible=False)
import pandas as pd, os, tempfile
def check_password(pw):
# اقرأ كلمة مرور الأدمن من Secret (ADMIN_PASS) — لو مش موجود يستخدم 'pharmacy1' كقيمة افتراضية قصيرة الأمد
correct_pw = os.getenv("ADMIN_PASS", "pharmacy1")
if pw == correct_pw:
return gr.update(visible=True), "✅ تم تسجيل الدخول بنجاح."
else:
return gr.update(visible=False), "❌ كلمة المرور غير صحيحة."
login_btn.click(check_password, inputs=password, outputs=[admin_area, login_msg])
def load_requests():
file_path = "bank_requests.csv"
if not os.path.exists(file_path):
return pd.DataFrame(columns=["الاسم", "رقم البطاقة", "تاريخ الميلاد", "الإيميل الجامعي", "رقم التليفون", "تاريخ التسجيل"])
df = pd.read_csv(file_path)
return df
def export_excel(df):
if df is None or df.empty:
return gr.update(visible=False)
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
df.to_excel(tmp.name, index=False)
return gr.update(value=tmp.name, visible=True)
refresh_btn.click(load_requests, outputs=data_table)
download_btn.click(export_excel, inputs=data_table, outputs=download_file)
# -------------------- قسم إدارة الأسئلة داخل منطقة الإدارة --------------------
with admin_area:
gr.Markdown("### 🛠️ إدارة الأسئلة")
admin_refresh_q = gr.Button("🔄 تحديث الأسئلة")
admin_questions_df = gr.Dataframe(headers=["id","name","question","answer","status","datetime"], interactive=False)
admin_select_q = gr.Dropdown(choices=[], label="اختر رقم السؤال للرد عليه (ID)", value=None)
admin_answer_box = gr.Textbox(label="اكتب الرد هنا", lines=3, placeholder="أدخل رد الأخصائي...")
admin_save_answer_btn = gr.Button("حفظ الرد")
admin_msg = gr.Markdown("")
admin_export_btn = gr.Button("⬇️ تنزيل الأسئلة كملف Excel")
admin_export_file = gr.File(label="ملف الأسئلة", visible=False)
def admin_load_questions():
df = read_questions_df()
if df is None or df.empty:
df_show = pd.DataFrame(columns=["id","name","question","answer","status","datetime"])
else:
df_show = df.sort_values(by="id", ascending=False)
choices = df_show["id"].astype(str).tolist() if not df_show.empty else []
return df_show, gr.update(choices=choices, value=choices[0] if choices else None)
def admin_save_answer(qid, answer_text):
success, msg = save_answer_to_csv(qid, answer_text)
if not success:
return "❌ " + msg, admin_load_questions()[0]
df_show, dd_update = admin_load_questions()
return "✅ تم حفظ الرد.", df_show
def admin_export_questions():
df = read_questions_df()
if df is None or df.empty:
return gr.update(visible=False)
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
df.to_excel(tmp.name, index=False)
return gr.update(value=tmp.name, visible=True)
admin_refresh_q.click(lambda: admin_load_questions()[0], outputs=admin_questions_df)
admin_refresh_q.click(lambda: admin_load_questions()[1], outputs=admin_select_q)
admin_save_answer_btn.click(admin_save_answer, inputs=[admin_select_q, admin_answer_box], outputs=[admin_msg, admin_questions_df])
admin_export_btn.click(admin_export_questions, outputs=admin_export_file)
# -------------------- قسم إدارة طلبات الاستعارة لأعضاء هيئة التدريس --------------------
with admin_area:
gr.Markdown("### 🗂️ إدارة طلبات الاستعارة (أعضاء هيئة التدريس)")
admin_refresh_fac = gr.Button("🔄 تحديث طلبات الاستعارة")
admin_fac_df = gr.Dataframe(headers=["id","name","phone","nid","email","position","birthdate","status","datetime"], interactive=False)
admin_export_fac_btn = gr.Button("⬇️ تنزيل طلبات الاستعارة كملف Excel")
admin_export_fac_file = gr.File(label="ملف طلبات الاستعارة", visible=False)
def admin_load_faculty():
df = read_faculty_df()
if df is None or df.empty:
df_show = pd.DataFrame(columns=["id","name","phone","nid","email","position","birthdate","status","datetime"])
else:
df_show = df.sort_values(by="id", ascending=False)
return df_show
def admin_export_faculty():
df = read_faculty_df()
if df is None or df.empty:
return gr.update(visible=False)
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
df.to_excel(tmp.name, index=False)
return gr.update(value=tmp.name, visible=True)
admin_refresh_fac.click(lambda: admin_load_faculty(), outputs=admin_fac_df)
admin_export_fac_btn.click(admin_export_faculty, outputs=admin_export_fac_file)
gr.Markdown("---\n📌 *المشروع أعدته eman anter*")
# تشغيل التطبيق
if __name__ == "__main__":
demo.launch(share=True)