From 0fa7a6181aa8dcf7840c9b4b31199d2e427ab997 Mon Sep 17 00:00:00 2001 From: Agnibho Mondal Date: Thu, 26 Oct 2023 22:17:04 +0530 Subject: [PATCH] Implemented the plugin system --- README | 23 ++++++++++++- config.py | 12 ++++--- data/config.json | 1 + plugin.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ setting.py | 4 +++ window.py | 24 +++++++++++++ 6 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 plugin.py diff --git a/README b/README index b879ea8..bc8754b 100644 --- a/README +++ b/README @@ -214,10 +214,31 @@ here. 5. Markdown: The markdown formatting can be enabled from here. -6. S/MIME: This is an experimental feature and is disabled by default. To use +6. Check update: Whether updates are available can be checked at program start +up by enabling this option. + +7. Plugin: The plugin system can be enabled from here. + +8. S/MIME: This is an experimental feature and is disabled by default. To use it, it has to be enabled first from the settings. The Private key, X509 certificate and Root bundle can be selected from the options that follow this. +Plugin +------ + +MedScript has a plugin system which can be used to incorporate customized +plugins written in python. To use the plugins, the plugins must be saved in +the configured plugin directory and plugin must be enabled in the +configuration. + +The details of the plugin system and the documentation for development is +available in the project website. + +Website +------- + + + License ------- diff --git a/config.py b/config.py index 4b43fd8..f97cf81 100644 --- a/config.py +++ b/config.py @@ -33,11 +33,13 @@ default = { "template_directory": "template", "template": "default_prescription", "preset_directory": "preset", - "preset_newline": "True", + "plugin_directory": "plugin", + "enable_plugin": False, + "preset_newline": True, "preset_delimiter": ",", - "markdown": "False", - "check_update": "False", - "smime": "False", + "markdown": False, + "check_update": False, + "smime": False, "root_bundle": "", "private_key": "", "certificate": "" @@ -55,6 +57,7 @@ config["filename"]=args.filename config["data_directory"]=os.path.abspath(os.path.join(real_dir, os.path.expanduser(config["data_directory"]))) config["document_directory"]=os.path.join(config["data_directory"], config["document_directory"]) config["preset_directory"]=os.path.join(config["data_directory"], config["preset_directory"]) +config["plugin_directory"]=os.path.join(config["data_directory"], config["plugin_directory"]) config["template_directory"]=os.path.join(config["data_directory"], config["template_directory"]) config["template"]=os.path.join(config["template_directory"], config["template"]) config["resource"]=os.path.abspath(os.path.join(real_dir, "resource")) @@ -75,6 +78,7 @@ os.makedirs(config["data_directory"], exist_ok=True) os.makedirs(config["document_directory"], exist_ok=True) os.makedirs(config["prescriber_directory"], exist_ok=True) os.makedirs(config["preset_directory"], exist_ok=True) +os.makedirs(config["plugin_directory"], exist_ok=True) os.makedirs(config["template_directory"], exist_ok=True) if not os.path.exists(os.path.join(config["data_directory"], "config.json")): shutil.copyfile(os.path.abspath(os.path.join(real_dir, "data", "config.json")), os.path.join(config["data_directory"], "config.json")) diff --git a/data/config.json b/data/config.json index c931c38..d96bc87 100644 --- a/data/config.json +++ b/data/config.json @@ -10,6 +10,7 @@ "preset_delimiter": ",", "markdown": false, "check_update": false, + "enable_plugin": false, "smime": false, "root_bundle": "", "certificate": "", diff --git a/plugin.py b/plugin.py new file mode 100644 index 0000000..3b86520 --- /dev/null +++ b/plugin.py @@ -0,0 +1,88 @@ +# MedScript +# Copyright (C) 2023 Dr. Agnibho Mondal +# This file is part of MedScript. +# 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. +# 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 . + +import os, importlib +from PyQt6.QtWidgets import QMessageBox +from glob import glob +from config import config + +class Plugin(): + + plugins=[] + names=[] + + def __init__(self): + if(config["enable_plugin"]): + #if(not config["enable_plugin"]): + self.load() + + def load(self): + plugin_list=glob(os.path.join(config["plugin_directory"], "*")) + for i in plugin_list: + try: + spec=importlib.util.spec_from_file_location(os.path.basename(i), os.path.join(i, "main.py")) + mod=importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + self.plugins.append(mod) + except Exception as e: + print(i, ":", e) + + def get_name(self, mod): + try: + return(mod.name) + except Exception as e: + return(mod.__name__) + + def commands(self): + cmds=[] + for i in self.plugins: + cmds.append([i, self.get_name(i)]) + return(cmds) + + def new(self, prescription): + for i in self.plugins: + try: + msg=i.new(prescription) + if(msg): + QMessageBox.information(None, "Information", msg) + except Exception as e: + print(e) + + def open(self, prescription): + for i in self.plugins: + try: + msg=i.open(prescription) + if(msg): + QMessageBox.information(None, "Information", msg) + except Exception as e: + print(e) + + def save(self, prescription): + for i in self.plugins: + try: + msg=i.save(prescription) + if(msg): + QMessageBox.information(None, "Information", msg) + except Exception as e: + print(e) + + def refresh(self, prescription): + for i in self.plugins: + try: + msg=i.refresh(prescription) + if(msg): + QMessageBox.information(None, "Information", msg) + except Exception as e: + print(e) + + def run(self, module, prescription): + try: + msg=module.run(prescription) + if(msg): + QMessageBox.information(None, "Information", msg) + except Exception as e: + print(e) diff --git a/setting.py b/setting.py index a4a68fa..c05cd57 100644 --- a/setting.py +++ b/setting.py @@ -43,6 +43,7 @@ class EditConfiguration(QMainWindow): self.input_delimiter.setCurrentText(self.config["preset_delimiter"]) self.input_markdown.setChecked(bool(self.config["markdown"])) self.input_update.setChecked(bool(self.config["check_update"])) + self.input_plugin.setChecked(bool(self.config["enable_plugin"])) self.input_smime.setChecked(bool(self.config["smime"])) self.input_key.setText(self.config["private_key"]) self.input_certificate.setText(self.config["certificate"]) @@ -60,6 +61,7 @@ class EditConfiguration(QMainWindow): self.config["preset_delimiter"]=self.input_delimiter.currentText() self.config["markdown"]=self.input_markdown.isChecked() self.config["check_update"]=self.input_update.isChecked() + self.config["enable_plugin"]=self.input_plugin.isChecked() self.config["smime"]=self.input_smime.isChecked() self.config["private_key"]=self.input_key.text() self.config["certificate"]=self.input_certificate.text() @@ -110,6 +112,8 @@ class EditConfiguration(QMainWindow): layout.addRow("Markdown", self.input_markdown) self.input_update=QCheckBox("Check update on startup", self) layout.addRow("Check Update", self.input_update) + self.input_plugin=QCheckBox("Enable plugin", self) + layout.addRow("Plugin", self.input_plugin) self.input_smime=QCheckBox("Enable digital signature (experimental)", self) layout.addRow("S/MIME", self.input_smime) self.input_key=QLineEdit(self) diff --git a/window.py b/window.py index 0066531..0b0924c 100644 --- a/window.py +++ b/window.py @@ -13,6 +13,7 @@ from pathlib import Path from hashlib import md5 from urllib import request from packaging import version +from functools import partial from config import config, info, real_dir from prescription import Prescription @@ -24,6 +25,7 @@ from viewbox import ViewBox from preset import Preset from tabular import Tabular from index import Index +from plugin import Plugin class MainWindow(QMainWindow): @@ -33,6 +35,7 @@ class MainWindow(QMainWindow): current_file=FileHandler() prescription=Prescription() renderer=Renderer() + plugin=Plugin() save_state=md5("".encode()).hexdigest() unchanged_state=False @@ -50,6 +53,7 @@ class MainWindow(QMainWindow): self.current_file.set_file(QFileDialog.getOpenFileName(self, "Open File", config["document_directory"], "Prescriptions (*.mpaz);; All Files (*)")[0]) self.current_file.open() 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.save_state=md5(self.prescription.get_json().encode()).hexdigest() self.load_attachment(self.current_file.list()) @@ -69,6 +73,8 @@ class MainWindow(QMainWindow): def cmd_save(self, save_as=False): self.update_instance() + self.plugin.save(self.prescription) + self.load_interface_from_instance() suggest=self.prescription.id if(self.prescription.id) else self.prescription.name suggest=os.path.abspath(os.path.join(config["document_directory"], suggest)+".mpaz") if(save_as or not self.unchanged_state or QMessageBox.StandardButton.Yes==QMessageBox.question(self,"Confirm change", "Modify the original file?")): @@ -96,6 +102,8 @@ class MainWindow(QMainWindow): self.cmd_save(save_as=True) def cmd_refresh(self): + self.plugin.refresh(self.prescription) + self.load_interface_from_instance() self.refresh() def cmd_quit(self): @@ -394,6 +402,8 @@ class MainWindow(QMainWindow): self.input_attachment.clear() self.load_interface() self.update_instance() + self.plugin.new(self.prescription) + self.load_interface_from_instance() self.save_state=md5(self.prescription.get_json().encode()).hexdigest() def refresh(self): @@ -532,6 +542,20 @@ class MainWindow(QMainWindow): menu_data=menubar.addMenu("Data") menu_data.addAction(action_index) menu_data.addAction(action_tabular) + + if(config["enable_plugin"]): + action_plugin=[] + try: + for i in self.plugin.commands(): + action_plugin.append(QAction(i[1], self)) + 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) + menu_plugin=menubar.addMenu("Plugin") + for i in action_plugin: + menu_plugin.addAction(i) + menu_help=menubar.addMenu("Help") menu_help.addAction(action_update) menu_help.addAction(action_about) -- 2.39.5