diff --git a/app.py b/app.py index 95f30ad..d97a37f 100644 --- a/app.py +++ b/app.py @@ -5,8 +5,9 @@ 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.lists import create_open_list_win from mods.files import list_row_ui_str +from mods.utils import show_msg, ConditionalFilter from datetime import date, datetime, timedelta import peewee import re @@ -55,18 +56,6 @@ def get_reception_timelist(rec_date): 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) @@ -181,18 +170,6 @@ class MainWinHandler: 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) @@ -341,15 +318,7 @@ 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(' ', '') diff --git a/mods/catalogs.py b/mods/catalogs.py index f9024fc..082479f 100644 --- a/mods/catalogs.py +++ b/mods/catalogs.py @@ -1,6 +1,5 @@ from mods.db import db, Catalog, CatalogIndex - - +import re def add_catalog(name): with db.atomic(): @@ -14,6 +13,8 @@ def add_catalog(name): return catalog def search_catalogs(q): + q = re.sub(r'\s+', ' ', q).strip() + q = ' '.join([ f'{x}*' for x in q.split(' ')]) with db.atomic(): return (Catalog.select() .join( diff --git a/mods/db.py b/mods/db.py index b89d10b..1f84e10 100644 --- a/mods/db.py +++ b/mods/db.py @@ -1,6 +1,7 @@ import os from peewee import Model, CharField, DateTimeField, TextField, ForeignKeyField, DateField, fn from playhouse.sqlite_ext import CSqliteExtDatabase, FTS5Model, AutoIncrementField, SearchField, RowIDField, JSONField +import re var_dir = os.path.join(os.path.abspath(os.environ['HOME']), '.eldoc') db_dir = os.path.join(var_dir, 'db') @@ -87,7 +88,7 @@ Patient.add_index( Patient.index(fn.lower(Patient.last_name), fn.lower(Patient.first_name), Patient.birth_date, unique=True, name='patient_first_last_name_birth_date_unique') ) Catalog.add_index(Catalog.index(fn.lower(Catalog.name), unique=True, name='catalog_name_unique')) -ListRecord.add_index(ListRecord.index(ListRecord.list, fn.lower(ListRecord.text), unique=True)) +ListRecord.add_index(ListRecord.index(ListRecord.list, fn.lower(ListRecord.text), unique=True, name='listrec_unique')) db.create_tables([ Patient, PatientIndex, @@ -105,6 +106,8 @@ db.create_tables([ ]) def search_patients(q): + q = re.sub(r'\s+', ' ', q).strip() + q = ' '.join([ f'{x}*' for x in q.split(' ')]) with db.atomic(): return (Patient.select() .join( diff --git a/mods/files.py b/mods/files.py index 36ec732..a9733c2 100644 --- a/mods/files.py +++ b/mods/files.py @@ -9,3 +9,6 @@ open_list_win_file = os.path.join(ui_dir, 'open_list_win.glade') listrecord_row_file = os.path.join(ui_dir, 'listrecord_row.glade') with open(listrecord_row_file, 'r') as f: listrecord_row_ui_str = f.read() +editable_row_file = os.path.join(ui_dir, 'editable_row.glade') +with open(editable_row_file, 'r') as f: + editable_row_ui_str = f.read() diff --git a/mods/lists.py b/mods/lists.py index 46e2c79..3173677 100644 --- a/mods/lists.py +++ b/mods/lists.py @@ -1,8 +1,11 @@ import gi gi.require_version('Gtk', '3.0') -from gi.repository import Gtk, GObject +from gi.repository import Gtk, GObject, Gdk from mods.db import db, List, ListRecord, ListRecordIndex -from mods.files import open_list_win_file, listrecord_row_ui_str +from mods.files import open_list_win_file, listrecord_row_ui_str, editable_row_ui_str +from mods.utils import show_msg, disable_widget, enable_widget, ConditionalFilter +import re +import peewee lists_map = { 'diagnoz': 'Диагноз', @@ -17,6 +20,13 @@ for s_id in lists_map: if not len(q): List.create(name=lists_map[s_id], system_id=s_id) +class ListRecFilter(ConditionalFilter): + def filter(self, list_id, query): + if query != self.fstr: + self.fstr = query + self.ids = list(map(lambda x: x.id, self.search_func(list_id, query))) + return self.ids + class ListRecordRow(Gtk.ListBoxRow): @GObject.Property def db_id(self): @@ -31,22 +41,12 @@ class ListRecordRow(Gtk.ListBoxRow): def text_setter(self, value): self._text = value -def build_listrecord_row(listrec_o): - b = Gtk.Builder() - b.add_from_string(listrecord_row_ui_str) - win = b.get_object('win') - box = b.get_object('listrecord_box') - b.get_object('text').set_text(listrec_o.text) - row = ListRecordRow() - row.props.db_id = listrec_o.id - row.text = listrec_o.text - win.remove(win.get_children()[0]) - row.add(box) - return row - def get_list(list_id): with db.atomic(): return List.get_by_id(list_id) +def get_listrecord(listrec_id): + with db.atomic(): + return ListRecord.get_by_id(listrec_id) def get_all_list_records(list_id): with db.atomic(): @@ -68,14 +68,17 @@ def delete_list_record(listrec_id): ListRecord.delete().where(ListRecord.id == listrec_id).execute() ListRecordIndex.delete().where(ListRecordIndex.rowid == listrec_id).execute() def change_list_record(listrec_id, new_text): - listrec_o = ListRecord.get_by_id(listrec_id) + listrec_o = get_listrecord(listrec_id) if listrec_o.text == new_text: - return False + return listrec_o with db.atomic(): - ListRecord.update(text=new_text).execute() - ListRecordIndex.update(text=new_text).execute() + ListRecord.update(text=new_text).where(ListRecord.id == listrec_id).execute() + ListRecordIndex.update(text=new_text).where(ListRecordIndex.rowid == listrec_id).execute() + return ListRecord.get_by_id(listrec_id) def search_list_record(list_id, q): - list_o = List.get_by_id(list_id) + q = re.sub(r'\s+', ' ', q).strip() + q = ' '.join([ f'{x}*' for x in q.split(' ')]) + list_o = get_list(list_id) with db.atomic(): return (ListRecord.select() .join( @@ -83,19 +86,146 @@ def search_list_record(list_id, q): on=(ListRecord.id == ListRecordIndex.rowid)) .where((ListRecord.list == list_o) & (ListRecordIndex.match(q))) .order_by(ListRecordIndex.bm25())) +##################################################################################################################### +def build_listrecord_row(listrec_o): + b = Gtk.Builder() + b.add_from_string(listrecord_row_ui_str) + win = b.get_object('win') + box = b.get_object('listrecord_box') + b.get_object('text').set_text(listrec_o.text) + row = ListRecordRow() + row.props.db_id = listrec_o.id + row.text = listrec_o.text + win.remove(win.get_children()[0]) + row.add(box) + return row + +def build_listrec_editable_row(listrec_o=None): + b = Gtk.Builder() + b.add_from_string(editable_row_ui_str) + #b.connect_signals({'editing_done': listrec_editing_done}) + win = b.get_object('win') + box = b.get_object('editable_box') + text_input = b.get_object('text_input') + text_input.set_text(listrec_o.text if listrec_o else '') + row = ListRecordRow() + row.props.db_id = listrec_o.id if listrec_o else -1 + row.text = listrec_o.text if listrec_o else '' + win.remove(win.get_children()[0]) + row.add(box) + return (row, text_input) def create_open_list_win(list_id): list_o = get_list(list_id) b = Gtk.Builder() + listrecord_filter = ListRecFilter(search_list_record) class OpenListHandler: - pass + def row_add_cancel(self, entry, ev, row): + if ev.keyval == Gdk.KEY_Escape: + lr_list.remove(row) + row.destroy() + def row_edit_cancel(self, entry, ev, edit_row): + if ev.keyval == Gdk.KEY_Escape: + rec = get_listrecord(edit_row.props.db_id) + row = build_listrecord_row(rec) + lr_list.remove(edit_row) + edit_row.destroy() + lr_list.add(row) + lr_list.show_all() + def enable_buttons_on_add_edit(self, *a): + enable_widget([add_button, edit_button, remove_button, lr_filter]) + def remove_row(self, button): + row = lr_list.get_selected_row() + delete_list_record(row.props.db_id) + lr_list.remove(row) + row.destroy() + lr_list.show_all() + self.listrec_row_unselected() + def edit_row(self, button): + row = lr_list.get_selected_row() + rec = get_listrecord(row.props.db_id) + (edit_row, inp) = build_listrec_editable_row(rec) + lr_list.remove(row) + row.destroy() + lr_list.add(edit_row) + inp.grab_focus() + inp.connect('key-release-event', self.row_edit_cancel, edit_row) + inp.connect('activate', self.update_row, edit_row) + inp.connect('destroy', self.enable_buttons_on_add_edit) + disable_widget([add_button, edit_button, remove_button, lr_filter]) + lr_list.show_all() + def add_new_row(self, button): + (row, text_input) = build_listrec_editable_row() + lr_list.add(row) + text_input.grab_focus() + text_input.connect('key-release-event', self.row_add_cancel, row) + text_input.connect('activate', self.save_new_row, row) + text_input.connect('destroy', self.enable_buttons_on_add_edit) + disable_widget([add_button, edit_button, remove_button, lr_filter]) + lr_list.show_all() + def update_row(self, inp, edit_row): + new_text = re.sub(r'\s+', ' ', inp.get_text()).strip() + if new_text: + try: + rec = change_list_record(edit_row.props.db_id, new_text) + except peewee.IntegrityError: + return show_msg('Такая же запись уже существует', 'В данном списке уже существует\nдругая запись с данным текстом', level='warn') + row = build_listrecord_row(rec) + else: + row = build_listrecord_row(get_listrecord(edit_row.props.db_id)) + lr_list.add(row) + lr_list.select_row(row) + lr_list.show_all() + lr_list.remove(edit_row) + edit_row.destroy() + def save_new_row(self, inp, row): + row_text = re.sub(r'\s+', ' ', inp.get_text()).strip() + if row_text: + try: + rec = add_list_record(list_id, row_text) + except peewee.IntegrityError: + return show_msg('Данная запись уже существует', 'В данном списке уже существует\nзапись с данным текстом', level='warn') + new_row = build_listrecord_row(rec) + lr_list.add(new_row) + lr_list.select_row(new_row) + lr_list.show_all() + lr_list.remove(row) + row.destroy() + def listrec_row_selected(self, *a): + enable_widget([edit_button, remove_button]) + def listrec_row_unselected(self, *a): + disable_widget([edit_button, remove_button]) + def listrec_filter_changed(self, filter_widget): + lr_list.unselect_all() + self.listrec_row_unselected() + lr_list.invalidate_filter() b.add_from_file(open_list_win_file) b.connect_signals(OpenListHandler()) + # Gtk objects w = b.get_object('open_list_window') - listrec_list = b.get_object('listrecord_list') + lr_list = b.get_object('listrecord_list') + lr_filter = b.get_object('listrecord_filter') + add_button = b.get_object('add_button') + edit_button = b.get_object('edit_button') + remove_button = b.get_object('remove_button') list_header = b.get_object('list_win_header') + ############# + ###### + def listrecord_sort_func(row1, row2, *a): + text1 = row1.props.text + text2 = row2.props.text + return (text1 > text2) - (text1 < text2) + def listrecord_filter_func(row): + fstr = lr_filter.get_text().strip() + if not fstr: + listrecord_filter.reset() + return True + return row.props.db_id in listrecord_filter.filter(list_id, fstr) + ###### + lr_list.set_sort_func(listrecord_sort_func) + lr_list.set_filter_func(listrecord_filter_func) list_header.props.title = list_o.name for lr in get_all_list_records(list_id): - listrec_list.add(build_listrecord_row(lr)) + lr_list.add(build_listrecord_row(lr)) return w \ No newline at end of file diff --git a/mods/utils.py b/mods/utils.py new file mode 100644 index 0000000..8a46cdb --- /dev/null +++ b/mods/utils.py @@ -0,0 +1,34 @@ +import os +from mods.files import ui_dir +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +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 enable_widget(w_list): + for widget in w_list: + widget.set_sensitive(True) +def disable_widget(w_list): + for widget in w_list: + widget.set_sensitive(False) + +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 = [] diff --git a/res/ui/editable_row.glade b/res/ui/editable_row.glade new file mode 100644 index 0000000..7f88ed9 --- /dev/null +++ b/res/ui/editable_row.glade @@ -0,0 +1,29 @@ + + + + + + False + + + + + + True + False + vertical + + + True + True + + + True + True + 0 + + + + + + diff --git a/res/ui/listrecord_list_row.glade b/res/ui/listrecord_list_row.glade index f60f56a..2b06590 100644 --- a/res/ui/listrecord_list_row.glade +++ b/res/ui/listrecord_list_row.glade @@ -19,7 +19,7 @@ label 0 - + diff --git a/res/ui/listrecord_row.glade b/res/ui/listrecord_row.glade index 6f49541..f60f56a 100644 --- a/res/ui/listrecord_row.glade +++ b/res/ui/listrecord_row.glade @@ -9,11 +9,11 @@ - + True False - + True False label diff --git a/res/ui/open_list_win.glade b/res/ui/open_list_win.glade index d904964..4c8f346 100644 --- a/res/ui/open_list_win.glade +++ b/res/ui/open_list_win.glade @@ -15,17 +15,18 @@ False True - + gtk-add True True True True True + - + gtk-edit True False @@ -33,13 +34,14 @@ True True True + 1 - + gtk-remove True False @@ -47,6 +49,7 @@ True True True + 2 @@ -55,22 +58,85 @@ - + True - True - never - in + False + vertical - + True - False + True + never + in - + True False + + + True + False + + + + + True + True + 0 + + + + + True + False + 3 + 3 + + + True + False + + + True + True + 0 + + + + + True + True + edit-find-symbolic + False + False + + + + True + True + 1 + + + + + True + False + + + True + True + 2 + + + + + False + True + 1 +