Esmaeilkianii's picture
Update app.py
e570500 verified
import streamlit as st
import sqlite3
import pandas as pd
from datetime import datetime
import os
import shutil
import altair as alt # برای چارت‌ها
# تنظیمات اولیه
st.set_page_config(layout="wide", page_title="سیستم مدیریت بیماران بهداری")
st.markdown("""
<style>
body { direction: rtl; text-align: right; font-family: 'Tahoma'; }
.stSidebar { background-color: #f0f0f0; }
</style>
""", unsafe_allow_html=True)
# توابع تبدیل تاریخ بدون لایبرری
def gregorian_to_jalali(gy, gm, gd):
g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
gy -= 1600
gm -= 1
gd -= 1
g_day_no = 365 * gy + (gy + 3) // 4 - (gy + 99) // 100 + (gy + 399) // 400
for i in range(gm):
g_day_no += g_d_m[i]
if gm > 1 and ((gy % 4 == 0 and gy % 100 != 0) or (gy % 400 == 0)):
g_day_no += 1
g_day_no += gd
j_day_no = g_day_no - 79
j_np = j_day_no // 12053
j_day_no %= 12053
jy = 979 + 33 * j_np + 4 * (j_day_no // 1461)
j_day_no %= 1461
if j_day_no >= 366:
jy += (j_day_no - 1) // 365
j_day_no = (j_day_no - 1) % 365
j_days_in_month = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29]
i = 0
while i < 11 and j_day_no >= j_days_in_month[i]:
j_day_no -= j_days_in_month[i]
i += 1
jm = i + 1
jd = j_day_no + 1
return jy, jm, jd
def jalali_to_gregorian(jy, jm, jd):
jy += 1595
days = -355668 + (365 * jy) + (jy // 33 * 8) + ((jy % 33) // 4) + jd
if jm < 7:
days += (jm - 1) * 31
else:
days += ((jm - 7) * 30) + 186
gy = 400 * (days // 146097)
days %= 146097
if days > 36524:
days -= 36525
gy += 100
gy += 4 * (days // 1461)
days %= 1461
if days > 365:
gy += (days - 1) // 365
days = (days - 1) % 365
gd = days + 1
if (gy % 4 == 0 and gy % 100 != 0) or (gy % 400 == 0):
sal_a = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
else:
sal_a = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
gm = 0
while gm < 13 and gd > sal_a[gm]:
gm += 1
gd -= sal_a[gm - 1]
return gy, gm, gd
# اتصال به DB
DB_FILE = "health_unit.db"
BACKUP_DIR = "backups"
if not os.path.exists(BACKUP_DIR):
os.makedirs(BACKUP_DIR)
def init_db():
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
# جدول کاربران
c.execute('''CREATE TABLE IF NOT EXISTS users
(id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT,
password TEXT,
role TEXT)''')
# هش رمز نمونه
def hash_pw(pw): return hashlib.sha256(pw.encode()).hexdigest()
# داده‌های اولیه کاربران
users = [
('admin', hash_pw('admin'), 'admin'),
('doctor_zakizadeh', hash_pw('password'), 'doctor_zakizadeh'),
('receptionist', hash_pw('password'), 'receptionist'),
('hsee_zali', hash_pw('password'), 'hsee_zali')
]
for u in users:
c.execute("INSERT OR IGNORE INTO users (username, password, role) VALUES (?, ?, ?)", u)
# جدول بیماران
c.execute('''CREATE TABLE IF NOT EXISTS patients
(id INTEGER PRIMARY KEY AUTOINCREMENT,
first_name TEXT,
last_name TEXT,
national_id TEXT UNIQUE,
personnel_id TEXT UNIQUE,
gender TEXT,
age INTEGER,
job TEXT,
phone TEXT,
department TEXT,
medical_history TEXT,
blood_type TEXT,
vaccination_status TEXT)''')
# جدول ویزیت‌ها
c.execute('''CREATE TABLE IF NOT EXISTS visits
(id INTEGER PRIMARY KEY AUTOINCREMENT,
patient_id INTEGER,
visit_date TEXT, -- جلالی string YYYY/MM/DD
symptoms TEXT,
history TEXT,
physical_exam TEXT,
diagnosis TEXT,
prescribed_medications TEXT,
recommendations TEXT,
needs_lab BOOLEAN,
needs_referral BOOLEAN,
needs_hospitalization BOOLEAN)''')
# جدول معاینات ادواری
c.execute('''CREATE TABLE IF NOT EXISTS periodic_examinations
(id INTEGER PRIMARY KEY AUTOINCREMENT,
department TEXT,
scheduled_date TEXT,
notified BOOLEAN DEFAULT 0)''')
# جدول داروها
c.execute('''CREATE TABLE IF NOT EXISTS medications
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
stock_quantity INTEGER DEFAULT 100)''')
# داده‌های اولیه داروها
meds = ['آسپیرین', 'ایبوپروفن', 'پاراستامول', 'آموکسی‌سیلین', 'لوراتادین', 'امپرازول', 'آتورواستاتین', 'متفورمین', 'لوزارتان', 'کلوپیدوگرل',
'دیکلوفناک', 'رانیتیدین', 'سیپروفلوکساسین', 'آزیترومایسین', 'کدئین', 'ترامادول', 'دیازپام', 'آلپرازولام', 'سرترالین', 'فلوکستین']
for m in meds:
c.execute("INSERT OR IGNORE INTO medications (name) VALUES (?)", (m,))
# جدول مصرف دارو
c.execute('''CREATE TABLE IF NOT EXISTS medication_usage
(id INTEGER PRIMARY KEY AUTOINCREMENT,
medication_id INTEGER,
visit_id INTEGER,
quantity INTEGER,
date_used TEXT,
department TEXT)''')
# جدول بخش‌ها
c.execute('''CREATE TABLE IF NOT EXISTS departments
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE)''')
depts = ["معاونت کشاورزی", "مطالعات کاربردی", "مهندسی زراعی", "تولید یکم", "تولید دوم",
"اداری", "بازرگانی", "حراست", "انبار", "کارخانه", "یارد", "تجهیزات مکانیکی",
"دفتر فنی", "غیر نیشکری"]
for d in depts:
c.execute("INSERT OR IGNORE INTO departments (name) VALUES (?)", (d,))
conn.commit()
conn.close()
init_db()
# توابع کمکی
def get_jalali_today():
now = datetime.now()
jy, jm, jd = gregorian_to_jalali(now.year, now.month, now.day)
return f"{jy:04d}/{jm:02d}/{jd:02d}"
# چک نوتیفیکیشن‌ها
def check_notifications():
today_str = get_jalali_today()
ty, tm, td = map(int, today_str.split('/'))
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
c.execute("SELECT department, scheduled_date FROM periodic_examinations WHERE notified=0")
exams = c.fetchall()
notifications = []
for dept, sch_date in exams:
sy, sm, sd = map(int, sch_date.split('/'))
# تبدیل به gregorian برای محاسبه تفاوت
g_sch_y, g_sch_m, g_sch_d = jalali_to_gregorian(sy, sm, sd)
g_today_y, g_today_m, g_today_d = jalali_to_gregorian(ty, tm, td)
sch_date_g = datetime(g_sch_y, g_sch_m, g_sch_d)
today_g = datetime(g_today_y, g_today_m, g_today_d)
days_diff = (sch_date_g - today_g).days
if days_diff == 3:
notifications.append(f"معاینه ادواری برای بخش {dept} در {sch_date}")
c.execute("UPDATE periodic_examinations SET notified=1 WHERE department=? AND scheduled_date=?", (dept, sch_date))
conn.commit()
conn.close()
return notifications
# مدیریت جلسه
if 'logged_in' not in st.session_state:
st.session_state.logged_in = False
if 'role' not in st.session_state:
st.session_state.role = None
# صفحه ورود
def login_page():
st.title("ورود به سیستم")
username = st.text_input("نام کاربری")
password = st.text_input("رمز عبور", type="password")
if st.button("ورود"):
import hashlib
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
hash_pw = hashlib.sha256(password.encode()).hexdigest()
c.execute("SELECT role FROM users WHERE username=? AND password=?", (username, hash_pw))
result = c.fetchone()
conn.close()
if result:
st.session_state.logged_in = True
st.session_state.role = result[0]
st.rerun()
else:
st.error("نام کاربری یا رمز عبور اشتباه است")
# داشبورد
def dashboard():
st.title("داشبورد")
notifications = check_notifications()
if notifications:
st.warning("اعلان‌ها:")
for n in notifications:
st.write(n)
# ثبت بیمار
def register_patient():
st.title("ثبت بیمار جدید")
with st.form("register_form"):
first_name = st.text_input("نام")
last_name = st.text_input("نام خانوادگی")
national_id = st.text_input("کد ملی")
personnel_id = st.text_input("شماره پرسنلی")
gender = st.selectbox("جنسیت", ["مرد", "زن"])
age = st.number_input("سن", min_value=0)
job = st.text_input("شغل")
phone = st.text_input("شماره تماس")
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
c.execute("SELECT name FROM departments")
depts = [row[0] for row in c.fetchall()]
conn.close()
department = st.selectbox("بخش", depts)
medical_history = st.text_area("سابقه بیماری")
blood_type = st.text_input("گروه خونی")
vaccination_status = st.text_input("وضعیت واکسیناسیون")
submitted = st.form_submit_button("ثبت")
if submitted:
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
try:
c.execute("SELECT id FROM patients WHERE national_id=? OR personnel_id=?", (national_id, personnel_id))
if c.fetchone():
st.error("کد ملی یا شماره پرسنلی تکراری است")
else:
c.execute("""INSERT INTO patients (first_name, last_name, national_id, personnel_id, gender, age, job, phone, department, medical_history, blood_type, vaccination_status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(first_name, last_name, national_id, personnel_id, gender, age, job, phone, department, medical_history, blood_type, vaccination_status))
conn.commit()
st.success("بیمار ثبت شد")
except Exception as e:
st.error(f"خطا: {e}")
conn.close()
# جستجو بیمار
def search_patient(query):
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
c.execute("SELECT * FROM patients WHERE national_id LIKE ? OR last_name LIKE ?", (f"%{query}%", f"%{query}%"))
results = c.fetchall()
conn.close()
return results
# ویزیت بیمار
def visit_patient():
st.title("ویزیت بیمار")
query = st.text_input("جستجو با کد ملی یا نام خانوادگی", key="search_key")
if query:
patients = search_patient(query)
if patients:
patient_options = {f"{p[1]} {p[2]} - کد ملی: {p[3]}": p[0] for p in patients}
selected = st.selectbox("انتخاب بیمار", list(patient_options.keys()))
if selected:
selected_id = patient_options[selected]
with st.form("visit_form"):
visit_date = st.text_input("تاریخ ویزیت (جلالی YYYY/MM/DD)", value=get_jalali_today())
symptoms = st.text_area("علائم بیماری")
history = st.text_area("شرح حال")
physical_exam = st.text_area("معاینه فیزیکی")
diagnosis = st.text_area("تشخیص پزشک")
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
c.execute("SELECT id, name FROM medications")
meds = {row[1]: row[0] for row in c.fetchall()}
selected_meds = st.multiselect("داروهای تجویز شده", list(meds.keys()))
recommendations = st.text_area("توصیه‌ها")
needs_lab = st.checkbox("نیاز به آزمایش")
needs_referral = st.checkbox("نیاز به ارجاع")
needs_hospitalization = st.checkbox("نیاز به بستری")
submitted = st.form_submit_button("ثبت ویزیت")
if submitted:
prescribed = ','.join(selected_meds)
try:
c.execute("""INSERT INTO visits (patient_id, visit_date, symptoms, history, physical_exam, diagnosis, prescribed_medications, recommendations, needs_lab, needs_referral, needs_hospitalization)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(selected_id, visit_date, symptoms, history, physical_exam, diagnosis, prescribed, recommendations, needs_lab, needs_referral, needs_hospitalization))
visit_id = c.lastrowid
for m in selected_meds:
med_id = meds[m]
c.execute("UPDATE medications SET stock_quantity = stock_quantity - 1 WHERE id=?", (med_id,))
c.execute("""INSERT INTO medication_usage (medication_id, visit_id, quantity, date_used, department)
VALUES (?, ?, ?, ?, ?)""", (med_id, visit_id, 1, visit_date, patients[0][8])) # department
conn.commit()
st.success("ویزیت ثبت شد")
except Exception as e:
st.error(f"خطا: {e}")
conn.close()
else:
st.info("بیماری یافت نشد")
# برنامه معاینات
def schedules():
st.title("برنامه معاینات ادواری")
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
c.execute("SELECT name FROM departments")
depts = [row[0] for row in c.fetchall()]
department = st.selectbox("بخش", depts)
scheduled_date = st.text_input("تاریخ برنامه‌ریزی (جلالی YYYY/MM/DD)")
if st.button("اضافه کردن"):
try:
c.execute("INSERT INTO periodic_examinations (department, scheduled_date) VALUES (?, ?)", (department, scheduled_date))
conn.commit()
st.success("برنامه اضافه شد")
except Exception as e:
st.error(f"خطا: {e}")
c.execute("SELECT * FROM periodic_examinations")
df = pd.DataFrame(c.fetchall(), columns=['ID', 'بخش', 'تاریخ', 'اعلان شده'])
st.dataframe(df)
conn.close()
# گزارشات
def reports():
st.title("گزارش‌گیری")
conn = sqlite3.connect(DB_FILE)
c = conn.cursor()
start_date = st.text_input("از تاریخ (جلالی YYYY/MM/DD)")
end_date = st.text_input("تا تاریخ (جلالی YYYY/MM/DD)")
c.execute("SELECT name FROM departments")
depts = ['همه'] + [row[0] for row in c.fetchall()]
department = st.selectbox("بخش", depts)
where = ""
params = []
if start_date:
where += " AND v.visit_date >= ?"
params.append(start_date)
if end_date:
where += " AND v.visit_date <= ?"
params.append(end_date)
if department != 'همه':
where += " AND p.department = ?"
params.append(department)
# بیشترین بیماری‌ها
query = f"""SELECT diagnosis, COUNT(*) as count FROM visits v
JOIN patients p ON v.patient_id = p.id
WHERE 1=1 {where}
GROUP BY diagnosis ORDER BY count DESC"""
df_diseases = pd.read_sql_query(query, conn, params=params)
st.subheader("بیشترین بیماری‌ها")
st.dataframe(df_diseases)
chart = alt.Chart(df_diseases).mark_bar().encode(x='diagnosis', y='count', color='diagnosis').properties(title="نمودار بیماری‌ها")
st.altair_chart(chart, use_container_width=True)
# درصد
total = df_diseases['count'].sum()
if total > 0:
df_diseases['percent'] = df_diseases['count'] / total * 100
st.subheader("تفکیک درصدی بیماری‌ها")
st.dataframe(df_diseases)
# موجودی دارو
st.subheader("موجودی داروها")
df_meds = pd.read_sql_query("SELECT name, stock_quantity FROM medications", conn)
st.dataframe(df_meds)
# مصرف دارو
st.subheader("داروهای مصرف شده")
query_usage = f"""SELECT m.name, u.quantity, u.date_used, u.department FROM medication_usage u
JOIN medications m ON u.medication_id = m.id
WHERE 1=1 {where.replace('v.', 'u.').replace('p.', 'u.')}""" # تقریبی
df_usage = pd.read_sql_query(query_usage, conn, params=params)
st.dataframe(df_usage)
conn.close()
# بکاپ
def backup_db():
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = os.path.join(BACKUP_DIR, f"backup_{timestamp}.db")
shutil.copy(DB_FILE, backup_file)
st.success(f"بکاپ گرفته شد: {backup_file}")
# منوی اصلی
if not st.session_state.logged_in:
login_page()
else:
with st.sidebar:
st.title("منو")
page = st.radio("صفحه", ["داشبورد", "ثبت بیمار", "ویزیت بیمار", "برنامه معاینات", "گزارشات", "بکاپ"])
if st.button("خروج"):
st.session_state.logged_in = False
st.session_state.role = None
st.rerun()
if page == "داشبورد":
dashboard()
elif page == "ثبت بیمار" and st.session_state.role in ['admin', 'receptionist']:
register_patient()
elif page == "ویزیت بیمار" and st.session_state.role in ['admin', 'doctor_zakizadeh']:
visit_patient()
elif page == "برنامه معاینات" and st.session_state.role in ['admin', 'doctor_zakizadeh']:
schedules()
elif page == "گزارشات" and st.session_state.role in ['admin', 'hsee_zali', 'doctor_zakizadeh']:
reports()
elif page == "بکاپ" and st.session_state.role == 'admin':
if st.button("گرفتن بکاپ"):
backup_db()
else:
st.error("دسترسی مجاز نیست")
# برای Hugging Face Spaces:
# 1. فایل app.py ایجاد کنید با این کد.
# 2. requirements.txt با: streamlit pandas altair
# 3. DB در runtime ایجاد می‌شود.
# توجه: Hugging Face persistent storage دارد، پس DB حفظ می‌شود.
# برای جستجوی بلادرنگ، از key در text_input استفاده شد، اما برای debounce، Streamlit builtin است.