import gi import os gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GObject, Gdk from mods.db import store_patient_index, update_patient_index, search_patients, db, Patient, Reception, Catalog, List from mods.settings import s_get_reception_list, s_set_reception_list from mods.catalogs import add_catalog, search_catalogs from mods.lists import add_list_record, create_open_list_win from mods.files import list_row_ui_str from datetime import date, datetime, timedelta import peewee import re gender_dict = { 'male': 'Мужской', 'female': 'Женский' } doc_type_dict = { 'passport': 'Паспорт', 'birth_cert': 'Св.о рождении', 'foreign_passport': 'Паспорт иностранца' } # Variables resource_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'res') ui_dir = os.path.join(resource_dir, 'ui') icon_dir = os.path.join(resource_dir, 'icons') main_win_file = os.path.join(ui_dir, 'main_win.glade') new_patient_win_file = os.path.join(ui_dir, 'new_patient_win.glade') open_patient_win_file = os.path.join(ui_dir, 'open_patient_win.glade') edit_patient_win_file = os.path.join(ui_dir, 'edit_patient_win.glade') male_patient_row_file = os.path.join(ui_dir, 'male_patient_row.glade') reception_list_settings_win_file = os.path.join(ui_dir, 'reception_list_settings.glade') female_patient_row_file = os.path.join(ui_dir, 'female_patient_row.glade') new_catalog_win_file = os.path.join(ui_dir, 'new_catalog_win.glade') reception_row_file = os.path.join(ui_dir, 'reception_row.glade') with open(reception_row_file, 'r') as f: reception_row_ui_str = f.read() catalog_row_file = os.path.join(ui_dir, 'catalog_list_row.glade') with open(catalog_row_file, 'r') as f: catalog_row_ui_str = f.read() gender_ui_str = {} with open(male_patient_row_file, 'r') as f: gender_ui_str['male'] = f.read() with open(female_patient_row_file, 'r') as f: gender_ui_str['female'] = f.read() ############ builder = Gtk.Builder() def get_reception_timelist(rec_date): s = s_get_reception_list() dstart = datetime(rec_date.year, rec_date.month, rec_date.day, s.day_start[0], s.day_start[1]) dend = datetime(rec_date.year, rec_date.month, rec_date.day, s.day_end[0], s.day_end[1]) work_day_minutes_total = (dend.hour - dstart.hour) * 60 + dend.minute - dstart.minute shift_minutes_range = range(0, work_day_minutes_total, s.interval) return [dstart + timedelta(minutes=x) for x in shift_minutes_range] class ConditionalFilter: def __init__(self, search_func): self.search_func = search_func self.reset() def filter(self, query): if query != self.fstr: self.fstr = query self.ids = list(map(lambda x: x.id, self.search_func(query))) return self.ids def reset(self): self.fstr = '' self.ids = [] patient_filter = ConditionalFilter(search_patients) catalog_filter = ConditionalFilter(search_catalogs) class PatientRow(Gtk.ListBoxRow): @GObject.Property def db_id(self): return self._db_id @db_id.setter def db_id_setter(self, value): self._db_id = value @GObject.Property def fio(self): return self._fio @fio.setter def fio_setter(self, value): self._fio = value @GObject.Property def birth_date(self): return self._birth_date @birth_date.setter def birth_date_setter(self, value): self._birth_date = value class ReceptionRow(Gtk.ListBoxRow): @GObject.Property def db_id(self): return self._db_id @db_id.setter def db_id_setter(self, value): self._db_id = value @GObject.Property def datetime(self): return self._datetime @datetime.setter def datetime_setter(self, value): self._datetime = value @GObject.Property def scheduled(self): return self._scheduled @scheduled.setter def scheduled_setter(self, value): self._scheduled = value class CatalogRow(Gtk.ListBoxRow): @GObject.Property def db_id(self): return self._db_id @db_id.setter def db_id_setter(self, value): self._db_id = value @GObject.Property def name(self): return self._name @name.setter def name_setter(self, value): self._name = value class ListRow(Gtk.ListBoxRow): @GObject.Property def db_id(self): return self._db_id @db_id.setter def db_id_setter(self, value): self._db_id = value @GObject.Property def name(self): return self._name @name.setter def name_setter(self, value): self._name = value class MainWinHandler: def main_win_close(self, *args): Gtk.main_quit() def set_reception_list_today(self, *a): cal = builder.get_object('reception_cal') d = datetime.now() cal.select_month(d.month - 1, d.year) cal.select_day(d.day) redraw_reception_list(d) def change_reception_list_day(self, *a): cal = builder.get_object('reception_cal') cal_date = cal.get_date() d = datetime(cal_date.year, cal_date.month + 1, cal_date.day) redraw_reception_list(d) def show_reception_list_settings_win(self, button): reception_list_settings_win = create_reception_list_settings_win() reception_list_settings_win.show_all() def show_new_patient_win(self, button): new_patient_win = create_new_patient_win() new_patient_win.show_all() def show_open_patient_win(self, button): pl = builder.get_object('patient_list') row = pl.get_selected_row() open_patient_win = create_open_patient_win(row.props.db_id) open_patient_win.show_all() def show_open_list_win(self, button): ll = builder.get_object('list_list') row = ll.get_selected_row() open_list_win = create_open_list_win(row.props.db_id) open_list_win.show_all() def show_new_catalog_win(self, button): new_catalog_win = create_new_catalog_win() new_catalog_win.show_all() def patient_list_selected(self, *a): button = builder.get_object('patient_open_button') button.set_sensitive(True) def patient_list_unselected(self, *a): button = builder.get_object('patient_open_button') button.set_sensitive(False) def patient_filter_changed(self, filter_widget): pl = builder.get_object('patient_list') pl.unselect_all() self.patient_list_unselected() pl.invalidate_filter() '''pl = builder.get_object('patient_list') for r in pl.get_children(): pl.remove(r) fstr = builder.get_object('patient_filter').get_text().strip() if not fstr: for p in Patient.select(): pl.add(build_patient_row(p)) else: patients = search_patients(fstr) for p in patients: pl.add(build_patient_row(p)) pl.show_all()''' def list_list_selected(self, *a): open_button = builder.get_object('list_open_button') open_button.set_sensitive(True) def catalog_list_selected(self, *a): open_button = builder.get_object('catalog_open_button') rename_button = builder.get_object('catalog_rename_button') open_button.set_sensitive(True) rename_button.set_sensitive(True) def catalog_list_unselected(self, *a): open_button = builder.get_object('catalog_open_button') rename_button = builder.get_object('catalog_rename_button') open_button.set_sensitive(False) rename_button.set_sensitive(False) def catalog_filter_changed(self, filter_widget): cl = builder.get_object('catalog_list') cl.unselect_all() self.catalog_list_unselected() cl.invalidate_filter() def build_list_row(list_o): b = Gtk.Builder() b.add_from_string(list_row_ui_str) win = b.get_object('win') box = b.get_object('list_box') b.get_object('name').set_text(list_o.name) row = ListRow() row.props.db_id = list_o.id row.name = list_o.name win.remove(win.get_children()[0]) row.add(box) return row def build_catalog_row(catalog): b = Gtk.Builder() b.add_from_string(catalog_row_ui_str) win = b.get_object('win') box = b.get_object('catalog_box') b.get_object('name').set_text(catalog.name) row = CatalogRow() row.props.db_id = catalog.id row.name = catalog.name win.remove(win.get_children()[0]) row.add(box) return row def build_reception_row(reception_datetime): b = Gtk.Builder() b.add_from_string(reception_row_ui_str) win = b.get_object('win') box = b.get_object('reception_box') b.get_object('hour').set_text(reception_datetime.strftime('%H')) b.get_object('minute').set_text(reception_datetime.strftime('%M')) with db.atomic(): reception = Reception.select().where(Reception.time == reception_datetime) row = ReceptionRow() if len(reception): row.props.scheduled = True row.props.db_id = reception.id reception_cont = b.get_object('reception_cont') reception_cont.props.border_width = 2 reception_cont.override_background_color(Gtk.StateFlags(0), Gdk.RGBA(red=0.5, green=0.7, blue=0.5, alpha=1.0)) reception_patient = Gtk.Label() reception_patient.set_text(' '.join([reception.patient.last_name, reception.patient.first_name, reception.patient.middle_name])) reception_cont.add(reception_patient) else: row.props.scheduled = False win.remove(win.get_children()[0]) row.props.datetime = reception_datetime row.add(box) return row def build_patient_row(patient): b = Gtk.Builder() b.add_from_string(gender_ui_str[patient.gender]) win = b.get_object('win') box = b.get_object('patient_box') icon = b.get_object('icon') icon.set_from_file(os.path.join(icon_dir, f'{patient.gender}.png')) fio = f'{" ".join([patient.last_name, patient.first_name, patient.middle_name])}' b.get_object('fio').set_text(fio) birth_date = patient.birth_date b.get_object('birth_date').set_text(birth_date.strftime('%d.%m.%Y')) win.remove(win.get_children()[0]) row = PatientRow() row.props.db_id = patient.id row.props.fio = fio row.props.birth_date = birth_date row.add(box) return row def patient_sort_func(row1, row2, *a): text1 = row1.props.fio text2 = row2.props.fio return (text1 > text2) - (text1 < text2) def patient_filter_func(row): fstr = builder.get_object('patient_filter').get_text().strip() if not fstr: patient_filter.reset() return True return row.props.db_id in patient_filter.filter(fstr) def list_sort_func(row1, row2, *a): text1 = row1.props.name text2 = row2.props.name return (text1 > text2) - (text1 < text2) def catalog_sort_func(row1, row2, *a): text1 = row1.props.name text2 = row2.props.name return (text1 > text2) - (text1 < text2) def catalog_filter_func(row): fstr = builder.get_object('catalog_filter').get_text().strip() if not fstr: catalog_filter.reset() return True return row.props.db_id in catalog_filter.filter(fstr) def redraw_reception_list(selected_date): reception_timelist = get_reception_timelist(selected_date) reception_list = builder.get_object('reception_list') for c in reception_list.get_children(): reception_list.remove(c) for d in reception_timelist: reception_list.add(build_reception_row(d)) reception_list.show_all() builder.add_from_file(main_win_file) builder.connect_signals(MainWinHandler()) ##### redraw_reception_list(datetime.now()) ##### patient_list = builder.get_object('patient_list') patient_list.set_sort_func(patient_sort_func) patient_list.set_filter_func(patient_filter_func) with db.atomic(): for p in Patient.select(): patient_list.add(build_patient_row(p)) ##### list_list = builder.get_object('list_list') list_list.set_sort_func(list_sort_func) with db.atomic(): for l in List.select(): list_list.add(build_list_row(l)) ##### catalog_list = builder.get_object('catalog_list') catalog_list.set_sort_func(catalog_sort_func) catalog_list.set_filter_func(catalog_filter_func) with db.atomic(): for c in Catalog.select(): catalog_list.add(build_catalog_row(c)) def show_msg(text, sec_text='', level='info'): msg_win_file = os.path.join(ui_dir, f'{level}_win.glade') b = Gtk.Builder() b.add_from_file(msg_win_file) w = b.get_object(level) w.set_property('text',text) w.set_property('secondary_text',sec_text) w.run() w.close() def get_patient_win_values(b): l_name = b.get_object('last_name').get_text().replace(' ', '') f_name = b.get_object('first_name').get_text().replace(' ', '') m_name = b.get_object('middle_name').get_text().replace(' ', '') if '' in (l_name, f_name): show_msg('Не указаны имя или фамилия', 'Данные поля должны быть заполнены', level='warn') return None try: birth_d = int(b.get_object('birth_day').get_text()) if birth_d < 1 or birth_d > 31: return show_msg('Неверный день месяца', 'Укажите число в диапазоне 1-31', level='warn') birth_m = int(b.get_object('birth_month').get_text()) if birth_m < 1 or birth_m > 12: show_msg('Неверный номер месяца', 'Укажите число в диапазоне 1-12', level='warn') return None birth_y = int(b.get_object('birth_year').get_text()) except ValueError: show_msg('Неверно указана дата рождения', 'Все поля даты должны быть заполнены', level='warn') return None gender = b.get_object('gender').get_active_id() if not gender: show_msg('Не выбран пол', level='warn') return None birth_date = date(birth_y, birth_m, birth_d) doc_type = b.get_object('doc_type').get_active_id() doc_serial = b.get_object('doc_serial').get_text() doc_number = b.get_object('doc_number').get_text() policy_number = b.get_object('policy_number').get_text() policy_company = b.get_object('policy_company').get_text() snils_number = b.get_object('snils_number').get_text() notes_buffer = b.get_object('notes_buffer') notes_start = notes_buffer.get_start_iter() notes_end = notes_buffer.get_end_iter() notes = notes_buffer.get_text(notes_start, notes_end, True) return { 'last_name': l_name, 'first_name': f_name, 'middle_name': m_name, 'gender': gender, 'birth_date': birth_date, 'doc_type': doc_type, 'doc_serial': doc_serial, 'doc_number': doc_number, 'policy_number': policy_number, 'policy_company': policy_company, 'snils_number': snils_number, 'notes': notes } def set_patient_win_values(patient_id, b, edit=False): with db.atomic(): pat = Patient.select().where(Patient.id == patient_id).get() b.get_object('last_name').set_text(pat.last_name) b.get_object('first_name').set_text(pat.first_name) b.get_object('middle_name').set_text(pat.middle_name) if not edit: b.get_object('gender').set_text(gender_dict[pat.gender]) else: b.get_object('gender').set_active_id(pat.gender) if not edit: b.get_object('birth_date').set_text(pat.birth_date.strftime('%d.%m.%Y')) else: b.get_object('birth_day').set_text(str(pat.birth_date.day)) b.get_object('birth_month').set_text(str(pat.birth_date.month)) b.get_object('birth_year').set_text(str(pat.birth_date.year)) if not edit: b.get_object('doc_type').set_text(doc_type_dict[pat.doc_type] if pat.doc_type else '') elif pat.doc_type: b.get_object('doc_type').set_active_id(pat.doc_type) b.get_object('doc_serial').set_text(pat.doc_serial) b.get_object('doc_number').set_text(pat.doc_number) b.get_object('policy_number').set_text(pat.policy_number) b.get_object('policy_company').set_text(pat.policy_company) b.get_object('snils_number').set_text(pat.snils_number) b.get_object('notes_buffer').set_text(pat.notes) def create_open_patient_win(patient_id): b = Gtk.Builder() class OpenPatientWinHandler: def show_edit_patient_win(self, *a): edit_patient_win = create_edit_patient_win(patient_id) w.destroy() edit_patient_win.show_all() b.add_from_file(open_patient_win_file) b.connect_signals(OpenPatientWinHandler()) w = b.get_object('open_patient_window') # db_id = b.get_object('db_id') # db_id.set_text(str(patient_id)) set_patient_win_values(patient_id, b) return w def create_edit_patient_win(patient_id): b = Gtk.Builder() class EditPatientWinHandler: def edit_patient_win_close(self, *args): w.destroy() open_patient_win = create_open_patient_win(patient_id) open_patient_win.show_all() def only_digits(self, entry): text = entry.get_text() text = ''.join(filter(lambda x: x.isdigit(), text)) entry.set_text(text) def save_patient(self, *args): values = get_patient_win_values(b) if not values: return with db.atomic(): try: Patient.update(**values).where(Patient.id == patient_id).execute() except peewee.IntegrityError: return show_msg('Данный пациент уже существует', 'Другой пациент с указанными фамилией, именем\nи датой рождения уже есть с базе данных', level='warn') patient = Patient.select().where(Patient.id == patient_id).get() update_patient_index(patient) cur_row = list(filter(lambda x: x.props.db_id == patient_id, patient_list.get_children()))[0] patient_list.remove(cur_row) row = build_patient_row(patient) patient_list.add(row) patient_list.select_row(row) patient_list.show_all() b.get_object('edit_patient_window').close() b.add_from_file(edit_patient_win_file) b.connect_signals(EditPatientWinHandler()) w = b.get_object('edit_patient_window') set_patient_win_values(patient_id, b, edit=True) return w def create_new_patient_win(): b = Gtk.Builder() class NewPatienWinHandler: def only_digits(self, entry): text = entry.get_text() text = ''.join(filter(lambda x: x.isdigit(), text)) entry.set_text(text) def save_patient(self, *args): values = get_patient_win_values(b) if not values: return with db.atomic(): try: patient = Patient.create(**values) except peewee.IntegrityError: return show_msg('Данный пациент уже существует', 'Пациент с указанными фамилией, именем\nи датой рождения уже есть с базе данных', level='warn') store_patient_index(patient) row = build_patient_row(patient) patient_list.add(row) patient_list.select_row(row) patient_list.show_all() b.get_object('new_patient_window').close() b.add_from_file(new_patient_win_file) b.connect_signals(NewPatienWinHandler()) w = b.get_object('new_patient_window') return w def create_reception_list_settings_win(): b = Gtk.Builder() class ReceptionListSettingsHandler: def save_settings(self, *a): start_hour = int(b.get_object('start_hour').get_value()) start_minute = int(b.get_object('start_minute').get_value()) end_hour = int(b.get_object('end_hour').get_value()) end_minute = int(b.get_object('end_minute').get_value()) interval = int(b.get_object('interval').get_value()) s_set_reception_list([start_hour, start_minute], [end_hour, end_minute], interval) redraw_reception_list(datetime.now()) b.get_object('reception_list_settings_win').close() b.add_from_file(reception_list_settings_win_file) b.connect_signals(ReceptionListSettingsHandler()) s = s_get_reception_list() b.get_object('start_hour').set_value(s.day_start[0]) b.get_object('start_minute').set_value(s.day_start[1]) b.get_object('end_hour').set_value(s.day_end[0]) b.get_object('end_minute').set_value(s.day_end[1]) b.get_object('interval').set_value(s.interval) w = b.get_object('reception_list_settings_win') return w def create_new_catalog_win(): b = Gtk.Builder() class NewCatalogHandler: def save_catalog(self, *a): catname = re.sub(r'\s+', ' ', b.get_object('catalog_name').get_text()).strip() if not len(catname): return show_msg('Не указано имя справочника', 'Данное поле должно быть заполнено', level='warn') try: catalog = add_catalog(catname) except peewee.IntegrityError: return show_msg('Данный справочник уже существует', 'Справочник с указанным названием уже есть с базе данных', level='warn') row = build_catalog_row(catalog) catalog_list.add(row) catalog_list.select_row(row) catalog_list.show_all() b.get_object('new_catalog_window').close() b.add_from_file(new_catalog_win_file) b.connect_signals(NewCatalogHandler()) w = b.get_object('new_catalog_window') return w main_win = builder.get_object('main_window') main_win.show_all() Gtk.main()