# 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.
# You should have received a copy of the GNU General Public License along with MedScript. If not, see <https://www.gnu.org/licenses/>.
-import os, sys, datetime, dateutil.parser, shutil, json, threading
+import logging, os, sys, datetime, dateutil.parser, shutil, json, copy, threading
from PyQt6.QtCore import Qt, QDateTime, QDate, QSize, pyqtSignal
from PyQt6.QtWidgets import QWidget, QMainWindow, QMessageBox, QLabel, QPushButton, QLineEdit, QTextEdit, QDateTimeEdit, QDateEdit, QCalendarWidget, QListWidget, QComboBox, QCheckBox, QRadioButton, QButtonGroup, QVBoxLayout, QHBoxLayout, QFormLayout, QToolBar, QTabWidget, QStatusBar, QFileDialog, QInputDialog, QCompleter, QSizePolicy
from PyQt6.QtGui import QAction, QIcon
from prescription import Prescription, Prescriber
from renderer import Renderer
from filehandler import FileHandler
-from renderbox import RenderBox
+from renderbox import RenderBox, UnrenderBox
from setting import EditConfiguration, EditPrescriber, SelectPrescriber
from editpreset import EditPreset
from viewbox import ViewBox
from preset import Preset
-from tabular import Tabular
from index import Index
from customform import CustomForm
from plugin import Plugin
+from installer import Installer
class MainWindow(QMainWindow):
self.prescription.read_from(os.path.join(self.current_file.directory.name,"prescription.json"))
self.plugin.open(self.prescription)
self.load_interface_from_instance()
-
+ self.update_instance()
self.save_state=md5(self.prescription.get_json().encode()).hexdigest()
self.load_attachment(self.current_file.list())
self.unchanged_state=True
+ except FileNotFoundError as e:
+ logging.warning(e)
except Exception as e:
QMessageBox.warning(self,"Open failed", "Failed to open file.")
- print(e)
+ logging.exception(e)
def cmd_copy(self, data):
self.cmd_new()
- self.prescription.name=data["name"]
- self.prescription.age=data["age"]
- self.prescription.sex=data["sex"]
- self.prescription.address=data["address"]
- self.prescription.contact=data["contact"]
+ self.prescription.set_data_from_json(data)
+ self.prescription.id=""
+ self.prescription.date=None
self.load_interface_from_instance()
+ self.refresh()
def cmd_save(self, save_as=False):
self.update_instance()
try:
if not os.path.exists(self.current_file.file):
filename=QFileDialog.getSaveFileName(self, "Save File", suggest, "Prescriptions (*.mpaz);; All Files (*)")[0]
+ if(len(filename)<=0):
+ return
if(not filename.endswith(".mpaz")):
filename=filename+".mpaz"
self.current_file.set_file(filename)
for i in range(self.input_attachment.count()):
self.current_file.copy(self.input_attachment.item(i).text())
+ if(self.prescription.prescriber.get_json()!=self.prescriber.get_json()):
+ if(QMessageBox.StandardButton.Yes==QMessageBox.question(self,"Change Prescriber", "Original Prescriber: "+self.prescription.prescriber.name+"\nCurrent Prescriber: "+self.prescriber.name+"\nReplace original with current?")):
+ self.prescription.prescriber=copy.deepcopy(self.prescriber)
self.prescription.write_to(os.path.join(self.current_file.directory.name, "prescription.json"))
if change_template:
config["template"]=os.path.join(config["template_directory"], template)
self.save_state=md5(self.prescription.get_json().encode()).hexdigest()
except Exception as e:
QMessageBox.warning(self,"Save failed", "Failed to save file.")
- print(e)
+ logging.exception(e)
def cmd_save_as(self):
suggest=self.prescription.id if(self.prescription.id) else self.prescription.name
suggest=os.path.abspath(os.path.join(config["document_directory"], suggest)+".mpaz")
- self.current_file.set_file(QFileDialog.getSaveFileName(self, "Save File", suggest, "Prescriptions (*.mpaz);; All Files (*)")[0])
- Path(self.current_file.file).touch()
- self.cmd_save(save_as=True)
+ filename=QFileDialog.getSaveFileName(self, "Save File", suggest, "Prescriptions (*.mpaz);; All Files (*)")[0]
+ if(len(filename)>0):
+ if not filename.endswith(".mpaz"):
+ filename=filename+".mpaz"
+ self.current_file.set_file(filename)
+ Path(self.current_file.file).touch()
+ self.cmd_save(save_as=True)
def cmd_refresh(self):
- self.update_instance()
- self.plugin.refresh(self.prescription)
- self.load_interface_from_instance()
self.refresh()
def cmd_quit(self):
if(self.confirm_close()):
sys.exit()
+ def cmd_unrender(self):
+ self.update_instance()
+ self.unrenderbox.show(self.prescription).exec()
+
def cmd_render(self):
- self.refresh()
+ self.update_instance()
if(self.save_state==md5(self.prescription.get_json().encode()).hexdigest()):
try:
target=self.renderer.render(self.current_file.directory.name)
- self.signal_view.emit(target)
- self.renderbox.showMaximized()
+ if target is not None:
+ self.signal_view.emit(target)
+ self.renderbox.showMaximized()
+ else:
+ QMessageBox.critical(self, "Render failed", "Presciption rendering failed. Please check if prescription file or template is corrupted.")
+ logging.error("Prescription rendering failed.")
except FileNotFoundError as e:
- print(e)
+ logging.warning(e)
QMessageBox.information(self, "Save first", "Please save the file before rendering.")
-
else:
QMessageBox.information(self, "Save first", "Please save the file before rendering.")
def cmd_sign(self):
- self.refresh()
+ self.update_instance()
if(self.save_state==md5(self.prescription.get_json().encode()).hexdigest()):
ok=True #password, ok=QInputDialog.getText(self, "Enter password", "Private key password", QLineEdit.EchoMode.Password)
if(ok):
#self.current_file.sign(password)
self.cmd_save()
except FileNotFoundError as e:
- print(e)
+ logging.warning(e)
QMessageBox.information(self, "Save first", "Please save the file before signing.")
except TypeError as e:
- print(e)
+ logging.warning(e)
QMessageBox.information(self, "Configure", "Please add valid key and certificate to the config file.")
except EVPError as e:
- print(e)
+ logging.warning(e)
QMessageBox.information(self, "Check password", "Failed to load key. Please check if password is correct.")
except BIOError as e:
- print(e)
+ logging.warning(e)
QMessageBox.information(self, "Not found", "Certifcate and/or key not found.")
except SMIME_Error as e:
- print(e)
+ logging.warning(e)
QMessageBox.information(self, "Failed to load", "Failed to sign. Please check if certificate and key match.")
except Exception as e:
- print(e)
+ logging.exception(e)
QMessageBox.information(self, "Failed", "Failed to sign.")
except Exception as e:
- print(e)
+ logging.exception(e)
else:
QMessageBox.information(self, "Save first", "Please save the file before signing.")
elif result is None:
QMessageBox.warning(self, "No Siganture", "No signature was found.")
else:
- print(result)
+ logging.info(result)
QMessageBox.information(self, "Valid signature", "Valid signature found with the following information:\n"+result)
except FileNotFoundError as e:
- print(e)
+ logging.warning(e)
QMessageBox.warning(self, "No Siganture", "No signature was found.")
except Exception as e:
- print(e)
+ logging.exception(e)
QMessageBox.warning(self, "Failed", "Failed to verify.")
- def cmd_tabular(self):
- try:
- filename=QFileDialog.getSaveFileName(self, "Export CSV File", os.path.join(config["data_directory"], "data.csv"), "CSV (*.csv);; All Files (*)")[0]
- Tabular.export(filename)
- QMessageBox.information(self, "Data Exported", "Data exported to."+filename)
- except Exception as e:
- print(e)
- QMessageBox.critical(self, "Export failed", "Failed to export the data.")
-
def cmd_index(self):
- self.index.refresh()
self.index.show()
def cmd_configuration(self):
- self.edit_configuration.exec()
-
- def cmd_prescriber(self, file=None):
- self.edit_prescriber.load(file)
- self.edit_prescriber.exec()
+ self.editConfiguration.exec()
- def cmd_switch(self):
+ def cmd_prescriber(self):
try:
- self.select_prescriber.load()
- self.select_prescriber.exec()
+ self.selectPrescriber.load()
+ self.selectPrescriber.exec()
except FileNotFoundError as e:
- print(e)
+ logging.warning(e)
def cmd_preset(self):
- self.edit_preset.show()
+ self.editPreset.show()
+
+ def cmd_installer(self):
+ self.installer.show()
def cmd_about(self):
year=datetime.datetime.now().year
def cmd_update(self, silent=False):
try:
- print("Current version "+info["version"])
+ logging.info("Current version "+info["version"])
with request.urlopen(info["url"]+"/info.json") as response:
latest=json.loads(response.read().decode())
- print("Latest version "+latest["version"])
+ logging.info("Latest version "+latest["version"])
if(version.parse(info["version"]) < version.parse(latest["version"])):
self.signal_update.emit("New version <strong>"+latest["version"]+"</strong> available.<br>Visit <a href='"+latest["url"]+"'>"+latest["url"]+"</a> to get the latest version.")
elif(not silent):
self.signal_update.emit("No update available. You are using version "+info["version"]+".")
except Exception as e:
self.signal_update.emit("Failed to check available update.")
- print(e)
+ logging.warning(e)
def show_update(self, message):
QMessageBox.information(self, "Check update", message)
if config["preset_newline"]:
self.input_certificate.insertPlainText("\n")
- def load_interface(self, file="", date=None, id="", name="", dob="", age="", sex="", address="", contact="", extra="", mode="", daw="", diagnosis="", note="", report="", advice="", investigation="", medication="", additional="", certificate="", custom=None):
+ def load_interface(self, file="", date=None, id="", pid="", name="", dob="", age="", sex="", address="", contact="", extra="", mode="", daw="", diagnosis="", note="", report="", advice="", investigation="", medication="", additional="", certificate="", custom=None):
try:
file_msg=self.current_file.file if self.current_file.file else "New file"
sign_msg="(signed)" if config["smime"] and self.current_file.is_signed() else ""
d=QDateTime.fromString(pdate.strftime("%Y-%m-%d %H:%M:%S"), "yyyy-MM-dd hh:mm:ss")
except Exception as e:
QMessageBox.warning(self,"Failed to load", str(e))
- print(e)
+ logging.exception(e)
self.input_date.setDateTime(d)
self.input_id.setText(id)
+ self.input_pid.setText(pid)
self.input_name.setText(name)
try:
pdate=dateutil.parser.parse(dob)
self.input_medication.setText(medication)
self.input_additional.setText(additional)
self.input_certificate.setText(certificate)
- self.input_custom.setData(custom)
+ if(config["enable_form"] and custom is not None):
+ self.input_custom.setData(custom)
self.label_prescriber.setText(self.prescriber.name)
except Exception as e:
QMessageBox.warning(self,"Failed to load", "Failed to load the data into the application.")
- print(e)
+ logging.exception(e)
def load_interface_from_instance(self):
if(self.current_file.has_template()):
file=self.prescription.file,
date=self.prescription.date,
id=self.prescription.id,
+ pid=self.prescription.pid,
name=self.prescription.name,
dob=self.prescription.dob,
age=self.prescription.age,
self.prescription.set_data(
date=self.input_date.dateTime().toString("yyyy-MM-dd hh:mm:ss"),
id=self.input_id.text(),
+ pid=self.input_pid.text(),
name=self.input_name.text(),
dob=self.input_dob.text(),
age=self.input_age.text(),
)
except Exception as e:
QMessageBox.critical(self,"Failed", "Critical failure happned. Please check console for more info.")
- print(e)
+ logging.error(e)
def new_doc(self):
self.current_file.reset()
self.update_instance()
self.plugin.new(self.prescription)
self.load_interface_from_instance()
+ if(config["age_default"]):
+ self.btnAge.click()
+ self.update_instance()
self.save_state=md5(self.prescription.get_json().encode()).hexdigest()
def change_prescriber(self, file):
self.prescriber.read_from(file)
self.refresh()
+ def edit_prescriber(self, file=None):
+ self.editPrescriber.load(file)
+ self.editPrescriber.exec()
+
def refresh(self):
self.update_instance()
+ self.plugin.refresh(self.prescription)
self.load_interface_from_instance()
def add_attachment(self):
self.input_attachment.addItem(new)
except Exception as e:
QMessageBox.warning(self,"Attach failed", "Failed to attach file.")
- print(e)
+ logging.exception(e)
def remove_attachment(self):
index=self.input_attachment.currentRow()
try:
shutil.copyfile(self.input_attachment.currentItem().text(), QFileDialog.getSaveFileName(self, "Save Attachment", os.path.join(config["document_directory"], os.path.basename(self.input_attachment.currentItem().text())))[0])
except Exception as e:
- print(e)
+ logging.exception(e)
def load_attachment(self, attachments):
for attach in attachments:
self.input_age.setText("")
self.input_age.setEnabled(False)
+ def load_presets(self):
+ self.preset_note=Preset("note")
+ self.preset_report=Preset("report")
+ self.preset_advice=Preset("advice")
+ self.preset_investigation=Preset("investigation")
+ self.preset_medication=Preset("medication", text_as_key=True)
+ self.preset_additional=Preset("additional")
+ self.preset_certificate=Preset("certificate")
+
+ def reload_presets(self):
+ self.load_presets()
+ self.input_note_preset.addItems(self.preset_note.data.keys())
+ self.input_report_preset.addItems(self.preset_note.data.keys())
+ self.input_advice_preset.addItems(self.preset_note.data.keys())
+ self.input_investigation_preset.addItems(self.preset_note.data.keys())
+ self.input_medication_preset.addItems(self.preset_note.data.keys())
+ self.input_additional_preset.addItems(self.preset_note.data.keys())
+ self.input_certificate_preset.addItems(self.preset_note.data.keys())
def confirm_close(self):
- self.refresh()
+ self.update_instance()
flag=(self.save_state==md5(self.prescription.get_json().encode()).hexdigest() or QMessageBox.StandardButton.Yes==QMessageBox.question(self,"Confirm action", "Unsaved changes may be lost. Continue?"))
return flag
self.setGeometry(100, 100, 600, 400)
self.setWindowIcon(QIcon(os.path.join(config["resource"], "icon_medscript.ico")))
+ icon_index=QIcon(os.path.join(config["resource"], "icon_index.svg"))
icon_open=QIcon(os.path.join(config["resource"], "icon_open.svg"))
icon_save=QIcon(os.path.join(config["resource"], "icon_save.svg"))
icon_render=QIcon(os.path.join(config["resource"], "icon_render.svg"))
icon_refresh=QIcon(os.path.join(config["resource"], "icon_refresh.svg"))
+ icon_view=QIcon(os.path.join(config["resource"], "icon_view.svg"))
- self.preset_note=Preset("note")
- self.preset_report=Preset("report")
- self.preset_advice=Preset("advice")
- self.preset_investigation=Preset("investigation")
- self.preset_medication=Preset("medication", text_as_key=True)
- self.preset_additional=Preset("additional")
- self.preset_certificate=Preset("certificate")
+ self.load_presets()
- action_new=QAction("New", self)
+ action_new=QAction("New File", self)
action_new.setShortcut("Ctrl+N")
action_new.triggered.connect(self.cmd_new)
- action_open=QAction("Open", self)
+ action_open=QAction("Open File", self)
action_open2=QAction(icon_open, "Open", self)
action_open.setShortcut("Ctrl+O")
action_open.triggered.connect(self.cmd_open)
action_open2.triggered.connect(self.cmd_open)
- action_save=QAction("Save", self)
+ action_save=QAction("Save File", self)
action_save2=QAction(icon_save, "Save", self)
action_save.setShortcut("Ctrl+S")
action_save.triggered.connect(self.cmd_save)
action_save_as=QAction("Save As", self)
action_save_as.setShortcut("Ctrl+Shift+S")
action_save_as.triggered.connect(self.cmd_save_as)
- action_refresh=QAction("Refresh", self)
+ action_refresh=QAction("Refresh Interface", self)
action_refresh.setShortcut("F5")
action_refresh2=QAction(icon_refresh, "Refresh", self)
action_refresh.triggered.connect(self.cmd_refresh)
action_refresh2.triggered.connect(self.cmd_refresh)
- action_quit=QAction("Quit", self)
+ action_quit=QAction("Quit MedScript", self)
action_quit.setShortcut("Ctrl+Q")
action_quit.triggered.connect(self.cmd_quit)
- action_render=QAction("Render", self)
+ action_render=QAction("Render Prescription", self)
action_render.setShortcut("Ctrl+R")
action_render2=QAction(icon_render, "Render", self)
action_render.triggered.connect(self.cmd_render)
action_render2.triggered.connect(self.cmd_render)
- action_sign=QAction("Sign", self)
+ action_unrender=QAction("Quick Display", self)
+ action_unrender.setShortcut("Ctrl+D")
+ action_unrender2=QAction(icon_view, "Display", self)
+ action_unrender.triggered.connect(self.cmd_unrender)
+ action_unrender2.triggered.connect(self.cmd_unrender)
+ action_sign=QAction("Sign Prescription", self)
action_sign.triggered.connect(self.cmd_sign)
- action_unsign=QAction("Unsign", self)
+ action_unsign=QAction("Delete Signature", self)
action_unsign.triggered.connect(self.cmd_unsign)
- action_verify=QAction("Verify", self)
+ action_verify=QAction("Verify Signature", self)
action_verify.triggered.connect(self.cmd_verify)
- action_configuration=QAction("Configuration", self)
+ action_configuration=QAction("Edit Configuration", self)
action_configuration.triggered.connect(self.cmd_configuration)
- action_prescriber=QAction("Prescriber", self)
- action_prescriber.triggered.connect(self.cmd_prescriber)
- action_switch=QAction("Switch", self)
- action_switch.triggered.connect(self.cmd_switch)
- action_preset=QAction("Preset", self)
+ action_preset=QAction("Edit Presets", self)
action_preset.triggered.connect(self.cmd_preset)
- action_tabular=QAction("Tabular", self)
- action_tabular.triggered.connect(self.cmd_tabular)
- action_index=QAction("Index", self)
+ action_prescriber=QAction("Select Prescriber", self)
+ action_prescriber.triggered.connect(self.cmd_prescriber)
+ action_installer=QAction("Package Installer", self)
+ action_installer.triggered.connect(self.cmd_installer)
+ action_index=QAction("Show Index", self)
action_index.triggered.connect(self.cmd_index)
- action_update=QAction("Update", self)
+ action_index.setShortcut("Ctrl+I")
+ action_index2=QAction(icon_index, "Index", self)
+ action_index2.triggered.connect(self.cmd_index)
+ action_update=QAction("Check Update", self)
action_update.triggered.connect(self.cmd_update)
- action_about=QAction("About", self)
+ action_about=QAction("About MedScript", self)
action_about.triggered.connect(self.cmd_about)
- action_help=QAction("Help", self)
+ action_help=QAction("Show Help", self)
action_help.setShortcut("F1")
action_help.triggered.connect(self.cmd_help)
menubar=self.menuBar()
menu_file=menubar.addMenu("File")
+ menu_file.addAction(action_index)
menu_file.addAction(action_new)
menu_file.addAction(action_open)
menu_file.addAction(action_save)
menu_file.addAction(action_save_as)
menu_file.addAction(action_quit)
- menu_prepare=menubar.addMenu("Prepare")
+ menu_prepare=menubar.addMenu("Process")
+ menu_prepare.addAction(action_unrender)
menu_prepare.addAction(action_render)
menu_prepare.addAction(action_refresh)
if(config["smime"]):
menu_prepare.addAction(action_verify)
menu_settings=menubar.addMenu("Settings")
menu_settings.addAction(action_configuration)
- menu_settings.addAction(action_prescriber)
- menu_settings.addAction(action_switch)
menu_settings.addAction(action_preset)
- menu_data=menubar.addMenu("Data")
- menu_data.addAction(action_index)
- menu_data.addAction(action_tabular)
+ menu_settings.addAction(action_prescriber)
+ menu_settings.addAction(action_installer)
if(config["enable_plugin"]):
action_plugin=[]
action_plugin[-1].triggered.connect(partial(self.plugin.run, i[0], self.prescription))
action_plugin[-1].triggered.connect(self.load_interface_from_instance)
except Exception as e:
- print(e)
+ logging.exception(e)
menu_plugin=menubar.addMenu("Plugin")
for i in action_plugin:
menu_plugin.addAction(i)
toolbar=QToolBar("Main Toolbar", floatable=False, movable=False)
toolbar.setIconSize(QSize(16, 16))
+ toolbar.addAction(action_index2)
toolbar.addAction(action_open2)
toolbar.addAction(action_save2)
toolbar.addAction(action_refresh2)
+ toolbar.addAction(action_unrender2)
toolbar.addAction(action_render2)
toolbar.addSeparator()
label_template=QLabel("Template:")
templates.remove(os.path.basename(config["template"]))
templates.insert(0, os.path.basename(config["template"]))
except Exception as e:
- print(e)
+ logging.exception(e)
self.input_template.addItems(templates)
toolbar.addWidget(self.input_template)
spacer=QWidget(self)
self.input_date.setCalendarWidget(QCalendarWidget())
layout_info.addRow("Date", self.input_date)
self.input_id=QLineEdit(self)
- layout_info.addRow("ID", self.input_id)
+ layout_info.addRow("Prescription ID", self.input_id)
+ self.input_pid=QLineEdit(self)
+ layout_info.addRow("Patient ID", self.input_pid)
self.input_name=QLineEdit(self)
layout_info.addRow("Name", self.input_name)
tab.addTab(tab_certificate, "Certificate")
if(config["enable_form"]):
tab.addTab(tab_custom, "Custom")
+ else:
+ tab_custom.hide()
tab.addTab(tab_attachment, "Attachment")
self.setCentralWidget(tab)
self.setStatusBar(self.statusbar)
self.renderbox=RenderBox()
+ self.unrenderbox=UnrenderBox()
self.signal_view.connect(self.renderbox.update)
- self.edit_configuration=EditConfiguration()
- self.edit_prescriber=EditPrescriber()
- self.edit_prescriber.signal_save.connect(self.change_prescriber)
- self.select_prescriber=SelectPrescriber()
- self.select_prescriber.signal_edit.connect(self.cmd_prescriber)
- self.select_prescriber.signal_select.connect(self.change_prescriber)
+ self.editConfiguration=EditConfiguration()
+ self.editPrescriber=EditPrescriber()
+ self.editPrescriber.signal_save.connect(self.change_prescriber)
+ self.selectPrescriber=SelectPrescriber()
+ self.selectPrescriber.signal_edit.connect(self.edit_prescriber)
+ self.selectPrescriber.signal_select.connect(self.change_prescriber)
self.viewbox=ViewBox()
self.index=Index()
- self.edit_preset=EditPreset()
+ self.editPreset=EditPreset()
+ self.editPreset.presetEdited.connect(self.reload_presets)
+ self.installer=Installer()
self.index.signal_open.connect(self.cmd_open)
self.index.signal_copy.connect(self.cmd_copy)
+ self.plugin.update.connect(lambda: self.load_interface_from_instance())
self.signal_update.connect(self.show_update)
self.new_doc()
self.cmd_open(config["filename"])
if(len(self.prescription.prescriber.name.strip())<1):
- self.cmd_prescriber()
+ self.edit_prescriber()
if(config["check_update"]):
threading.Thread(target=self.cmd_update, args=[True]).start()
+
self.setWindowIcon(QIcon(os.path.join(config["resource"], "icon_medscript.ico")))
self.showMaximized()