You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Robot/Orchestrator_ControlPanel.py

300 lines
17 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import psutil, datetime, logging, os, json
## # # # # # # # # # # # # # # # # ##
# GLOBAL PARAMETERS
gBuildFolderPathStr = '\\'.join(__file__.split('\\')[:-1])
gRobotRepositoryPathStr = os.popen(f'cd {gBuildFolderPathStr} && git rev-parse --show-toplevel').read().replace("\n",'').replace('/','\\') # needed for cmd and git pull
gRobotStartFilePathStr = os.path.join(gBuildFolderPathStr,r"pyRobotName_x64_Run.cmd") # path\to\start\file
gRobotProcessNameWOEXEStr = "pyRobotName" # RobotProcessName
gRobotADLoginStr = "AD LOGIN" # Login of the robot rdp session
gRobotADPasswordStr = "AD PASSWORD" # Password for rdp session
gRobotKeyStr = "pyRobotName" # Key for store OrchestratorConnector key-value (Orchstrator gSettings["Storage"][gRobotKeyStr])
gRDPSessionKeyStr = "pyRobotName"
gRDPSessionHostStr = "localhost" # Rdp session host
gRDPSessionPortStr = "3389" # Default RDP port is 3389
gControlPanelKeyStr = "pyRobotName" # Str key for RDP session key
gControlPanelCheckRobotProcessFromOrchestratorUserBool = True # Check process activity from orchestrator GUI session (with help of task manager, when users on the same machine)
# !! ATTENTION !! SCHEDULE TIME START STOP FILL BELOW IN ACTIVITY SECTION
gRobotToOrchestratorKeyList = ["Storage",gRobotKeyStr,"RobotToOrchestrator"]
gOrchestratorToRobotResetKeyList = ["Storage",gRobotKeyStr,"OrchestratorToRobotReset"]
gOrchestratorToRobotResetSafeStopKeyList = gOrchestratorToRobotResetKeyList+["TurnOffSafeBool"]
# # # # # # BUSINESS USER AD LOGIN # # # # # # # # # # # # # # #
gADLoginList = [
"ADLOGIN_1",
"ADLOGIN_2",
"ADLOGIN_3",
"ADLOGIN_4"
]
gADDomainNameStr = "MY-PC" # DOMAIN or EMPTY str if no domain
gADDomainIsDefaultBool = True # If domain is exist and is default (default = you can type login without domain name)
## # # # # # # # # # # # # # # # # ##
# ACTIVITY TEMPLATE START RDP & SEND CMD
gActivityROBOTStartTimeHH_MMStr = "01:01" # Time "HH:MM" [Server time] to execute activity, example "05:10"
gActivityROBOTStartWeekdayList = [0,1,2,3,4] # WeekdayList when atcivity is starting, default [0,1,2,3,4,5,6]
gActivityROBOTStartList = [
{ # Start RDP Session
"DefNameStr":"RDPSessionConnect", # Function name in RobotRDPActive.Processor
"ArgList":[], # Args list
"ArgDict":{"inRDPSessionKeyStr": gRDPSessionKeyStr, "inHostStr": gRDPSessionHostStr, "inPortStr": gRDPSessionPortStr, "inLoginStr": gRobotADLoginStr, "inPasswordStr": gRobotADPasswordStr} # Args dictionary
},
{ # Run robot file in RDP session
"DefNameStr":"RDPSessionProcessStartIfNotRunning", # Function name in RobotRDPActive.Processor
"ArgList":[], # Args list
"ArgDict":{"inRDPSessionKeyStr": gRDPSessionKeyStr, "inProcessNameWEXEStr": f"{gRobotProcessNameWOEXEStr}.exe", "inFilePathStr": gRobotStartFilePathStr, "inFlagGetAbsPathBool": False} # Args dictionary
}
]
## # # # # # # # # # # # # # # # # ##
# ACTIVITY TEMPLATE SAFE STOP (SIGNAL TO ROBOT TO STOP VIA ORCHESTRATOR CONNECTOR)
gActivityROBOTSafeStopTimeHH_MMStr = "23:40" # Time "HH:MM" [Server time] to execute activity, example "05:10"
gActivityROBOTSafeStopWeekdayList = [0,1,2,3,4] # WeekdayList when atcivity is starting, default [0,1,2,3,4,5,6]
gActivityROBOTSafeStopList = [
{
"Type": "GlobalDictKeyListValueSet",
"KeyList": gOrchestratorToRobotResetSafeStopKeyList,
"Value": True
},
#{
# "Type": "ProcessStop", # Activity type
# "Name": f"{gRobotProcessNameWOEXEStr}.exe", # proces name with .exe
# "FlagForce": False, # Safe kill
# "User": gRobotADLoginStr #Empty, user or %username%
#},
{
"Type": "GlobalDictKeyListValueOperator+", # Activity type
"KeyList": ['RobotRDPActive', 'ActivityList'], # RobotRDP Active ActivityList
"Value": [ # ActivityList - see upper
{ # Kill process
"DefNameStr": "RDPSessionMonitorStop", # Function name in RobotRDPActive.Processor
"ArgList": [], # Args list
"ArgDict": {"inRDPSessionKeyStr": gRDPSessionKeyStr} # Args dictionary
}
]
}
]
## # # # # # # # # # # # # # # # # ##
# ACTIVITY TEMPLATE FORCE STOP (SEND CMD TO STOP PROCESS & LOGOFF THE SESSION)
gActivityROBOTStopList = [
{ # Kill process
"DefNameStr":"RDPSessionProcessStop", # Function name in RobotRDPActive.Processor
"ArgList":[], # Args list
"ArgDict":{"inRDPSessionKeyStr": gRDPSessionKeyStr, "inProcessNameWEXEStr":f"{gRobotProcessNameWOEXEStr}.exe","inFlagForceCloseBool": True} # Args dictionary
},
{ # Logoff RDP Session
"DefNameStr":"RDPSessionLogoff", # Function name in RobotRDPActive.Processor
"ArgList":[], # Args list
"ArgDict":{"inRDPSessionKeyStr": gRDPSessionKeyStr} # Args dictionary
}
]
## # # # # # # # # # # # # # # # # ##
# CONTROL PANEL RENDER FOR HTML
def ControlPanelRenderDict(inRequest, inGSettings):
# window.prompt("Укажите дату лога в формате дд.мм.гггг",null)
lUAC = inRequest.OpenRPA["DefUserRoleAccessAsk"] # Alias for request user role ask
"""result={
"HeaderLeftText":"<Robot name>",
"HeaderRightText":"<header>",
"DataStorageKey":"Robot_Name", #Use key for set current dict in mGlobal.DataStorage["DataStorageKey"] on client side
"SubheaderText": "State: <span style=\"color:green\">Turned on</span>",
"BodyKeyValueList":[
{"Key":"Session list","Value":""},
{"Key":"Session list","Value":""}
],
"FooterText":"Last update: 9:38:00 09.10.2019",
"FooterButtonX2List":[
{"Text":"Turn on", "Color":"green", "Link":"", "OnClick": lOnClickRunButton.replace("\\","\\\\")},
],
"FooterButtonX1List":[
{"Text":"Kill", "Color":"red", "Link":"", "OnClick": lOnClickForceCloseButton.replace("\\","\\\\")}
]
}"""
# START :: Create activities :: START #
## Robot START (create RDP session, send CMD to start process)
lActivityROBOTStartEscaped = f"mGlobal.Processor.ServerOperatorPlus({json.dumps(['RobotRDPActive','ActivityList'])},{json.dumps(gActivityROBOTStartList)});".replace("\"","\'")
## Robot SAFE STOP (SAFE STOP COMMAND (FROM ROBOT RULES), Logoff must do robot when safe turn off)
lActivityROBOTSafeStopEscaped = f"mGlobal.Processor.Send({json.dumps(gActivityROBOTSafeStopList)});"
## Robot FORCE STOP (send CMD to stop process, logoff)
lActivityROBOTStopEscaped = f"mGlobal.Processor.ServerOperatorPlus({json.dumps(['RobotRDPActive','ActivityList'])},{json.dumps(gActivityROBOTStopList)});".replace("\"","\'")
## REPOSITORY GIT PULL (send CMD to git pull)
gRobotRepositoryPathEscapedStr = gRobotRepositoryPathStr.replace("\\","\\\\")
lActivityRepositoryGITPULL = f'mGlobal.Controller.CMDRunText(\"cd \\\"{gRobotRepositoryPathEscapedStr}\\\" && git reset --hard && git pull\");'.replace("\"","&quot;")
lActivityRepositoryGITPULLHTML=f'<a onclick=\"{lActivityRepositoryGITPULL}\" style=\"color:orange\">Update GIT</a>'
# Activity download report ReportFileDocList
lActivityReportDownloadHTML=f'<a href=\"{gRobotKeyStr}/Reports/ReportFileDocList.xlsx\" target=\"_blank\" style=\"color:green\">Download</a>'
# Activity download report ReportPTSList
lActivityReportListDownloadHTML=f'<a href=\"{gRobotKeyStr}/Reports/ReportPTSList.xlsx\" target=\"_blank\" style=\"color:green\">Download</a>'
# END :: Create activities :: END #
# START :: Init result dict template :: START #
# lBodyKeyValue_r3_start=f'<a onclick="{lActivityROBOTStartEscaped}" style=\"color:green\">Start</a>'
lStatistics = TechDictKeyList_ItemGet(inDict = inGSettings, inKeyList = gRobotToOrchestratorKeyList+["Statistics"], inDefault={})
lResultDict={
"HeaderLeftText":"Robot description",
"HeaderRightText":"Robot",
"DataStorageKey":gControlPanelKeyStr, # CLIENT SIDE:: Use key for set current dict in mGlobal.DataStorage["DataStorageKey"] on client side
"SubheaderText":"<Subheader text, state filled below>",
"BodyKeyValueList":[
{"Key":"<b>Reports</b>","Value":""}
],
"FooterText":"Last update: 9:38:00 09.10.2019",
"FooterButtonX2List":[],
"FooterButtonX1List":[],
# "GlobalStorage": inGSettings.get("Storage",{}) # UNCOMMENT FOR DEBUG PURPOSE TO WATCH inGSettings on client side
}
# Start button
if lUAC([gRobotKeyStr,"Technical","Start"]): lResultDict["FooterButtonX2List"].append({"Text":"On", "Color":"green", "Link":"", "OnClick": lActivityROBOTStartEscaped})
# Stop safe button
if lUAC([gRobotKeyStr,"Technical","StopSafe"]): lResultDict["FooterButtonX2List"].append({"Text":"Off", "Color":"orange", "Link":"", "OnClick": lActivityROBOTSafeStopEscaped})
# Stop force button
if lUAC([gRobotKeyStr,"Technical","StopForce"]): lResultDict["FooterButtonX1List"].append({"Text":"Force ", "Color":"red", "Link":"", "OnClick": lActivityROBOTStopEscaped})
# END :: Init result dict template :: END #
# START :: Fill BodyKeyValueList :: START #
# Download report button ReportFileDocList.xlsx
if lUAC([gRobotKeyStr,"Business","ReportFileDocListDownload"]): lResultDict["BodyKeyValueList"].append({"Key":"Report 1","Value":lActivityReportDownloadHTML})
# Download report button ReportList.xlsx
if lUAC([gRobotKeyStr,"Business","ReportListDownload"]): lResultDict["BodyKeyValueList"].append({"Key":"Report 2","Value":lActivityReportListDownloadHTML})
# Add Addendum title
if lUAC([gRobotKeyStr,"Technical"]): lResultDict["BodyKeyValueList"].append({"Key":"<b>Additional</b>","Value":""})
# Download log file
lActivityLogDownloadOnclickStr = """var MyDate = new Date();var MyDateString;MyDateString = MyDate.getFullYear() + "_" + ('0' + (MyDate.getMonth()+1)).slice(-2) + "_" + ('0' + MyDate.getDate()).slice(-2);
var lLogFileNameWOExtStr = window.prompt("Please enter the date in format yyyy_mm_dd (example 2020_06_16)",MyDateString); window.open('pyRobotName/Logs/'+lLogFileNameWOExtStr+'.log','_blank');""".replace("\"","&quot;")
lActivityDayLogDownloadHTML = f'<a onclick=\"{lActivityLogDownloadOnclickStr}\" style=\"color:orange\">Download</a>'
if lUAC([gRobotKeyStr,"Technical","DayLogDownload"]): lResultDict["BodyKeyValueList"].append({"Key":"Log on date","Value":lActivityDayLogDownloadHTML})
# update git button button
if lUAC([gRobotKeyStr,"Technical","GITUpdate"]): lResultDict["BodyKeyValueList"].append({"Key": "GIT Repository", "Value": lActivityRepositoryGITPULLHTML})
# END :: Fill BodyKeyValueList :: END #
# START :: Fill SubheaderText :: START #
## FILL Robot state by the check the RDP session state
lSubheaderRunTrueText="State: <span style=\"color:green\">Turned on</span>"
lSubheaderRunFalseText="State: <span style=\"color:red\">Turned off</span>"
if gControlPanelCheckRobotProcessFromOrchestratorUserBool and gRDPSessionKeyStr in inGSettings["RobotRDPActive"]["RDPList"]:
lResultDict["SubheaderText"]=lSubheaderRunTrueText
else:
lResultDict["SubheaderText"]=lSubheaderRunFalseText
# END :: Fill SubheaderText :: END #
# Fill FooterText
lResultDict["FooterText"]=f'Last update: {datetime.datetime.now().strftime("%H:%M:%S %d.%m.%Y")}'
return lResultDict
# Technical def - Get item by the list of keys
def TechDictKeyList_ItemGet(inDict, inKeyList, inDefault={}):
lResult=inDict
for lItem in inKeyList:
if lResult:
lResult = lResult.get(lItem,None)
if lResult is None:
lResult=inDefault
return lResult
# Orchestrator - pyRobot business user-role access
# Role model - if len of keys in dict is 0 - all access. If at least len is 1 - only this access
# "pyRobot":{
# "Business": {
# "Report": {
# "ReportFileDocListDownload": {}, # Download a report
# "ReportListDownload": {}, # Download a report
# "DayLogDownload": {}, # Download a report
# },
# },
# "Technical": { # Technical functions - for developers
# "GITUpdate": {}, # Action to update git repository
# "Start": {}, # Action for start robot
# "StopForce": {}, # Action for the stop force
# "StopSafe": {} # Actions for the safe stop
# }
# }
# }
# USAGE in .py
# inRequest.OpenRPA["DefUserRoleAccessAsk"](["Orchestrator","RDPActive","Controls"]) - return True or False
# inRequest.OpenRPA["DefUserRoleHierarchyGet"]() - Return dict of the role hierarchy or {}
gRuleDomainUserDict = {
"MethodMatchURLBeforeList": [
{"Method": "GET", "MatchType": "Beginwith", "URL": "/", "FlagAccess": True},
{"Method": "POST", "MatchType": "Beginwith", "URL": "/", "FlagAccess": True}
],
"ControlPanelKeyAllowedList": [gControlPanelKeyStr], # If empty - all is allowed ["RobotScreenActive", ""]
"RoleHierarchyAllowedDict": {
gRobotKeyStr:{
"Business": {
"ReportFileDocListDownload": {}, # Download a report
"ReportListDownload": {}, # Download a report
},
"Technical": { # Technical functions - for developers
"DayLogDownload": {}, # Download a report
# "GITUpdate": {}, # Action to update git repository
# "Start": {}, # Action for start robot
# "StopForce": {}, # Action for the stop force
# "StopSafe": {} # Actions for the safe stop
}
}
}
}
#Orchestrator settings update
def SettingsUpdate(inGSettings):
# Add user roles
for lUserNameStr in gADLoginList:
# Case add domain + user
inGSettings["Server"]["AccessUsers"]["RuleDomainUserDict"].update({(gADDomainNameStr.upper(),lUserNameStr.upper()):gRuleDomainUserDict})
if gADDomainIsDefaultBool:
# Case add default domain + user
inGSettings["Server"]["AccessUsers"]["RuleDomainUserDict"].update({("",lUserNameStr.upper()):gRuleDomainUserDict})
#Add RobotRDPActive in control panel
inGSettings["ControlPanelDict"]["RobotList"].append({"RenderFunction": ControlPanelRenderDict, "KeyStr": gControlPanelKeyStr})
# Add folder to server to download files
lReportFolderItem = {
"Method": "GET",
"URL": f"/{gRobotKeyStr}/Reports/", # URL of the request
"MatchType": "BeginWith", # "BeginWith|Contains|Equal|EqualCase",
# "ResponseFilePath": "", #Absolute or relative path
"ResponseFolderPath": os.path.join(gBuildFolderPathStr, "Reports"),
# Absolute or relative path
"ResponseContentType": "application/octet-stream", #HTTP Content-type
# "ResponseDefRequestGlobal": None #Function with str result
}
inGSettings["Server"]["URLList"].append(lReportFolderItem)
# Add folder to server to download files
lReportFolderItem = {
"Method": "GET",
"URL": f"/{gRobotKeyStr}/Logs/", # URL of the request
"MatchType": "BeginWith", # "BeginWith|Contains|Equal|EqualCase",
# "ResponseFilePath": "", #Absolute or relative path
"ResponseFolderPath": os.path.join(gBuildFolderPathStr, "Logs"),
# Absolute or relative path
"ResponseContentType": "application/octet-stream", #HTTP Content-type
# "ResponseDefRequestGlobal": None #Function with str result
}
inGSettings["Server"]["URLList"].append(lReportFolderItem)
# AUTOSTART Robot
inGSettings["OrchestratorStart"]["ActivityList"].append(
{
"Type": "GlobalDictKeyListValueOperator+", #Activity type
"KeyList": ['RobotRDPActive','ActivityList'], # RobotRDP Active ActivityList
"Value": gActivityROBOTStartList # ActivityList - see upper
}
)
# Add scheduler activity in Scheduler.ActivityTimeList
# # # # # # # # # # # # # # # # # # # # # # # #
lActivityTimeItemRobotStart = {
"TimeHH:MM": gActivityROBOTStartTimeHH_MMStr, #Time [HH:MM] to trigger activity
"WeekdayList": gActivityROBOTStartWeekdayList, #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7]
"Activity":{
"Type": "GlobalDictKeyListValueSet", #Activity type
"KeyList": ['RobotRDPActive','ActivityList'], # RobotRDP Active ActivityList
"Value": gActivityROBOTStartList # ActivityList - see upper
}
}
inGSettings["Scheduler"]["ActivityTimeList"].append(lActivityTimeItemRobotStart)
for lItem in gActivityROBOTSafeStopList: # Temporary method while Scheduler apply only 1 activity in 1 item
inGSettings["Scheduler"]["ActivityTimeList"].append({
"TimeHH:MM": gActivityROBOTSafeStopTimeHH_MMStr, # Time [HH:MM] to trigger activity
"WeekdayList": gActivityROBOTSafeStopWeekdayList,
"Activity": lItem # Run actiovity 0 from list # List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7]
})
return inGSettings