From ead0ec5f584b1fc92e4442caece967969c5dac9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D0=BE=D1=80=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=A0=D0=BE?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD?= Date: Tue, 12 Nov 2019 19:30:17 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BF=D0=B8=D1=81=D0=BE=D0=BA=20=D1=81?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D1=87=D0=BD=D0=B8=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 118 ++++++++++++---- mods/db.py | 183 +++++++++++++----------- mods/settings.py | 40 ++++++ res/ui/catalog_list_row.glade | 34 +++++ res/ui/edit_patient_win.glade | 5 + res/ui/main_win.glade | 255 ++++++++++++++++++++++++++++++++-- res/ui/new_patient_win.glade | 4 + res/ui/open_patient_win.glade | 2 + 8 files changed, 529 insertions(+), 112 deletions(-) create mode 100644 mods/settings.py create mode 100644 res/ui/catalog_list_row.glade diff --git a/app.py b/app.py index dd172e5..2288050 100644 --- a/app.py +++ b/app.py @@ -2,16 +2,12 @@ import gi import os gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GObject, Gdk -from mods.db import Patient, Reception, store_patient_index, update_patient_index, search_patients, db -from datetime import date, datetime, timedelta, time +from mods.db import store_patient_index, update_patient_index, search_patients, dbo +from mods.settings import s_get_reception_list, s_set_reception_list +from mods.catalogs import add_catalog +from datetime import date, datetime, timedelta import peewee -# FIXME: временно -DAY_START = time(8,0) -DAY_END = time(13,0) -TIME_INTERVAL = timedelta(minutes=30) -# - gender_dict = { 'male': 'Мужской', 'female': 'Женский' @@ -30,10 +26,14 @@ 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') 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() @@ -44,11 +44,12 @@ with open(female_patient_row_file, 'r') as f: builder = Gtk.Builder() def get_reception_timelist(rec_date): - dstart = datetime(rec_date.year, rec_date.month, rec_date.day, DAY_START.hour, DAY_START.minute) - dend = datetime(rec_date.year, rec_date.month, rec_date.day, DAY_END.hour, DAY_END.minute) + 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, int(TIME_INTERVAL.seconds / 60)) - return [(dstart + timedelta(minutes=x)).time() for x in shift_minutes_range] + shift_minutes_range = range(0, work_day_minutes_total, s.interval) + return [dstart + timedelta(minutes=x) for x in shift_minutes_range] class PatientFilter: def __init__(self): @@ -103,9 +104,31 @@ class ReceptionRow(Gtk.ListBoxRow): 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 + 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() @@ -138,6 +161,18 @@ class MainWinHandler: pl.add(build_patient_row(p)) pl.show_all()''' +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 + 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) @@ -145,10 +180,12 @@ def build_reception_row(reception_datetime): 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')) - reception = Reception.select().where(Reception.time == reception_datetime) + with dbo.db.atomic(): + reception = dbo._Reception.select().where(dbo._Reception.time == reception_datetime) # @UndefinedVariable 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)) @@ -194,8 +231,11 @@ def patient_filter_func(row): 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()) @@ -205,9 +245,15 @@ 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(): +with dbo.db.atomic(): + for p in dbo._Patient.select(): # @UndefinedVariable patient_list.add(build_patient_row(p)) +##### +catalog_list = builder.get_object('catalog_list') + +with dbo.db.atomic(): + for c in dbo._Catalog.select(): # @UndefinedVariable + 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') @@ -267,8 +313,9 @@ def get_patient_win_values(b): 'snils_number': snils_number, 'notes': notes } -def set_patient_values(patient_id, b, edit=False): - pat = Patient.select().where(Patient.id == patient_id).get() +def set_patient_win_values(patient_id, b, edit=False): + with dbo.db.atomic(): + pat = dbo._Patient.select().where(dbo._Patient.id == patient_id).get() # @UndefinedVariable 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) @@ -305,7 +352,7 @@ def create_open_patient_win(patient_id): w = b.get_object('open_patient_window') # db_id = b.get_object('db_id') # db_id.set_text(str(patient_id)) - set_patient_values(patient_id, b) + set_patient_win_values(patient_id, b) return w def create_edit_patient_win(patient_id): @@ -323,12 +370,12 @@ def create_edit_patient_win(patient_id): values = get_patient_win_values(b) if not values: return - with db.atomic(): + with dbo.db.atomic(): try: - Patient.update(**values).where(Patient.id == patient_id).execute() + dbo._Patient.update(**values).where(dbo._Patient.id == patient_id).execute() # @UndefinedVariable except peewee.IntegrityError: return show_msg('Данный пациент уже существует', 'Другой пациент с указанными фамилией, именем\nи датой рождения уже есть с базе данных', level='warn') - patient = Patient.select().where(Patient.id == patient_id).get() + patient = dbo._Patient.select().where(dbo._Patient.id == patient_id).get() # @UndefinedVariable 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) @@ -340,7 +387,7 @@ def create_edit_patient_win(patient_id): b.add_from_file(edit_patient_win_file) b.connect_signals(EditPatientWinHandler()) w = b.get_object('edit_patient_window') - set_patient_values(patient_id, b, edit=True) + set_patient_win_values(patient_id, b, edit=True) return w def create_new_patient_win(): @@ -354,9 +401,9 @@ def create_new_patient_win(): values = get_patient_win_values(b) if not values: return - with db.atomic(): + with dbo.db.atomic(): try: - patient = Patient.create(**values) + patient = dbo._Patient.create(**values) # @UndefinedVariable except peewee.IntegrityError: return show_msg('Данный пациент уже существует', 'Пациент с указанными фамилией, именем\nи датой рождения уже есть с базе данных', level='warn') store_patient_index(patient) @@ -370,6 +417,29 @@ def create_new_patient_win(): 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 + main_win = builder.get_object('main_window') main_win.show_all() Gtk.main() diff --git a/mods/db.py b/mods/db.py index faef363..a5ea089 100644 --- a/mods/db.py +++ b/mods/db.py @@ -1,6 +1,6 @@ import os -from peewee import Model, CharField, BigAutoField, DateTimeField, IntegerField, fn, Field, Expression, TextField, ForeignKeyField, DateField, TimeField -from playhouse.sqlite_ext import CSqliteExtDatabase, FTS5Model, AutoIncrementField, SearchField, RowIDField +from peewee import Model, CharField, DateTimeField, TextField, ForeignKeyField, DateField +from playhouse.sqlite_ext import CSqliteExtDatabase, FTS5Model, AutoIncrementField, SearchField, RowIDField, JSONField var_dir = os.path.join(os.path.abspath(os.environ['HOME']), '.eldoc') db_dir = os.path.join(var_dir, 'db') @@ -11,98 +11,121 @@ if not os.path.exists(var_dir): if not os.path.exists(db_dir): os.mkdir(db_dir) -db = CSqliteExtDatabase(db_file, pragmas={ - 'journal_mode': 'wal', - 'cache_size': -1 * 64000, # 64MB - 'foreign_keys': 'on', - 'ignore_check_constraints': 'off', - 'synchronous': 'off'}) - -class BaseModel(Model): - 'Базовая таблица' - class Meta: - database = db -class BaseFTSModel(FTS5Model): - class Meta: - database = db - options = {'tokenize': 'porter'} - -class Patient(BaseModel): - id = AutoIncrementField() - last_name = TextField() - first_name = TextField() - middle_name = TextField(null=True) - birth_date = DateField() - gender = CharField(6) - doc_type = CharField(32, null=True) - doc_serial = TextField(null=True) - doc_number = TextField(null=True) - policy_number = TextField(null=True) - policy_company = TextField(null=True) - snils_number = TextField(null=True) - notes = TextField(null=True) -Patient.add_index( - Patient.index(Patient.last_name, Patient.first_name, Patient.birth_date, unique=True) - ) -class Reception(BaseModel): - id = AutoIncrementField() - patient = ForeignKeyField(Patient, backref='receptions', on_delete='CASCADE') - time = DateTimeField() -class Diagnosis(BaseModel): - id = AutoIncrementField() - code = CharField() - title = TextField() - description = TextField(null=True) -class ReceptionDiagnosis(BaseModel): - id = AutoIncrementField() - reception = ForeignKeyField(Reception, backref='reception_diagnosisses', on_delete='CASCADE') - diagnosis = ForeignKeyField(Diagnosis, backref='reception_diagnosisses', on_delete='CASCADE') -class ReceptionAnamnesis(BaseModel): - id = AutoIncrementField() - reception = ForeignKeyField(Reception, backref='reception_anamnesisses', on_delete='CASCADE') - text = TextField() -class AnamnesisTemplate(BaseModel): - id = AutoIncrementField() - text = TextField() -class PatientIndex(BaseFTSModel): - rowid = RowIDField() - fio = SearchField() +class DBO: + def __init__(self): + self.db = CSqliteExtDatabase(db_file, pragmas={ + 'journal_mode': 'wal', + 'cache_size': -1 * 64000, # 64MB + 'foreign_keys': 'on', + 'ignore_check_constraints': 'off', + 'synchronous': 'off'}) + class BaseModel(Model): + 'Базовая таблица' + class Meta: + database = self.db + class BaseFTSModel(FTS5Model): + class Meta: + database = self.db + options = {'tokenize': 'porter'} + class Patient(BaseModel): + id = AutoIncrementField() + last_name = TextField() + first_name = TextField() + middle_name = TextField(null=True) + birth_date = DateField() + gender = CharField(6) + doc_type = CharField(32, null=True) + doc_serial = TextField(null=True) + doc_number = TextField(null=True) + policy_number = TextField(null=True) + policy_company = TextField(null=True) + snils_number = TextField(null=True) + notes = TextField(null=True) + class Catalog(BaseModel): + id = AutoIncrementField() + system_id = TextField(null=True) + name = TextField() + class CatalogRecord(BaseModel): + id = AutoIncrementField() + catalog = ForeignKeyField(Catalog, backref='catalog_records', on_delete='CASCADE') + text = TextField() + class CatalogRecordIndex(BaseFTSModel): + rowid = RowIDField() + text = SearchField() + class Reception(BaseModel): + id = AutoIncrementField() + patient = ForeignKeyField(Patient, backref='receptions', on_delete='CASCADE') + time = DateTimeField() + class ReceptionDiagnosis(BaseModel): + id = AutoIncrementField() + reception = ForeignKeyField(Reception, backref='reception_diagnosisses', on_delete='CASCADE') + diagnosis = ForeignKeyField(CatalogRecord, backref='reception_diagnosisses', on_delete='CASCADE') + class ReceptionAnamnesis(BaseModel): + id = AutoIncrementField() + reception = ForeignKeyField(Reception, backref='reception_anamnesisses', on_delete='CASCADE') + text = TextField() + class PatientIndex(BaseFTSModel): + rowid = RowIDField() + fio = SearchField() + ### Настройки + class Settings(BaseModel): + key = TextField() + val = JSONField() + ### + self._Patient = Patient + self._Catalog = Catalog + self._CatalogRecord = CatalogRecord + self._CatalogRecordIndex = CatalogRecordIndex + self._Reception = Reception + self._ReceptionDiagnosis = ReceptionDiagnosis + self._ReceptionAnamnesis = ReceptionAnamnesis + self._PatientIndex = PatientIndex + self._Settings = Settings + self.db.connect() + def create(self): + self._Patient.add_index( + self._Patient.index(self._Patient.last_name, self._Patient.first_name, self._Patient.birth_date, unique=True) + ) + self.db.create_tables([ + self._Patient, + self._PatientIndex, + self._Catalog, + self._CatalogRecord, + self._CatalogRecordIndex, + self._Reception, + self._ReceptionDiagnosis, + self._ReceptionAnamnesis, + self._Settings + ]) def search_patients(q): - with db.atomic(): - return (Patient.select() + with dbo.db.atomic(): + return (dbo._Patient.select() .join( - PatientIndex, - on=(Patient.id == PatientIndex.rowid)) - .where(PatientIndex.match(q)) - .order_by(PatientIndex.bm25())) + dbo._PatientIndex, + on=(dbo._Patient.id == dbo._PatientIndex.rowid)) + .where(dbo._PatientIndex.match(q)) + .order_by(dbo._PatientIndex.bm25())) def store_patient_index(pat): fio_list = [pat.last_name, pat.first_name] if pat.middle_name: fio_list.append(pat.middle_name) - PatientIndex.insert( + dbo._PatientIndex.insert( { - PatientIndex.rowid: pat.id, - PatientIndex.fio: ' '.join(fio_list) + dbo._PatientIndex.rowid: pat.id, + dbo._PatientIndex.fio: ' '.join(fio_list) } ).execute() def update_patient_index(pat): fio_list = [pat.last_name, pat.first_name] if pat.middle_name: fio_list.append(pat.middle_name) - PatientIndex.update( + dbo._PatientIndex.update( { - PatientIndex.fio: ' '.join(fio_list) + dbo._PatientIndex.fio: ' '.join(fio_list) } - ).where(PatientIndex.rowid == pat.id).execute() + ).where(dbo._PatientIndex.rowid == pat.id).execute() -db.connect() -db.create_tables([ - Patient, - PatientIndex, - Reception, - Diagnosis, - ReceptionDiagnosis, - ReceptionAnamnesis, - AnamnesisTemplate - ]) + +dbo = DBO() +dbo.create() diff --git a/mods/settings.py b/mods/settings.py new file mode 100644 index 0000000..db9b765 --- /dev/null +++ b/mods/settings.py @@ -0,0 +1,40 @@ +from mods.db import dbo + +# Имена ключей параметров +S_KEY_RECEPTION_LIST = 'reception_list' +S_RECEPTION_LIST_DAY_START = 'day_start' +S_RECEPTION_LIST_DAY_END = 'day_end' +S_RECEPTION_LIST_INTERVAL = 'interval' + +# Получение параметров +def s_get_reception_list(): + class RLSettings: + day_start = None + day_end = None + interval = None + s = dbo._Settings.get(dbo._Settings.key == S_KEY_RECEPTION_LIST).val # @UndefinedVariable + o = RLSettings() + o.day_start = s[S_RECEPTION_LIST_DAY_START] + o.day_end = s[S_RECEPTION_LIST_DAY_END] + o.interval = s[S_RECEPTION_LIST_INTERVAL] + return o +# Установка параметров +def s_set_reception_list(dstart, dend, interval): + v = { + S_RECEPTION_LIST_DAY_START: dstart, + S_RECEPTION_LIST_DAY_END: dend, + S_RECEPTION_LIST_INTERVAL: interval + } + with dbo.db.atomic(): + q = dbo._Settings.select().where(dbo._Settings.key == S_KEY_RECEPTION_LIST) # @UndefinedVariable + if not len(q): + dbo._Settings.create(key=S_KEY_RECEPTION_LIST, val=v) # @UndefinedVariable + else: + dbo._Settings.update(val=v).where(dbo._Settings.key == S_KEY_RECEPTION_LIST).execute() # @UndefinedVariable + +### Инициализация начальных значений параметров +# Начальный график приёмов +with dbo.db.atomic(): + q = dbo._Settings.select().where(dbo._Settings.key == S_KEY_RECEPTION_LIST) # @UndefinedVariable +if not len(q): + s_set_reception_list([8,0], [17,0], 30) diff --git a/res/ui/catalog_list_row.glade b/res/ui/catalog_list_row.glade new file mode 100644 index 0000000..bdf0174 --- /dev/null +++ b/res/ui/catalog_list_row.glade @@ -0,0 +1,34 @@ + + + + + + False + False + + + + + + True + False + + + True + False + label + 0 + + + + + + True + True + 1 + + + + + + diff --git a/res/ui/edit_patient_win.glade b/res/ui/edit_patient_win.glade index 4d12dd2..4ca8bc9 100644 --- a/res/ui/edit_patient_win.glade +++ b/res/ui/edit_patient_win.glade @@ -6,11 +6,13 @@ False True + center True False + Изменение данных пациента True @@ -212,6 +214,7 @@ True True + День 2 3 digits @@ -239,6 +242,7 @@ True True + Месяц 2 3 digits @@ -266,6 +270,7 @@ True True + Год 4 5 digits diff --git a/res/ui/main_win.glade b/res/ui/main_win.glade index 3b42943..9b123ae 100644 --- a/res/ui/main_win.glade +++ b/res/ui/main_win.glade @@ -70,6 +70,30 @@ True + + + True + False + + + False + True + + + + + True + False + Настройки + True + gtk-preferences + + + + False + True + + False @@ -78,14 +102,90 @@ - + True False + + + True + True + never + in + + + True + False + + + True + False + + + + + + + True + True + 0 + + + + + True + False + vertical + + + True + True + + + + False + True + 0 + + + + + True + False + both + + + True + False + Сегодня + True + gtk-refresh + + + + True + True + + + + + False + True + 1 + + + + + False + True + end + 1 + + - False + True True - 2 + 1 @@ -143,13 +243,27 @@ - + True - False - + True + never + in + + + True + False + + + True + False + + + + + - False + True True 1 @@ -183,7 +297,7 @@ - False + True True 1 @@ -214,6 +328,131 @@ 1 + + + True + False + vertical + + + True + False + both + + + True + False + Создать + True + gtk-add + + + False + True + + + + + True + False + Переименовать + True + gtk-index + + + False + True + + + + + False + True + 0 + + + + + True + True + in + + + True + False + + + True + False + + + + + + + True + True + 1 + + + + + True + False + 3 + 3 + 3 + 3 + + + True + False + + + True + True + 0 + + + + + True + True + edit-find-symbolic + False + False + + + True + True + 1 + + + + + True + False + + + True + True + 2 + + + + + False + True + 2 + + + + + page2 + Справочники + 2 + + True diff --git a/res/ui/new_patient_win.glade b/res/ui/new_patient_win.glade index fb77fc2..fa4eb34 100644 --- a/res/ui/new_patient_win.glade +++ b/res/ui/new_patient_win.glade @@ -6,6 +6,7 @@ False True + center True @@ -212,6 +213,7 @@ True True + День 2 3 digits @@ -239,6 +241,7 @@ True True + Месяц 2 3 digits @@ -266,6 +269,7 @@ True True + Год 4 5 digits diff --git a/res/ui/open_patient_win.glade b/res/ui/open_patient_win.glade index e6d7fcd..716dd25 100644 --- a/res/ui/open_patient_win.glade +++ b/res/ui/open_patient_win.glade @@ -6,10 +6,12 @@ False True + center True False + Данные пациента True