From 34396b13ddeb228b0b595ce665c2f698373282a3 Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Fri, 24 Dec 2021 18:55:35 +0300 Subject: [PATCH] draft 1.2.7 (need test) # Create Managers.ControlPanel class - manage control panels # Orchestrator.WeCPUpdate updated for the new Managers.ControlPanel # BackwardCompatibility is released # Need test for the New usage of Managers.ControlPanel # Need to create DataProcess # Need to check verification if data is equal - do not send the response (check for None in HTML and JSON) --- .../Orchestrator/BackwardCompatibility.py | 20 +- .../Orchestrator/Managers/ControlPanel.py | 202 ++++++++++++++++++ .../Orchestrator/Managers/__init__.py | 1 + .../pyOpenRPA/Orchestrator/ServerSettings.py | 73 +------ .../Orchestrator/SettingsTemplate.py | 5 + .../Orchestrator/__Orchestrator__.py | 15 +- 6 files changed, 240 insertions(+), 76 deletions(-) create mode 100644 Sources/pyOpenRPA/Orchestrator/Managers/ControlPanel.py create mode 100644 Sources/pyOpenRPA/Orchestrator/Managers/__init__.py diff --git a/Sources/pyOpenRPA/Orchestrator/BackwardCompatibility.py b/Sources/pyOpenRPA/Orchestrator/BackwardCompatibility.py index 727376ee..ee2b5d9a 100644 --- a/Sources/pyOpenRPA/Orchestrator/BackwardCompatibility.py +++ b/Sources/pyOpenRPA/Orchestrator/BackwardCompatibility.py @@ -488,4 +488,22 @@ def Update(inGSettings): if "AgentLimitLogSizeBytesInt" not in inGSettings["ServerDict"]: inGSettings["ServerDict"]["AgentLimitLogSizeBytesInt"] = 300 if lL: lL.warning( - f"Backward compatibility (v1.2.3 to v1.2.4): Add new key ServerDict > AgentLimitLogSizeBytesInt") # Log about compatibility \ No newline at end of file + f"Backward compatibility (v1.2.3 to v1.2.4): Add new key ServerDict > AgentLimitLogSizeBytesInt") # Log about compatibility + # Remove ControlPanelDict and CPDict > go to ServerDict > ControlPanelDict + if "ControlPanelDict" in inGSettings: + del inGSettings["ControlPanelDict"] + if lL: lL.warning( + f"Backward compatibility (v1.2.4 to v1.2.7): Remove old key: ControlPanelDict") # Log about compatibility + if "CPDict" in inGSettings: + for lCPKeyStr in inGSettings["CPDict"]: + lCPItemDict = inGSettings["CPDict"][lCPKeyStr] + __Orchestrator__.WebCPUpdate(inCPKeyStr=lCPKeyStr,inHTMLRenderDef=lCPItemDict["HTMLRenderDef"], + inJSONGeneratorDef=lCPItemDict["JSONGeneratorDef"], + inJSInitGeneratorDef=lCPItemDict["JSInitGeneratorDef"]) + del inGSettings["CPDict"] + if lL: lL.warning( + f"Backward compatibility (v1.2.4 to v1.2.7): Remove old key: CPDict") # Log about compatibility + if "ControlPanelDict" not in inGSettings["ServerDict"]: + inGSettings["ServerDict"]["ControlPanelDict"]={} + if lL: lL.warning( + f"Backward compatibility (v1.2.4 to v1.2.7): Create new key: ServerDict > ControlPanelDict") # Log about compatibility \ No newline at end of file diff --git a/Sources/pyOpenRPA/Orchestrator/Managers/ControlPanel.py b/Sources/pyOpenRPA/Orchestrator/Managers/ControlPanel.py new file mode 100644 index 00000000..3529310c --- /dev/null +++ b/Sources/pyOpenRPA/Orchestrator/Managers/ControlPanel.py @@ -0,0 +1,202 @@ +from ... import Orchestrator +import jinja2 +import os +from inspect import signature # For detect count of def args +from ..Web import Basic + +class ControlPanel(): + """ + Manage your control panel on the orchestrator + """ + mControlPanelNameStr = None + mJinja2TemplatePathStr = None + mJinja2Loader = None + mJinja2Env = None + mJinja2Template = None + + mBackwardCompatibilityHTMLDef = None + mBackwardCompatibilityJSDef = None + mBackwardCompatibilityJSONDef = None + + def __init__(self, inControlPanelNameStr, inJinja2TemplatePathStr = None): + """ + Constructor of the control panel manager + + :param inControlPanelNameStr: + :param inJinja2TemplatePathStr: + """ + # Connect self witch pyOpenRPA via ControlPanelNameStr + if inControlPanelNameStr in Orchestrator.GSettingsGet()["ServerDict"]["ControlPanelDict"]: + raise Exception(f"Another control panel with name {inControlPanelNameStr} is already exists. Please resolve the error and restart") + Orchestrator.GSettingsGet()["ServerDict"]["ControlPanelDict"][inControlPanelNameStr] = self + self.Jinja2TemplatePathSet(inJinja2TemplatePathStr = inJinja2TemplatePathStr) + self.mControlPanelNameStr = inControlPanelNameStr # Set the name of the control panel + + def Jinja2TemplatePathSet(self, inJinja2TemplatePathStr): + """ + Create Jinja2 env and load the template html + + :param inJinja2TemplatePathStr: + :return: + """ + if inJinja2TemplatePathStr is not None: + lSystemLoaderPathStr = "/".join(inJinja2TemplatePathStr.split("\\")[0:-1]) + lTemplateFileNameStr = inJinja2TemplatePathStr.split("\\")[-1] + self.mJinja2Loader = jinja2.FileSystemLoader(lSystemLoaderPathStr) + self.mJinja2Env = jinja2.Environment(loader=self.mJinja2Loader, trim_blocks=True) + self.mJinja2Template = self.mJinja2Env.get_template(lTemplateFileNameStr) + + def Jinja2StrGenerate(self, inDataDict): + """ + Generate the HTML str from the Jinja2. Pass the context inDataDict + :param inDataDict: + :return: + """ + lHTMLStr = self.mJinja2Template.render(**inDataDict) # Render the template into str + return lHTMLStr + + def DataDictGenerate(self, inRequest): + """ + + :return: + """ + lData = {} + return lData + + def OnRefreshHTMLStr(self, inRequest): + """ + Event to generate HTML code of the control panel when refresh time is over. + Support backward compatibility for previous versions. + :param inRequest: + :return: + """ + lHTMLStr = None + lL = Orchestrator.OrchestratorLoggerGet() + if self.mBackwardCompatibilityHTMLDef is None: + if self.mJinja2Template is not None: + lDataDict = self.OnRefreshHTMLDataDict(inRequest = inRequest) + # Jinja code + lHTMLStr = self.Jinja2StrGenerate(inDataDict=lDataDict) + else: + lHTMLStr = self.BackwardAdapterHTMLDef(inRequest=inRequest) + # return the str + return lHTMLStr + + def OnRefreshHTMLDataDict(self, inRequest): + """ + Event to prepare data context for the futher Jinja2 HTML generation. You can override this def if you want some thing more data + :return: dict + """ + return self.DataDictGenerate(inRequest=inRequest) + + def OnRefreshJSONDict(self, inRequest): + """ + Event to transmit some data from server side to the client side in JSON format. Call when page refresh is initialized + + :return: Dict type + """ + lResultDict = None + if self.mBackwardCompatibilityJSONDef is None: + pass + else: + lResultDict = self.BackwardAdapterJSONDef(inRequest=inRequest) + return lResultDict + + def OnInitJSStr(self, inRequest): + """ + Event when orchestrator web page is init on the client side - you can transmit some java script code is str type to execute it once. + + :return: "" + """ + lJSStr = "" + if self.mBackwardCompatibilityJSDef is None: + pass + else: + lJSStr = self.BackwardAdapterJSDef(inRequest=inRequest) + return lJSStr + + def BackwardAdapterHTMLDef(self,inRequest): + lGS = Orchestrator.GSettingsGet() + lL = Orchestrator.OrchestratorLoggerGet() + # HTMLRenderDef + lItemHTMLRenderDef = self.mBackwardCompatibilityHTMLDef + lResultStr = "" + if lItemHTMLRenderDef is not None: # Call def (inRequest, inGSettings) or def (inGSettings) + lHTMLResult = None + lDEFSignature = signature(lItemHTMLRenderDef) # Get signature of the def + lDEFARGLen = len(lDEFSignature.parameters.keys()) # get count of the def args + try: + if lDEFARGLen == 1: # def (inGSettings) + lHTMLResult = lItemHTMLRenderDef(lGS) + elif lDEFARGLen == 2: # def (inRequest, inGSettings) + lHTMLResult = lItemHTMLRenderDef(inRequest, lGS) + elif lDEFARGLen == 0: # def () + lHTMLResult = lItemHTMLRenderDef() + # RunFunction + # Backward compatibility up to 1.2.0 - call HTML generator if result has no "HTMLStr" + if type(lHTMLResult) is str: + lResultStr = lHTMLResult + elif "HTMLStr" in lHTMLResult or "JSONDict" in lHTMLResult: + lResultStr = lHTMLResult["HTMLStr"] + else: + # Call backward compatibility HTML generator + lResultStr = Basic.HTMLControlPanelBC(inCPDict=lHTMLResult) + except Exception as e: + if lL: lL.exception(f"Error in control panel HTMLRenderDef. CP Key {self.mControlPanelNameStr}. Exception are below") + return lResultStr + + + def BackwardAdapterJSONDef(self,inRequest): + lGS = Orchestrator.GSettingsGet() + lL = Orchestrator.OrchestratorLoggerGet() + # HTMLRenderDef + lItemJSONGeneratorDef = self.mBackwardCompatibilityJSONDef + lResultDict = {} + if lItemJSONGeneratorDef is not None: # Call def (inRequest, inGSettings) or def (inGSettings) + lJSONResult = None + lDEFSignature = signature(lItemJSONGeneratorDef) # Get signature of the def + lDEFARGLen = len(lDEFSignature.parameters.keys()) # get count of the def args + try: + if lDEFARGLen == 1: # def (inGSettings) + lJSONResult = lItemJSONGeneratorDef(lGS) + elif lDEFARGLen == 2: # def (inRequest, inGSettings) + lJSONResult = lItemJSONGeneratorDef(inRequest, lGS) + elif lDEFARGLen == 0: # def () + lJSONResult = lItemJSONGeneratorDef() + # RunFunction + # Backward compatibility up to 1.2.0 - call HTML generator if result has no "HTMLStr" + lType = type(lJSONResult) + if lType is str or lJSONResult is None or lType is int or lType is list or lType is dict or lType is bool or lType is float: + lResultDict = lJSONResult + else: + if lL: lL.warning(f"JSONGenerator return bad type: {str(type(lJSONResult))}, CP Key {self.mControlPanelNameStr}") + except Exception as e: + if lL: lL.exception( + f"Error in control panel JSONGeneratorDef. CP Key {self.mControlPanelNameStr}. Exception are below") + return lResultDict + + def BackwardAdapterJSDef(self,inRequest): + lGS = Orchestrator.GSettingsGet() + lL = Orchestrator.OrchestratorLoggerGet() + # HTMLRenderDef + lJSInitGeneratorDef = self.mBackwardCompatibilityJSDef + lResultStr = "" + if lJSInitGeneratorDef is not None: # Call def (inRequest, inGSettings) or def (inGSettings) + lJSResult = "" + lDEFSignature = signature(lJSInitGeneratorDef) # Get signature of the def + lDEFARGLen = len(lDEFSignature.parameters.keys()) # get count of the def args + try: + if lDEFARGLen == 1: # def (inGSettings) + lJSResult = lJSInitGeneratorDef(lGS) + elif lDEFARGLen == 2: # def (inRequest, inGSettings) + lJSResult = lJSInitGeneratorDef(inRequest, lGS) + elif lDEFARGLen == 0: # def () + lJSResult = lJSInitGeneratorDef() + if type(lJSResult) is str: + lResultStr = lJSResult # Add delimiter to some cases + else: + if lL: lL.warning(f"JSInitGenerator return bad type: {str(type(lJSResult))}, CP Key {self.mControlPanelNameStr}") + except Exception as e: + if lL: lL.exception( + f"Error in control panel JSInitGeneratorDef. CP Key {self.mControlPanelNameStr}. Exception are below") + return lResultStr \ No newline at end of file diff --git a/Sources/pyOpenRPA/Orchestrator/Managers/__init__.py b/Sources/pyOpenRPA/Orchestrator/Managers/__init__.py new file mode 100644 index 00000000..e449f4e5 --- /dev/null +++ b/Sources/pyOpenRPA/Orchestrator/Managers/__init__.py @@ -0,0 +1 @@ +from .ControlPanel import ControlPanel \ No newline at end of file diff --git a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py index 803e735a..01abe8d5 100644 --- a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py +++ b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py @@ -24,31 +24,14 @@ def HiddenJSInitGenerate(inRequest, inGSettings): lUACCPTemplateKeyList=["pyOpenRPADict","CPKeyDict"] lL = inGSettings["Logger"] # Alias for logger lJSInitResultStr = "" - lRenderFunctionsRobotDict = inGSettings["CPDict"] + lRenderFunctionsRobotDict = inGSettings["ServerDict"]["ControlPanelDict"] for lItemKeyStr in lRenderFunctionsRobotDict: lItemDict = lRenderFunctionsRobotDict[lItemKeyStr] - lJSInitGeneratorDef = lItemDict.get("JSInitGeneratorDef",None) lUACBool = dUAC(inRoleKeyList=lUACCPTemplateKeyList+[lItemKeyStr]) # Check if render function is applicable User Access Rights (UAC) if lItemKeyStr=="VersionCheck": lUACBool=True # For backward compatibility for the old fron version which not reload page when new orch version is comming if lUACBool: # Run function if UAC is TRUE # JSONGeneratorDef - if lJSInitGeneratorDef is not None: # Call def (inRequest, inGSettings) or def (inGSettings) - lJSResult = None - lDEFSignature = signature(lJSInitGeneratorDef) # Get signature of the def - lDEFARGLen = len(lDEFSignature.parameters.keys()) # get count of the def args - try: - if lDEFARGLen == 1: # def (inGSettings) - lJSResult = lJSInitGeneratorDef(inGSettings) - elif lDEFARGLen == 2: # def (inRequest, inGSettings) - lJSResult = lJSInitGeneratorDef(inRequest, inGSettings) - elif lDEFARGLen == 0: # def () - lJSResult = lJSInitGeneratorDef() - if type(lJSResult) is str: - lJSInitResultStr += "; "+lJSResult # Add delimiter to some cases - else: - if lL: lL.warning(f"JSInitGenerator return bad type: {str(type(lJSResult))}, CP Key {lItemKeyStr}") - except Exception as e: - if lL: lL.exception(f"Error in control panel JSInitGeneratorDef. CP Key {lItemKeyStr}. Exception are below") + lJSInitResultStr = lJSInitResultStr + ";" + lItemDict.OnInitJSStr(inRequest=inRequest) return lJSInitResultStr # Generate CP HTML + JSON @@ -59,59 +42,17 @@ def HiddenCPDictGenerate(inRequest, inGSettings): lL = inGSettings["Logger"] # Alias for logger # Create result JSON lCPDict = {} - lRenderFunctionsRobotDict = inGSettings["CPDict"] + lRenderFunctionsRobotDict = inGSettings["ServerDict"]["ControlPanelDict"] for lItemKeyStr in lRenderFunctionsRobotDict: lItemDict = lRenderFunctionsRobotDict[lItemKeyStr] - lItemHTMLRenderDef = lItemDict.get("HTMLRenderDef",None) - lItemJSONGeneratorDef = lItemDict.get("JSONGeneratorDef",None) lUACBool = dUAC(inRoleKeyList=lUACCPTemplateKeyList+[lItemKeyStr]) # Check if render function is applicable User Access Rights (UAC) if lItemKeyStr=="VersionCheck": lUACBool=True # For backward compatibility for the old fron version which not reload page when new orch version is comming if lUACBool: # Run function if UAC is TRUE lCPItemDict = {"HTMLStr": None, "JSONDict":None} - # HTMLRenderDef - if lItemHTMLRenderDef is not None: # Call def (inRequest, inGSettings) or def (inGSettings) - lHTMLResult = None - lDEFSignature = signature(lItemHTMLRenderDef) # Get signature of the def - lDEFARGLen = len(lDEFSignature.parameters.keys()) # get count of the def args - try: - if lDEFARGLen == 1: # def (inGSettings) - lHTMLResult = lItemHTMLRenderDef(inGSettings) - elif lDEFARGLen == 2: # def (inRequest, inGSettings) - lHTMLResult = lItemHTMLRenderDef(inRequest, inGSettings) - elif lDEFARGLen == 0: # def () - lHTMLResult = lItemHTMLRenderDef() - # RunFunction - # Backward compatibility up to 1.2.0 - call HTML generator if result has no "HTMLStr" - if type(lHTMLResult) is str: - lCPItemDict["HTMLStr"] = lHTMLResult - elif "HTMLStr" in lHTMLResult or "JSONDict" in lHTMLResult: - lCPItemDict = lHTMLResult # new version - else: - # Call backward compatibility HTML generator - lCPItemDict["HTMLStr"] = Basic.HTMLControlPanelBC(inCPDict=lHTMLResult) - except Exception as e: - if lL: lL.exception(f"Error in control panel HTMLRenderDef. CP Key {lItemKeyStr}. Exception are below") + # HTML Render + lCPItemDict["HTMLStr"] = lItemDict.OnRefreshHTMLStr(inRequest=inRequest) # JSONGeneratorDef - if lItemJSONGeneratorDef is not None: # Call def (inRequest, inGSettings) or def (inGSettings) - lJSONResult = None - lDEFSignature = signature(lItemJSONGeneratorDef) # Get signature of the def - lDEFARGLen = len(lDEFSignature.parameters.keys()) # get count of the def args - try: - if lDEFARGLen == 1: # def (inGSettings) - lJSONResult = lItemJSONGeneratorDef(inGSettings) - elif lDEFARGLen == 2: # def (inRequest, inGSettings) - lJSONResult = lItemJSONGeneratorDef(inRequest, inGSettings) - elif lDEFARGLen == 0: # def () - lJSONResult = lItemJSONGeneratorDef() - # RunFunction - # Backward compatibility up to 1.2.0 - call HTML generator if result has no "HTMLStr" - lType = type(lJSONResult) - if lType is str or lJSONResult is None or lType is int or lType is list or lType is dict or lType is bool or lType is float: - lCPItemDict["JSONDict"] = lJSONResult - else: - if lL: lL.warning(f"JSONGenerator return bad type: {str(type(lJSONResult))}, CP Key {lItemKeyStr}") - except Exception as e: - if lL: lL.exception(f"Error in control panel JSONGeneratorDef. CP Key {lItemKeyStr}. Exception are below") + lCPItemDict["JSONDict"] = lItemDict.OnRefreshJSONDict(inRequest=inRequest) # Insert CPItemDict in result CPDict lCPDict[lItemKeyStr]=lCPItemDict return lCPDict @@ -207,6 +148,8 @@ def pyOpenRPA_ServerData(inRequest,inGSettings): def pyOpenRPA_ServerJSInit(inRequest,inGSettings): lResultStr = HiddenJSInitGenerate(inRequest=inRequest, inGSettings=inGSettings) inResponseDict = inRequest.OpenRPAResponseDict + if lResultStr is None: + lResultStr = "" # Write content as utf-8 data inResponseDict["Body"] = bytes(lResultStr, "utf8") diff --git a/Sources/pyOpenRPA/Orchestrator/SettingsTemplate.py b/Sources/pyOpenRPA/Orchestrator/SettingsTemplate.py index b92371b7..6d59a801 100644 --- a/Sources/pyOpenRPA/Orchestrator/SettingsTemplate.py +++ b/Sources/pyOpenRPA/Orchestrator/SettingsTemplate.py @@ -39,6 +39,9 @@ def __Create__(): # # # # # # # # # # # # # # # # # # }, "ServerDict": { + "ControlPanelDict": { + # "CPKey": + }, "AgentLimitLogSizeBytesInt": 300, # Don't show body if json body of transmition is more than "ServerThread": None, # Server thread is there "AgentActivityLifetimeSecFloat": 1200.0, # Time in seconds to life for activity for the agent @@ -162,6 +165,7 @@ def __Create__(): "ThreadIdInt": None, # Technical field - will be setup when processor init "WarningExecutionMoreThanSecFloat": 60.0 # Push warning if execution more than n seconds }, + # TODO REMOVE DEPRECATED KEYS IN v.2.0.0 "ControlPanelDict": { # Old structure > CPDict "RefreshSeconds": 5, # deprecated parameter "RobotList": [ @@ -171,6 +175,7 @@ def __Create__(): #} ] }, + # TODO REMOVE DEPRECATED KEYS IN v.2.0.0 "CPDict": { # "CPKey": {"HTMLRenderDef":None, "JSONGeneratorDef":None, "JSInitGeneratorDef":None} }, diff --git a/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py b/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py index 06c63bfd..e60819c3 100644 --- a/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py +++ b/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py @@ -8,6 +8,7 @@ from . import Timer from . import Processor from . import BackwardCompatibility # Backward compatibility from v1.1.13 from . import Core +from . import Managers from subprocess import CREATE_NEW_CONSOLE from .Utils import LoggerHandlerDumpLogList @@ -799,19 +800,13 @@ def WebCPUpdate(inCPKeyStr, inHTMLRenderDef=None, inJSONGeneratorDef=None, inJSI :param inJSONGeneratorDef: :param inJSInitGeneratorDef: """ - inGSettings = GSettingsGet(inGSettings=inGSettings) # Set the global settings - # Create Struct if the re is current key - if inCPKeyStr not in inGSettings["CPDict"]: - inGSettings["CPDict"][inCPKeyStr] = {"HTMLRenderDef": None,"JSONGeneratorDef": None, "JSInitGeneratorDef": None} + lCPManager = Managers.ControlPanel(inControlPanelNameStr=inCPKeyStr, inJinja2TemplatePathStr=None) # CASE HTMLRender - if inHTMLRenderDef is not None: - inGSettings["CPDict"][inCPKeyStr]["HTMLRenderDef"]=inHTMLRenderDef + if inHTMLRenderDef is not None: lCPManager.mBackwardCompatibilityHTMLDef = inHTMLRenderDef # CASE JSONGenerator - if inJSONGeneratorDef is not None: - inGSettings["CPDict"][inCPKeyStr]["JSONGeneratorDef"] = inJSONGeneratorDef + if inJSONGeneratorDef is not None: lCPManager.mBackwardCompatibilityJSONDef = inJSONGeneratorDef # CASE JSInitGeneratorDef - if inJSInitGeneratorDef is not None: - inGSettings["CPDict"][inCPKeyStr]["JSInitGeneratorDef"] = inJSInitGeneratorDef + if inJSInitGeneratorDef is not None: lCPManager.mBackwardCompatibilityJSDef = inJSInitGeneratorDef def WebAuditMessageCreate(inRequest, inOperationCodeStr="-", inMessageStr="-"):