]> Softwares of Agnibho - medscript.git/blob - index.py
Bugfix: Windows uninstall package permission error
[medscript.git] / index.py
1 # MedScript
2 # Copyright (C) 2023 Dr. Agnibho Mondal
3 # This file is part of MedScript.
4 # MedScript is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
5 # MedScript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
6 # You should have received a copy of the GNU General Public License along with MedScript. If not, see <https://www.gnu.org/licenses/>.
7
8 from PyQt6.QtWidgets import QWidget, QMainWindow, QFormLayout, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit, QTableView, QAbstractItemView, QFileDialog
9 from PyQt6.QtGui import QIcon, QStandardItemModel, QStandardItem
10 from PyQt6.QtCore import Qt, pyqtSignal, QSortFilterProxyModel, QThread
11 from glob import glob
12 from zipfile import ZipFile
13 from watchdog.observers import Observer
14 from watchdog.events import FileSystemEventHandler
15 from config import config
16 from renderbox import UnrenderBox
17 import logging, os, json
18
19 class Index(QMainWindow):
20
21 signal_open=pyqtSignal(str)
22 signal_copy=pyqtSignal(dict)
23 index={}
24 proxymodel=QSortFilterProxyModel()
25
26 def __init__(self, *args, **kwargs):
27 super().__init__(*args, **kwargs)
28
29 self.setWindowTitle("MedScript Index")
30 self.setGeometry(200, 200, 600, 400)
31
32 widget=QWidget(self)
33 layout=QVBoxLayout(widget)
34 self.table=QTableView()
35 self.table.setSortingEnabled(True)
36 self.table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
37 self.table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
38 self.table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
39 self.table.doubleClicked.connect(self.cmd_view)
40 layout2=QFormLayout()
41 self.input_pid=QLineEdit()
42 self.input_pid.returnPressed.connect(self.cmd_filter_pid)
43 self.input_id=QLineEdit()
44 self.input_id.returnPressed.connect(self.cmd_filter_id)
45 self.input_name=QLineEdit()
46 self.input_name.returnPressed.connect(self.cmd_filter_name)
47 layout2.addRow("Filter by PID:", self.input_pid)
48 layout2.addRow("Filter by ID:", self.input_id)
49 layout2.addRow("Filter by Name:", self.input_name)
50 layout3=QHBoxLayout()
51 button_view=QPushButton("View Prescription")
52 button_view.clicked.connect(self.cmd_view)
53 button_open=QPushButton("Open Original")
54 button_open.clicked.connect(self.cmd_open)
55 button_copy=QPushButton("Create Copy")
56 button_copy.clicked.connect(self.cmd_copy)
57 button_rebuild=QPushButton("Rebuild Index")
58 button_rebuild.clicked.connect(self.cmd_rebuild)
59 button_browse=QPushButton("File Browser")
60 button_browse.clicked.connect(self.cmd_browse)
61 layout3.addWidget(button_view)
62 layout3.addWidget(button_open)
63 layout3.addWidget(button_copy)
64 layout3.addWidget(button_rebuild)
65 layout3.addWidget(button_browse)
66 layout.addLayout(layout2)
67 layout.addLayout(layout3)
68 layout.addWidget(self.table)
69
70 self.unrenderbox=UnrenderBox()
71
72 self.worker=Worker()
73 self.worker.signal_update.connect(self.update)
74 self.worker.start()
75
76 self.setCentralWidget(widget)
77 self.setWindowIcon(QIcon(os.path.join("resource", "icon_medscript.ico")))
78
79 self.build()
80 self.load()
81
82 def update(self, event_type, src_path):
83 if(src_path.endswith(".mpaz")):
84 if(event_type=="created"):
85 self.add(src_path)
86 elif(event_type=="modified"):
87 self.change(src_path)
88 elif(event_type=="deleted"):
89 self.delete(src_path)
90 self.load()
91
92 def cmd_rebuild(self):
93 self.build()
94 self.load()
95
96 def cmd_filter_pid(self):
97 self.input_id.setText("")
98 self.input_name.setText("")
99 self.proxymodel.setFilterKeyColumn(0)
100 self.proxymodel.setFilterFixedString(self.input_pid.text())
101
102 def cmd_filter_id(self):
103 self.input_pid.setText("")
104 self.input_name.setText("")
105 self.proxymodel.setFilterKeyColumn(1)
106 self.proxymodel.setFilterFixedString(self.input_id.text())
107
108 def cmd_filter_name(self):
109 self.input_pid.setText("")
110 self.input_id.setText("")
111 self.proxymodel.setFilterKeyColumn(2)
112 self.proxymodel.setFilterFixedString(self.input_name.text())
113
114 def cmd_view(self):
115 try:
116 file=self.getSelectedFile()
117 if(file):
118 with ZipFile(file) as zf:
119 with zf.open("prescription.json") as pf:
120 prescription=json.loads(pf.read())
121 self.unrenderbox.show(prescription).exec()
122 except Exception as e:
123 logging.exception(e)
124
125 def cmd_open(self):
126 try:
127 file=self.getSelectedFile()
128 if(file):
129 self.signal_open.emit(file)
130 self.hide()
131 except Exception as e:
132 logging.exception(e)
133
134 def cmd_copy(self):
135 try:
136 file=self.getSelectedFile()
137 if(file):
138 with ZipFile(file) as zf:
139 with zf.open("prescription.json") as pf:
140 pres=json.loads(pf.read())
141 self.signal_copy.emit(pres)
142 self.hide()
143 except Exception as e:
144 logging.exception(e)
145
146 def cmd_browse(self):
147 try:
148 file=QFileDialog.getOpenFileName(self, "Browse", config["document_directory"], "Prescriptions (*.mpaz);; PDF (*.pdf);; All Files (*)")[0]
149 if(file):
150 with ZipFile(file) as zf:
151 with zf.open("prescription.json") as pf:
152 prescription=json.loads(pf.read())
153 self.unrenderbox.show(prescription).exec()
154 except Exception as e:
155 logging.exception(e)
156
157 def getSelectedFile(self):
158 try:
159 selection=self.table.selectedIndexes()
160 file=selection[-1].data()
161 return file
162 except IndexError as e:
163 logging.warning(e)
164 except Exception as e:
165 logging.exception(e)
166
167 def build(self):
168 try:
169 files=glob(os.path.join(config["document_directory"], "**", "*.mpaz"), recursive=True)
170 self.index={}
171 for file in files:
172 self.add(file)
173 except Exception as e:
174 logging.exception(e)
175
176 def add(self, file):
177 try:
178 with ZipFile(file) as zf:
179 try:
180 with zf.open("prescription.json") as pf:
181 pres=json.loads(pf.read())
182 self.index[file]=[pres["pid"], pres["id"], pres["name"], pres["dob"], pres["age"], pres["sex"], pres["date"], pres["diagnosis"], file]
183 except KeyError as e:
184 logging.warning(e)
185 except Exception as e:
186 logging.exception(e)
187 except Exception as e:
188 logging.exception(e)
189
190 def delete(self, file):
191 try:
192 del self.index[file]
193 except KeyError as e:
194 logging.warning(e)
195 except Exception as e:
196 logging.exception(e)
197
198 def change(self, file):
199 self.delete(file)
200 self.add(file)
201
202 def load(self):
203 model=QStandardItemModel()
204 model.setHorizontalHeaderLabels(["Patient ID", "Prescription ID", "Name", "Date of Birth", "Age", "Sex", "Date", "Diagnosis", "File"])
205 for key, item in self.index.items():
206 row=[]
207 for i in item:
208 row.append(QStandardItem(i))
209 model.appendRow(row)
210 self.proxymodel.setSourceModel(model)
211 self.proxymodel.setFilterCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
212 self.table.setModel(self.proxymodel)
213 self.table.resizeColumnsToContents()
214
215 class WatchHandler(FileSystemEventHandler):
216 def __init__(self, signal):
217 super().__init__()
218 self.signal=signal
219 def on_any_event(self, event):
220 if not event.is_directory:
221 self.signal.emit(event.event_type, event.src_path)
222
223 class Worker(QThread):
224 signal_update=pyqtSignal(str, str)
225 def run(self):
226 self.watchHandler=WatchHandler(self.signal_update)
227 self.observer=Observer()
228 self.observer.schedule(self.watchHandler, path=config["document_directory"], recursive=True)
229 self.observer.start()
230 self.observer.join()