diff --git a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py index ee45af62..c1fa9b19 100644 --- a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py +++ b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py @@ -1,6 +1,5 @@ import json, os import copy -from inspect import signature # For detect count of def args from . import __Orchestrator__ #ControlPanelDict from desktopmagic.screengrab_win32 import ( @@ -15,6 +14,7 @@ from .Web import Basic from . import BackwardCompatibility # Support old up to 1.2.0 defs from . import Processor from . import SettingsTemplate + # # # # # # # # # # # # # v 1.2.0 Functionallity # # # # # # # # # # # # @@ -380,6 +380,31 @@ def pyOpenRPA_Agent_O2A(inRequest, inGSettings): except Exception as e: if lL: lL.exception("pyOpenRPA_Agent_O2A Exception!") lThisAgentDict["ConnectionCountInt"] -= 1 # Connection go to be closed - decrement the connection count + +def pyOpenRPA_Debugging_HelperDefList(inRequest, inGSettings): + # Parse query + lResultDict = { + "success": True, + "results": [] + } + # Get the path + lPathSplitList = __Orchestrator__.WebRequestParsePath(inRequest=inRequest).split('/') + lQueryStr = None + if "HelperDefList" != lPathSplitList[-1] and "" != lPathSplitList[-1]: lQueryStr = lPathSplitList[-1] + lDefList = __Orchestrator__.ActivityItemHelperDefList(inDefQueryStr=lQueryStr) + for lDefStr in lDefList: + lResultDict["results"].append({"name": lDefStr, "value": lDefStr, "text": lDefStr}) + __Orchestrator__.WebRequestResponseSend(inRequest=inRequest, inResponeStr=json.dumps(lResultDict)) + +def pyOpenRPA_Debugging_HelperDefAutofill(inRequest, inGSettings): + # Parse query + # Get the path + lPathSplitList = __Orchestrator__.WebRequestParsePath(inRequest=inRequest).split('/') + lQueryStr = None + if "HelperDefAutofill" != lPathSplitList[-1] and "" != lPathSplitList[-1]: lQueryStr = lPathSplitList[-1] + lResultDict = __Orchestrator__.ActivityItemHelperDefAutofill(inDef = lQueryStr) + __Orchestrator__.WebRequestResponseSend(inRequest=inRequest, inResponeStr=json.dumps(lResultDict)) + # See docs in Agent (pyOpenRPA.Agent.A2O) def pyOpenRPA_Agent_A2O(inRequest, inGSettings): lL = inGSettings["Logger"] @@ -448,7 +473,8 @@ def SettingsUpdate(inGlobalConfiguration): {"Method": "POST", "URL": "/pyOpenRPA/ProcessorQueueAdd", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Processor, "ResponseContentType": "application/json"}, {"Method": "POST", "URL": "/pyOpenRPA/ActivityListExecute", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_ActivityListExecute, "ResponseContentType": "application/json"}, {"Method": "POST", "URL": "/pyOpenRPA/Agent/O2A", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Agent_O2A, "ResponseContentType": "application/json"}, - {"Method": "POST", "URL": "/pyOpenRPA/Agent/A2O", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Agent_A2O, "ResponseContentType": "application/json"}, + {"Method": "GET", "URL": "/pyOpenRPA/Debugging/HelperDefList/", "MatchType": "BeginWith","ResponseDefRequestGlobal": pyOpenRPA_Debugging_HelperDefList, "ResponseContentType": "application/json"}, + {"Method": "GET", "URL": "/pyOpenRPA/Debugging/HelperDefAutofill/", "MatchType": "BeginWith","ResponseDefRequestGlobal": pyOpenRPA_Debugging_HelperDefAutofill, "ResponseContentType": "application/json"}, ] inGlobalConfiguration["ServerDict"]["URLList"]=inGlobalConfiguration["ServerDict"]["URLList"]+lURLList return inGlobalConfiguration \ No newline at end of file diff --git a/Sources/pyOpenRPA/Orchestrator/Web/Index.js b/Sources/pyOpenRPA/Orchestrator/Web/Index.js index 018fdde0..ad17e3d3 100644 --- a/Sources/pyOpenRPA/Orchestrator/Web/Index.js +++ b/Sources/pyOpenRPA/Orchestrator/Web/Index.js @@ -893,11 +893,43 @@ $(document).ready(function() { mGlobal.pyOpenRPA.ServerLogListRefreshDef(); // Init the refresh data def from the log window mGlobal.pyOpenRPA.ServerLogListDoRenderTrue(); // Init button to freeze/unfreeze textare with logs - $('.ui.dropdown').dropdown(); + //$('.ui.dropdown').dropdown(); //////////////////////////////////////////// - // Debugging + // 1.2.7 Debugging /// Execute ActivityItem + // 1.2.7 Debugging toolbox init + $('.ui.dropdown.mGlobal-pyOpenRPA-Debugging-Def-Dropdown') + .dropdown({ + apiSettings: { + // this url parses query server side and returns filtered results + url: '/pyOpenRPA/Debugging/HelperDefList/{query}' + }, + }) + ; + // Debugging onchange def autofill init + $('.ui.dropdown.mGlobal-pyOpenRPA-Debugging-Def-Dropdown')[0].onchange=function(inEvent){ + lValueStr = inEvent.target.value + $.ajax({ + type: "GET", + url: '/pyOpenRPA/Debugging/HelperDefAutofill/'+lValueStr, + data: null, + success: + function(lData,l2,l3) + { + var lResponseJSON=JSON.parse(lData) + console.log("HelperDefAutofill:") + console.log(lResponseJSON) + $(".mGlobal-pyOpenRPA-Debugging-ArgList")[0].value = JSON.stringify(lResponseJSON["ArgList"]) + $(".mGlobal-pyOpenRPA-Debugging-ArgDict")[0].value = JSON.stringify(lResponseJSON["ArgDict"]) + $(".mGlobal-pyOpenRPA-Debugging-ArgGSettingsStr")[0].value = JSON.stringify(lResponseJSON["ArgGSettingsStr"]) + $(".mGlobal-pyOpenRPA-Debugging-ArgLoggerStr")[0].value = JSON.stringify(lResponseJSON["ArgLoggerStr"]) + }, + dataType: "text" + }); + } + + mGlobal.pyOpenRPA.DebuggingExecute=function() { ///EXAMPLE // { @@ -910,14 +942,14 @@ $(document).ready(function() { ///Подготовить конфигурацию lArgListStr = $(".mGlobal-pyOpenRPA-Debugging-ArgList")[0].value lArgDictStr = $(".mGlobal-pyOpenRPA-Debugging-ArgDict")[0].value - lArgGSettingsStr = $(".mGlobal-pyOpenRPA-Debugging-ArgGSettings")[0].value - lArgLoggerStr = $(".mGlobal-pyOpenRPA-Debugging-ArgLogger")[0].value + lArgGSettingsStr = $(".mGlobal-pyOpenRPA-Debugging-ArgGSettingsStr")[0].value + lArgLoggerStr = $(".mGlobal-pyOpenRPA-Debugging-ArgLoggerStr")[0].value lActivityItem = { "Def":$(".mGlobal-pyOpenRPA-Debugging-Def")[0].value, // def link or def alias (look gSettings["Processor"]["AliasDefDict"]) "ArgList":(lArgListStr == "" ? [] : JSON.parse(lArgListStr)), // Args list "ArgDict":(lArgDictStr == "" ? {} : JSON.parse(lArgDictStr)), // Args dictionary - "ArgGSettings": (lArgGSettingsStr == "" ? null : lArgGSettingsStr), // Name of GSettings attribute: str (ArgDict) or index (for ArgList) - "ArgLogger": (lArgLoggerStr == "" ? null : lArgLoggerStr) // Name of GSettings attribute: str (ArgDict) or index (for ArgList) + "ArgGSettingsStr": (lArgGSettingsStr == "" ? null : lArgGSettingsStr), // Name of GSettings attribute: str (ArgDict) or index (for ArgList) + "ArgLoggerStr": (lArgLoggerStr == "" ? null : lArgLoggerStr) // Name of GSettings attribute: str (ArgDict) or index (for ArgList) } lData = [lActivityItem] $.ajax({ diff --git a/Sources/pyOpenRPA/Orchestrator/Web/Index.xhtml b/Sources/pyOpenRPA/Orchestrator/Web/Index.xhtml index c355bbd4..c74326e4 100644 --- a/Sources/pyOpenRPA/Orchestrator/Web/Index.xhtml +++ b/Sources/pyOpenRPA/Orchestrator/Web/Index.xhtml @@ -304,11 +304,19 @@ Debugging - Send -
-
- Def -
- +
+
Def
+
+
@@ -324,15 +332,15 @@
- ArgGSettings + ArgGSettingsStr
- +
- ArgLogger + ArgLoggerStr
- +
Execute
diff --git a/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py b/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py index 83b88f0c..b44dd0ab 100644 --- a/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py +++ b/Sources/pyOpenRPA/Orchestrator/__Orchestrator__.py @@ -1,6 +1,6 @@ import subprocess, json, psutil, time, os, win32security, sys, base64, logging, ctypes, copy #Get input argument import pickle - +import inspect from partd import Server from . import Server @@ -29,6 +29,7 @@ import uuid # Generate uuid import datetime # datetime import math import glob # search the files +import urllib #Единый глобальный словарь (За основу взять из Settings.py) gSettingsDict = None @@ -874,6 +875,15 @@ def WebRequestParseBodyJSON(inRequest): """ return json.loads(WebRequestParseBodyStr(inRequest=inRequest)) +def WebRequestParsePath(inRequest): + """ + Parse the request - extract the url. Example: /pyOpenRPA/Debugging/DefHelper/... + + :param inRequest: + :return: Str, Example: /pyOpenRPA/Debugging/DefHelper/... + """ + return urllib.parse.unquote(inRequest.path) + def WebRequestParseFile(inRequest): """ Parse the request - extract the file (name, body in bytes) @@ -900,6 +910,13 @@ def WebRequestParseFile(inRequest): return lResultTurple +def WebRequestResponseSend(inRequest, inResponeStr): + """ + Send response for the request + :return: + """ + inRequest.OpenRPAResponseDict["Body"] = bytes(inResponeStr, "utf8") + def WebUserInfoGet(inRequest): """ Return User info about request @@ -1109,6 +1126,7 @@ def ProcessorAliasDefCreate(inDef, inAliasStr=None, inGSettings = None): """ Create alias for def (can be used in ActivityItem in field Def) !WHEN DEF ALIAS IS REQUIRED! - Def alias is required when you try to call Python def from the Orchestrator WEB side (because you can't transmit Python def object out of the Python environment) + Deprecated. See ActivityItemDefAliasCreate .. code-block:: python @@ -1128,21 +1146,13 @@ def ProcessorAliasDefCreate(inDef, inAliasStr=None, inGSettings = None): :param inAliasStr: String alias for associated def :return: str Alias string (Alias can be regenerated if previous alias was occupied) """ - #TODO Pay attention - New alias can be used too - need to create more complex algorythm to create new alias! - inGSettings = GSettingsGet(inGSettings=inGSettings) # Set the global settings - lL = inGSettings["Logger"] - if inAliasStr is None: inAliasStr = str(inDef) - # Check if key is not exists - if inAliasStr in inGSettings["ProcessorDict"]["AliasDefDict"]: - inAliasStr = str(inDef) - if lL: lL.warning(f"Orchestrator.ProcessorAliasDefCreate: Alias {inAliasStr} already exists in alias dictionary. Another alias will be generated and returned") - inGSettings["ProcessorDict"]["AliasDefDict"][inAliasStr] = inDef - return inAliasStr + return ActivityItemDefAliasCreate(inDef=inDef, inAliasStr=inAliasStr, inGSettings = inGSettings) def ProcessorAliasDefUpdate(inDef, inAliasStr, inGSettings = None): """ Update alias for def (can be used in ActivityItem in field Def). !WHEN DEF ALIAS IS REQUIRED! - Def alias is required when you try to call Python def from the Orchestrator WEB side (because you can't transmit Python def object out of the Python environment) + Deprecated. See ActivityItemDefAliasUpdate .. code-block:: python @@ -1162,12 +1172,58 @@ def ProcessorAliasDefUpdate(inDef, inAliasStr, inGSettings = None): :param inAliasStr: String alias for associated def :return: str Alias string """ - inGSettings = GSettingsGet(inGSettings=inGSettings) # Set the global settings - if callable(inDef): inGSettings["ProcessorDict"]["AliasDefDict"][inAliasStr] = inDef - else: raise Exception(f"pyOpenRPA Exception: You can't use Orchestrator.ProcessorAliasDefUpdate with arg 'inDef' string value. inDef is '{inDef}', inAliasStr is '{inAliasStr}'") - return inAliasStr + return ActivityItemDefAliasUpdate(inDef=inDef, inAliasStr=inAliasStr, inGSettings = inGSettings) -def ProcessorActivityItemCreate(inDef, inArgList=None, inArgDict=None, inArgGSettingsStr=None, inArgLoggerStr=None, inGUIDStr = None, inThreadBool = False): +# ActivityItem defs +def ActivityItemHelperDefList(inDefQueryStr=None): + """ + Create list of the available Def names in activity item. You can use query def filter via arg inDefQueryStr + + :param inDefStr: + :return: ["ActivityItemDefAliasUpdate", "ActivityItemDefAliasCreate", etc...] + """ + lResultList = [] + if inDefQueryStr is not None: # do search alg + for lKeyStr in GSettingsGet()["ProcessorDict"]["AliasDefDict"]: + if inDefQueryStr in lKeyStr: + lResultList.append(lKeyStr) + else: + for lKeyStr in GSettingsGet()["ProcessorDict"]["AliasDefDict"]: + lResultList.append(lKeyStr) + return lResultList + +def ActivityItemHelperDefAutofill(inDef): + """ + Detect def by the name and prepare the activity item dict with values. + + :param inDef: + :return: + """ + lResultDict = { + "Def": None, + "ArgList": [], + "ArgDict": {}, + "ArgGSettingsStr": None, + "ArgLoggerStr": None + } + lResultDict["Def"] = inDef + lGS = GSettingsGet() + lDefSignature = inspect.signature(lGS["ProcessorDict"]["AliasDefDict"][inDef]) + for lItemKeyStr in lDefSignature.parameters: + lItemValue = lDefSignature.parameters[lItemKeyStr] + # Check if arg name contains "GSetting" or "Logger" + if "GSETTING" in lItemKeyStr.upper(): + lResultDict["ArgGSettingsStr"] = lItemKeyStr + elif "LOGGER" in lItemKeyStr.upper(): + lResultDict["ArgLoggerStr"] = lItemKeyStr + else: + if lItemValue.default is inspect._empty: + lResultDict["ArgDict"][lItemKeyStr] = None + else: + lResultDict["ArgDict"][lItemKeyStr] = lItemValue.default + return lResultDict + +def ActivityItemCreate(inDef, inArgList=None, inArgDict=None, inArgGSettingsStr=None, inArgLoggerStr=None, inGUIDStr = None, inThreadBool = False): """ Create activity item. Activity item can be used as list item in ProcessorActivityItemAppend or in Processor.ActivityListExecute. @@ -1179,7 +1235,7 @@ def ProcessorActivityItemCreate(inDef, inArgList=None, inArgDict=None, inArgGSet # EXAMPLE 1 def TestDef(inArg1Str, inGSettings, inLogger): pass - lActivityItem = Orchestrator.ProcessorActivityItemCreate( + lActivityItem = Orchestrator.ActivityItemCreate( inDef = TestDef, inArgList=[], inArgDict={"inArg1Str": "ArgValueStr"}, @@ -1197,11 +1253,11 @@ def ProcessorActivityItemCreate(inDef, inArgList=None, inArgDict=None, inArgGSet # EXAMPLE 2 def TestDef(inArg1Str): pass - Orchestrator.ProcessorAliasDefUpdate( + Orchestrator.ActivityItemDefAliasUpdate( inGSettings = gSettings, inDef = TestDef, inAliasStr="TestDefAlias") - lActivityItem = Orchestrator.ProcessorActivityItemCreate( + lActivityItem = Orchestrator.ActivityItemCreate( inDef = "TestDefAlias", inArgList=[], inArgDict={"inArg1Str": "ArgValueStr"}, @@ -1241,6 +1297,132 @@ def ProcessorActivityItemCreate(inDef, inArgList=None, inArgDict=None, inArgGSet } return lActivityItemDict + +def ActivityItemDefAliasCreate(inDef, inAliasStr=None, inGSettings = None): + """ + Create alias for def (can be used in ActivityItem in field Def) + !WHEN DEF ALIAS IS REQUIRED! - Def alias is required when you try to call Python def from the Orchestrator WEB side (because you can't transmit Python def object out of the Python environment) + + .. code-block:: python + + # USAGE + from pyOpenRPA import Orchestrator + + def TestDef(): + pass + lAliasStr = Orchestrator.ActivityItemDefAliasCreate( + inGSettings = gSettings, + inDef = TestDef, + inAliasStr="TestDefAlias") + # Now you can call TestDef by the alias from var lAliasStr with help of ActivityItem (key Def = lAliasStr) + + :param inGSettings: Global settings dict (singleton) + :param inDef: Def + :param inAliasStr: String alias for associated def + :return: str Alias string (Alias can be regenerated if previous alias was occupied) + """ + #TODO Pay attention - New alias can be used too - need to create more complex algorythm to create new alias! + inGSettings = GSettingsGet(inGSettings=inGSettings) # Set the global settings + lL = inGSettings["Logger"] + if inAliasStr is None: inAliasStr = str(inDef) + # Check if key is not exists + if inAliasStr in inGSettings["ProcessorDict"]["AliasDefDict"]: + inAliasStr = str(inDef) + if lL: lL.warning(f"Orchestrator.ProcessorAliasDefCreate: Alias {inAliasStr} already exists in alias dictionary. Another alias will be generated and returned") + inGSettings["ProcessorDict"]["AliasDefDict"][inAliasStr] = inDef + return inAliasStr + +def ActivityItemDefAliasUpdate(inDef, inAliasStr, inGSettings = None): + """ + Update alias for def (can be used in ActivityItem in field Def). + !WHEN DEF ALIAS IS REQUIRED! - Def alias is required when you try to call Python def from the Orchestrator WEB side (because you can't transmit Python def object out of the Python environment) + + .. code-block:: python + + # USAGE + from pyOpenRPA import Orchestrator + + def TestDef(): + pass + Orchestrator.ActivityItemDefAliasUpdate( + inGSettings = gSettings, + inDef = TestDef, + inAliasStr="TestDefAlias") + # Now you can call TestDef by the alias "TestDefAlias" with help of ActivityItem (key Def = "TestDefAlias") + + :param inGSettings: Global settings dict (singleton) + :param inDef: Def + :param inAliasStr: String alias for associated def + :return: str Alias string + """ + inGSettings = GSettingsGet(inGSettings=inGSettings) # Set the global settings + if callable(inDef): inGSettings["ProcessorDict"]["AliasDefDict"][inAliasStr] = inDef + else: raise Exception(f"pyOpenRPA Exception: You can't use Orchestrator.ActivityItemDefAliasUpdate with arg 'inDef' string value. inDef is '{inDef}', inAliasStr is '{inAliasStr}'") + return inAliasStr + + + +def ProcessorActivityItemCreate(inDef, inArgList=None, inArgDict=None, inArgGSettingsStr=None, inArgLoggerStr=None, inGUIDStr = None, inThreadBool = False): + """ + Create activity item. Activity item can be used as list item in ProcessorActivityItemAppend or in Processor.ActivityListExecute. + Deprecated. See ActivityItemCreate + .. code-block:: python + + # USAGE + from pyOpenRPA import Orchestrator + + # EXAMPLE 1 + def TestDef(inArg1Str, inGSettings, inLogger): + pass + lActivityItem = Orchestrator.ProcessorActivityItemCreate( + inDef = TestDef, + inArgList=[], + inArgDict={"inArg1Str": "ArgValueStr"}, + inArgGSettingsStr = "inGSettings", + inArgLoggerStr = "inLogger") + # lActivityItem: + # { + # "Def":TestDef, + # "ArgList":inArgList, + # "ArgDict":inArgDict, + # "ArgGSettings": "inArgGSettings", + # "ArgLogger": "inLogger" + # } + + # EXAMPLE 2 + def TestDef(inArg1Str): + pass + Orchestrator.ProcessorAliasDefUpdate( + inGSettings = gSettings, + inDef = TestDef, + inAliasStr="TestDefAlias") + lActivityItem = Orchestrator.ProcessorActivityItemCreate( + inDef = "TestDefAlias", + inArgList=[], + inArgDict={"inArg1Str": "ArgValueStr"}, + inArgGSettingsStr = None, + inArgLoggerStr = None) + # lActivityItem: + # { + # "Def":"TestDefAlias", + # "ArgList":inArgList, + # "ArgDict":inArgDict, + # "ArgGSettings": None, + # "ArgLogger": None + # } + + :param inDef: def link or def alias (look gSettings["Processor"]["AliasDefDict"]) + :param inArgList: Args list for the Def + :param inArgDict: Args dict for the def + :param inArgGSettingsStr: Name of def argument of the GSettings dict + :param inArgLoggerStr: Name of def argument of the logging object + :param inGUIDStr: GUID which you can specify. If None the GUID will be generated + :param inThreadBool: True - execute ActivityItem in new thread; False - in processor thread + :return: {} + """ + return ActivityItemCreate(inDef=inDef, inArgList=inArgList, inArgDict=inArgDict, inArgGSettingsStr=inArgGSettingsStr, inArgLoggerStr=inArgLoggerStr, + inGUIDStr=inGUIDStr, inThreadBool=inThreadBool) + def ProcessorActivityItemAppend(inGSettings = None, inDef=None, inArgList=None, inArgDict=None, inArgGSettingsStr=None, inArgLoggerStr=None, inActivityItemDict=None): """ Create and add activity item in processor queue.