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(""" """, 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 است.