From 77bb953c195586a1d558f98e27ee330fe701ac44 Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Sat, 25 Dec 2021 23:25:27 +0300 Subject: [PATCH] draft 1.2.7 (need test) # Add Orchestrator.StorageExists # Add Orchestrator.StorageGet # Create DataGenerator in Managers.ControlPanel for Jinja2 template --- Sources/GuideSphinx/Orchestrator/02_Defs.rst | 4 + .../Orchestrator/Managers/ControlPanel.py | 177 +++++++++++++++--- .../pyOpenRPA/Orchestrator/ServerSettings.py | 11 +- .../Orchestrator/__Orchestrator__.py | 22 ++- 4 files changed, 185 insertions(+), 29 deletions(-) diff --git a/Sources/GuideSphinx/Orchestrator/02_Defs.rst b/Sources/GuideSphinx/Orchestrator/02_Defs.rst index 64e42643..e9857c70 100644 --- a/Sources/GuideSphinx/Orchestrator/02_Defs.rst +++ b/Sources/GuideSphinx/Orchestrator/02_Defs.rst @@ -26,6 +26,10 @@ Group GSettings... ###################### Basic defs to work with singleton gSettings. +Group Storage... +###################### +Defs to work with special locations in Orchestrator: Robot, User (TODO), User + Robot (TODO) + Group OS... ###################### Interaction with shell on the Orchestrator user session. diff --git a/Sources/pyOpenRPA/Orchestrator/Managers/ControlPanel.py b/Sources/pyOpenRPA/Orchestrator/Managers/ControlPanel.py index 19e4b6e3..288f0afd 100644 --- a/Sources/pyOpenRPA/Orchestrator/Managers/ControlPanel.py +++ b/Sources/pyOpenRPA/Orchestrator/Managers/ControlPanel.py @@ -3,30 +3,72 @@ import jinja2 import os from inspect import signature # For detect count of def args from ..Web import Basic +import operator +import math class ControlPanel(): """ Manage your control panel on the orchestrator + Control panel has 3 events types: + - onRefreshHTML - run every n (see settings) second to detect changes in HTML control panel. + - onRefreshJSON - run every n (see settings) second to detect changes in JSON data container to client side. + - onInitJS - run when client reload the Orchestrator web page + .. code-block:: python # Usage example: lCPManager = Orchestrator.Managers.ControlPanel(inControlPanelNameStr="TestControlPanel", - inJinja2TemplatePathStr="ControlPanel\\test.html", inJinja2TemplateRefreshBool = True) + inRefreshHTMLJinja2TemplatePathStr="ControlPanel\\test.html", inJinja2TemplateRefreshBool = True) + + If you use Jinja2 you can use next data context: + StorageRobotDict: Orchestrator.StorageRobotGet(inRobotNameStr=self.mRobotNameStr), + ControlPanelInstance: self, + OrchestratorModule:Orchestrator, + RequestInstance: inRequest, + UserInfoDict: Orchestrator.WebUserInfoGet(inRequest=inRequest), + UserUACDict: Orchestrator.UACUserDictGet(inRequest=inRequest), + UserUACCheckDef: inRequest.UACClientCheck, + EnumerateDef: enumerate, + OperatorModule: operator, + MathModule: math + + .. code-block:: html + Hello my control panel! + You can use any def from Orchestrator module here in Jinja2 HTML template: + Example: OrchestratorModule.OSCMD(inCMDStr="notepad") + {{MathModule.pi}} + {% if UserInfoDict['UserNameUpperStr']=="ND" %} + YES - IT IS ND + {% endif %} + """ mControlPanelNameStr = None - mJinja2TemplatePathStr = None - mJinja2TemplateFileNameStr = None - mJinja2Loader = None - mJinja2Env = None - mJinja2Template = None + # Jinja2 consolidated mJinja2TemplateRefreshBool = None + mJinja2DataUpdateDict = None + + # RefreshHTML block + mRefreshHTMLJinja2TemplatePathStr = None + mRefreshHTMLJinja2TemplateFileNameStr = None + mRefreshHTMLJinja2Loader = None + mRefreshHTMLJinja2Env = None + mRefreshHTMLJinja2Template = None + + # InitJS block + mInitJSJinja2TemplatePathStr = None + mInitJSJinja2TemplateFileNameStr = None + mInitJSJinja2Loader = None + mInitJSJinja2Env = None + mInitJSJinja2Template = None mBackwardCompatibilityHTMLDef = None mBackwardCompatibilityJSDef = None mBackwardCompatibilityJSONDef = None - def __init__(self, inControlPanelNameStr, inJinja2TemplatePathStr = None, inJinja2TemplateRefreshBool = False): + mRobotNameStr = None + + def __init__(self, inControlPanelNameStr, inRefreshHTMLJinja2TemplatePathStr = None, inJinja2TemplateRefreshBool = False, inRobotNameStr = None): """ Constructor of the control panel manager @@ -37,58 +79,119 @@ class ControlPanel(): 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.RefreshHTMLJinja2TemplatePathSet(inJinja2TemplatePathStr = inRefreshHTMLJinja2TemplatePathStr) self.mJinja2TemplateRefreshBool = inJinja2TemplateRefreshBool self.mControlPanelNameStr = inControlPanelNameStr # Set the name of the control panel + self.mRobotNameStr = inRobotNameStr # Set the robot name for robot it execute + + def Jinja2DataUpdateDictSet(self, inJinja2DataUpdateDict): + """ + Set the data dict from the Jinja2 context (you can add some new params) + + :param inJinja2DataUpdateDict: dict, which will be appended to main data context + :return: None + """ + self.mJinja2DataUpdateDict = inJinja2DataUpdateDict + + def RefreshHTMLJinja2TemplatePathSet(self, inJinja2TemplatePathStr): + """ + Create Jinja2 env and load the template html + + :param inJinja2TemplatePathStr: + :return: + """ + try: + if inJinja2TemplatePathStr is not None: + lSystemLoaderPathStr = "/".join(inJinja2TemplatePathStr.split("\\")[0:-1]) + lTemplateFileNameStr = inJinja2TemplatePathStr.split("\\")[-1] + self.mRefreshHTMLJinja2TemplateFileNameStr = lTemplateFileNameStr + self.mRefreshHTMLJinja2Loader = jinja2.FileSystemLoader(lSystemLoaderPathStr) + self.mRefreshHTMLJinja2Env = jinja2.Environment(loader=self.mRefreshHTMLJinja2Loader, trim_blocks=True) + self.mRefreshHTMLJinja2Template = self.mRefreshHTMLJinja2Env.get_template(lTemplateFileNameStr) + except Exception as e: + Orchestrator.OrchestratorLoggerGet().exception("EXCEPTION WHEN INIT Jinja2") + + def RefreshHTMLJinja2StrGenerate(self, inDataDict): + """ + Generate the HTML str from the Jinja2. Pass the context inDataDict + :param inDataDict: + :return: + """ + if self.mJinja2TemplateRefreshBool == True: + self.mRefreshHTMLJinja2Template = self.mRefreshHTMLJinja2Env.get_template(self.mRefreshHTMLJinja2TemplateFileNameStr) + lHTMLStr = self.mRefreshHTMLJinja2Template.render(**inDataDict) # Render the template into str + return lHTMLStr - def Jinja2TemplatePathSet(self, inJinja2TemplatePathStr): + def InitJSJinja2TemplatePathSet(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.mJinja2TemplateFileNameStr = lTemplateFileNameStr - self.mJinja2Loader = jinja2.FileSystemLoader(lSystemLoaderPathStr) - self.mJinja2Env = jinja2.Environment(loader=self.mJinja2Loader, trim_blocks=True) - self.mJinja2Template = self.mJinja2Env.get_template(lTemplateFileNameStr) + try: + if inJinja2TemplatePathStr is not None: + lSystemLoaderPathStr = "/".join(inJinja2TemplatePathStr.split("\\")[0:-1]) + lTemplateFileNameStr = inJinja2TemplatePathStr.split("\\")[-1] + self.mInitJSJinja2TemplateFileNameStr = lTemplateFileNameStr + self.mInitJSJinja2Loader = jinja2.FileSystemLoader(lSystemLoaderPathStr) + self.mInitJSJinja2Env = jinja2.Environment(loader=self.mInitJSJinja2Loader, trim_blocks=True) + self.mInitJSJinja2Template = self.mInitJSJinja2Env.get_template(lTemplateFileNameStr) + except Exception as e: + Orchestrator.OrchestratorLoggerGet().exception("EXCEPTION WHEN INIT Jinja2") - def Jinja2StrGenerate(self, inDataDict): + def InitJSJinja2StrGenerate(self, inDataDict): """ Generate the HTML str from the Jinja2. Pass the context inDataDict :param inDataDict: :return: """ if self.mJinja2TemplateRefreshBool == True: - self.mJinja2Template = self.mJinja2Env.get_template(self.mJinja2TemplateFileNameStr) - lHTMLStr = self.mJinja2Template.render(**inDataDict) # Render the template into str + self.mInitJSJinja2Template = self.mInitJSJinja2Env.get_template(self.mInitJSJinja2TemplateFileNameStr) + lHTMLStr = self.mInitJSJinja2Template.render(**inDataDict) # Render the template into str return lHTMLStr def DataDictGenerate(self, inRequest): """ + :param inRequest: request handler (from http.server import BaseHTTPRequestHandler) :return: """ - lData = {} + lData = { + "StorageRobotDict": None, + "ControlPanelInstance":self, + "OrchestratorModule":Orchestrator, + "RequestInstance": inRequest, + "UserInfoDict": Orchestrator.WebUserInfoGet(inRequest=inRequest), + "UserUACDict": Orchestrator.UACUserDictGet(inRequest=inRequest), + "UserUACCheckDef": inRequest.UACClientCheck, + "EnumerateDef": enumerate, + "OperatorModule": operator, + "MathModule": math + } + # Get the robot storage by the robot name (if you set robot name when init) + if self.mRobotNameStr is not None: + lData["StorageRobotDict"] = Orchestrator.StorageRobotGet(inRobotNameStr=self.mRobotNameStr) + # Checkj Jinja2DataUpdateDict + if self.mJinja2DataUpdateDict is not None: + lData.update(self.mJinja2DataUpdateDict) 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: + + :param inRequest: request handler (from http.server import BaseHTTPRequestHandler) :return: """ lHTMLStr = None lL = Orchestrator.OrchestratorLoggerGet() if self.mBackwardCompatibilityHTMLDef is None: - if self.mJinja2Template is not None: + if self.mRefreshHTMLJinja2Template is not None or (self.mJinja2TemplateRefreshBool == True and self.mRefreshHTMLJinja2TemplateFileNameStr is not None): lDataDict = self.OnRefreshHTMLDataDict(inRequest = inRequest) # Jinja code - lHTMLStr = self.Jinja2StrGenerate(inDataDict=lDataDict) + lHTMLStr = self.RefreshHTMLJinja2StrGenerate(inDataDict=lDataDict) else: lHTMLStr = self.BackwardAdapterHTMLDef(inRequest=inRequest) # return the str @@ -97,14 +200,27 @@ class ControlPanel(): 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 + + :param inRequest: request handler (from http.server import BaseHTTPRequestHandler) :return: dict """ return self.DataDictGenerate(inRequest=inRequest) + def OnRefreshHTMLHashStr(self, inRequest): + """ + Generate the hash the result output HTML. You can override this function if you know how to optimize HTML rendering. + TODO NEED TO MODIFY ServerSettings to work with Hash because of all defs are need do use Hash + + :param inRequest: request handler (from http.server import BaseHTTPRequestHandler) + :return: None - default, hash function is not determined. Str - hash function is working on! + """ + return None + 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 + :param inRequest: request handler (from http.server import BaseHTTPRequestHandler) :return: Dict type """ lResultDict = None @@ -118,15 +234,28 @@ class ControlPanel(): """ 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. + :param inRequest: request handler (from http.server import BaseHTTPRequestHandler) :return: "" """ lJSStr = "" if self.mBackwardCompatibilityJSDef is None: - pass + if self.mInitJSJinja2Template is not None or (self.mJinja2TemplateRefreshBool == True and self.mInitJSJinja2TemplateFileNameStr is not None): + lDataDict = self.OnInitJSDataDict(inRequest = inRequest) + # Jinja code + lJSStr = self.InitJSJinja2StrGenerate(inDataDict=lDataDict) else: lJSStr = self.BackwardAdapterJSDef(inRequest=inRequest) return lJSStr + def OnInitJSDataDict(self, inRequest): + """ + Event to prepare data context for the futher Jinja2 JS init generation. You can override this def if you want some thing more data + + :param inRequest: request handler (from http.server import BaseHTTPRequestHandler) + :return: dict + """ + return self.DataDictGenerate(inRequest=inRequest) + def BackwardAdapterHTMLDef(self,inRequest): lGS = Orchestrator.GSettingsGet() lL = Orchestrator.OrchestratorLoggerGet() diff --git a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py index 01abe8d5..ee45af62 100644 --- a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py +++ b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py @@ -49,10 +49,13 @@ def HiddenCPDictGenerate(inRequest, inGSettings): 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} - # HTML Render - lCPItemDict["HTMLStr"] = lItemDict.OnRefreshHTMLStr(inRequest=inRequest) - # JSONGeneratorDef - lCPItemDict["JSONDict"] = lItemDict.OnRefreshJSONDict(inRequest=inRequest) + try: + # HTML Render + lCPItemDict["HTMLStr"] = lItemDict.OnRefreshHTMLStr(inRequest=inRequest) + # JSONGeneratorDef + lCPItemDict["JSONDict"] = lItemDict.OnRefreshJSONDict(inRequest=inRequest) + except Exception as e: + lL.exception(f"EXCEPTION WHEN HTML/ JSON RENDER") # Insert CPItemDict in result CPDict lCPDict[lItemKeyStr]=lCPItemDict return lCPDict diff --git a/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py b/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py index e60819c3..83b88f0c 100644 --- a/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py +++ b/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py @@ -800,7 +800,7 @@ def WebCPUpdate(inCPKeyStr, inHTMLRenderDef=None, inJSONGeneratorDef=None, inJSI :param inJSONGeneratorDef: :param inJSInitGeneratorDef: """ - lCPManager = Managers.ControlPanel(inControlPanelNameStr=inCPKeyStr, inJinja2TemplatePathStr=None) + lCPManager = Managers.ControlPanel(inControlPanelNameStr=inCPKeyStr, inRefreshHTMLJinja2TemplatePathStr=None) # CASE HTMLRender if inHTMLRenderDef is not None: lCPManager.mBackwardCompatibilityHTMLDef = inHTMLRenderDef # CASE JSONGenerator @@ -1085,6 +1085,26 @@ def GSettingsKeyListValueOperatorPlus(inValue, inKeyList=None, inGSettings = Non lDict[inKeyList[-1]] += inValue #Set value return True +def StorageRobotExists(inRobotNameStr): + """ + Check if robot storage exists + + :param inRobotNameStr: Robot name (case sensitive) + :return: True - robot storage exist; False - does not exist + """ + return inRobotNameStr in GSettingsGet()["StorageDict"] + +def StorageRobotGet(inRobotNameStr): + """ + Get the robot storage by the robot name. If Robot storage is not exist - function will create it + + :param inRobotNameStr: Robot name (case sensitive) + :return: Dict + """ + if inRobotNameStr not in GSettingsGet()["StorageDict"]: + GSettingsGet()["StorageDict"][inRobotNameStr]={} + return GSettingsGet()["StorageDict"][inRobotNameStr] + def ProcessorAliasDefCreate(inDef, inAliasStr=None, inGSettings = None): """ Create alias for def (can be used in ActivityItem in field Def)