diff --git a/Orchestrator/Orchestrator.py b/Orchestrator/Orchestrator.py index c34aa1e4..f1767c61 100644 --- a/Orchestrator/Orchestrator.py +++ b/Orchestrator/Orchestrator.py @@ -21,7 +21,7 @@ logging.basicConfig(filename="Reports\ReportRun_"+datetime.datetime.now().strfti #Единый глобальный словарь (За основу взять из Settings.py) global mGlobalDict -mGlobalDict = Settings.mDict +mGlobalDict = Settings.Settings() Processor.mGlobalDict = mGlobalDict Timer.mGlobalDict = mGlobalDict Timer.Processor.mGlobalDict = mGlobalDict @@ -38,12 +38,12 @@ lThreadServer = Server.RobotDaemonServer("ServerThread", mGlobalDict) lThreadServer.start() #Вечный цикл while True: - lCurrentDateTime=datetime.datetime.now() + lCurrentDateTime = datetime.datetime.now() #Циклический обход правил lFlagSearchActivityType=True for lIndex, lItem in enumerate(mGlobalDict["Scheduler"]["ActivityTimeList"]): #Проверка дней недели, в рамках которых можно запускать активность - lItemWeekdayList=lItem.get("WeekdayList",[0,1,2,3,4,5,6]) + lItemWeekdayList=lItem.get("WeekdayList", [0, 1, 2, 3, 4, 5, 6]) if lCurrentDateTime.weekday() in lItemWeekdayList: if lFlagSearchActivityType: #Лог diff --git a/Orchestrator/Server.py b/Orchestrator/Server.py index dfc4b316..c904e4fb 100644 --- a/Orchestrator/Server.py +++ b/Orchestrator/Server.py @@ -4,11 +4,16 @@ from threading import Thread import Processor import importlib import pdb +import base64 +import uuid +import datetime +from http import cookies from desktopmagic.screengrab_win32 import ( getDisplayRects, saveScreenToBmp, saveRectToBmp, getScreenAsImage, getRectAsImage, getDisplaysAsImages) global mGlobalDict + def SaveScreenshot(inFilePath): # grab fullscreen # Save the entire virtual screen as a PNG @@ -35,15 +40,87 @@ class RobotDaemonServer(Thread): print('running server...') httpd.serve_forever() - +#Authenticate function () +# return dict +# { +# "Domain": "", #Empty if Auth is not success +# "User": "" #Empty if Auth is not success +# } +def AuthenticateVerify(inRequest): + lResult={"Domain": "", "User": ""} + ###################################### + #Way 1 - try to find AuthToken + lCookies = cookies.SimpleCookie(inRequest.headers.get("Cookie", "")) + #pdb.set_trace() + if "AuthToken" in lCookies: + lCookieAuthToken = lCookies.get("AuthToken", "").value + if lCookieAuthToken: + #Find AuthToken in GlobalDict + if lCookieAuthToken in mGlobalDict.get("Server", {}).get("AccessUsers", {}).get("AuthTokensDict", {}): + #Auth Token Has Been Founded + lResult["Domain"] = mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["Domain"] + lResult["User"] = mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["User"] + #Exit earlier + return lResult + ###################################### + #Way 2 - try to logon + lHeaderAuthorization = inRequest.headers.get("Authorization", "").split(" ") + if len(lHeaderAuthorization) == 2: + llHeaderAuthorizationDecodedUserPasswordList = base64.b64decode(lHeaderAuthorization[1]).decode("utf-8").split( + ":") + lUser = llHeaderAuthorizationDecodedUserPasswordList[0] + lPassword = llHeaderAuthorizationDecodedUserPasswordList[1] + lDomain = None + if "\\" in lUser: + lDomain = lUser.split("\\")[0] + lUser = lUser.split("\\")[1] + #Try to logon - use processor + lLogonResult = Processor.Activity( + { + "Type": "WindowsLogon", + "Domain": lDomain, + "User": lUser, + "Password": lPassword + } + ) + #Check result + if lLogonResult["Result"]: + lResult["Domain"] = lLogonResult["Domain"] + lResult["User"] = lLogonResult["User"] + #Create token + lAuthToken=str(uuid.uuid1()) + mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken] = {} + mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["Domain"] = lResult["Domain"] + mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["User"] = lResult["User"] + mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["TokenDatetime"] = datetime.datetime.now() + #Set-cookie + inRequest.OpenRPA={} + inRequest.OpenRPA["AuthToken"] = lAuthToken + #inRequest.OpenRPAResponse["Set-Cookie"]=[]lResult["Set-Cookie"] = lAuthToken + #pdb.set_trace() + #inRequest.send_header("Set-Cookie:", f"AuthToken={lAuthToken}") + ###################################### + return lResult +def AuthenticateBlock(inRequest): + # Send response status code + inRequest.send_response(401) + # Send headers + inRequest.send_header('Content-type', 'text/html') + inRequest.send_header('WWW-Authenticate', 'Basic') # Always ask login pass + inRequest.end_headers() + # Write content as utf-8 data + inRequest.wfile.write(bytes("", "utf8")) # HTTPRequestHandler class class testHTTPServer_RequestHandler(BaseHTTPRequestHandler): #ResponseContentTypeFile - def SendResponseContentTypeFile(self,inContentType,inFilePath): + def SendResponseContentTypeFile(self, inContentType, inFilePath): # Send response status code self.send_response(200) # Send headers - self.send_header('Content-type',inContentType) + self.send_header('Content-type', inContentType) + #Check if var exist + if hasattr(self, "OpenRPA"): + self.send_header("Set-Cookie", f"AuthToken={self.OpenRPA['AuthToken']}") self.end_headers() lFileObject = open(inFilePath, "rb") # Write content as utf-8 data @@ -52,125 +129,127 @@ class testHTTPServer_RequestHandler(BaseHTTPRequestHandler): lFileObject.close() # GET def do_GET(self): - #Мост между файлом и http запросом (новый формат) - if self.path == "/": - self.SendResponseContentTypeFile('text/html',"Web\\Index.xhtml") - #Мост между файлом и http запросом (новый формат) - if self.path == '/3rdParty/Semantic-UI-CSS-master/semantic.min.css': - self.SendResponseContentTypeFile('text/css',"..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.css") - #Мост между файлом и http запросом (новый формат) - if self.path == '/3rdParty/Semantic-UI-CSS-master/semantic.min.js': - self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.js") - #Мост между файлом и http запросом (новый формат) - if self.path == '/3rdParty/jQuery/jquery-3.1.1.min.js': - self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\jQuery\\jquery-3.1.1.min.js") - #Мост между файлом и http запросом (новый формат) - if self.path == '/3rdParty/Google/LatoItalic.css': - self.SendResponseContentTypeFile('font/css',"..\\Resources\\Web\\Google\\LatoItalic.css") - #Мост между файлом и http запросом (новый формат) - if self.path == '/3rdParty/Semantic-UI-CSS-master/themes/default/assets/fonts/icons.woff2': - self.SendResponseContentTypeFile('font/woff2',"..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default\\assets\\fonts\\icons.woff2") - #Мост между файлом и http запросом (новый формат) - if self.path == '/favicon.ico': - self.SendResponseContentTypeFile('image/x-icon',"Web\\favicon.ico") - #Мост между файлом и http запросом (новый формат) - if self.path == '/3rdParty/Handlebars/handlebars-v4.1.2.js': - self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\Handlebars\\handlebars-v4.1.2.js") - #Получить скриншот - if self.path.split("?")[0] == '/GetScreenshot': - #Сохранить файл на диск - SaveScreenshot("Screenshot.png") - self.SendResponseContentTypeFile('image/png',"Screenshot.png") - #Monitor - if self.path == '/Monitor/JSONDaemonListGet': - # Send response status code - self.send_response(200) - # Send headers - self.send_header('Content-type','application/json') - self.end_headers() - # Send message back to client - message = json.dumps(mGlobalDict) - # Write content as utf-8 data - self.wfile.write(bytes(message, "utf8")) - if self.path == '/Monitor/ControlPanelDictGet': - # Send response status code - self.send_response(200) - # Send headers - self.send_header('Content-type','application/json') - self.end_headers() - #Create result JSON - lResultJSON={"RenderRobotList":[]} - lRenderFunctionsRobotList=mGlobalDict["ControlPanelDict"]["RobotList"] - for lItem in lRenderFunctionsRobotList: - #Подключить модуль для вызова - #print(globals()) - lModuleImportName = lItem.get("RenderFunctionModuleSubfolderName","") - if lModuleImportName!="": - lModuleImportName = f'{lItem["RenderFunctionModuleSubfolderName"]}.{lItem["RenderFunctionModuleName"]}' - else: - lModuleImportName = lItem["RenderFunctionModuleName"] - lModule=importlib.import_module(lModuleImportName) - #Найти функцию - lFunction=getattr(lModule,lItem["RenderFunctionName"]) - #Выполнить вызов и записать результат - lItemResultDict=lFunction(mGlobalDict) - #RunFunction - lResultJSON["RenderRobotList"].append(lItemResultDict) - # Send message back to client - message = json.dumps(lResultJSON) - # Write content as utf-8 data - self.wfile.write(bytes(message, "utf8")) - #Filemanager function - if self.path.lower().startswith('/filemanager/'): - lFileURL=self.path[13:] - # check if file in FileURL - File Path Mapping Dict - if lFileURL.lower() in mGlobalDict["FileManager"]["FileURLFilePathDict"]: - self.SendResponseContentTypeFile('application/octet-stream',mGlobalDict["FileManager"]["FileURLFilePathDict"][lFileURL]) - # Auth function - if self.path.lower().startswith('/auth'): - # Send response status code - self.send_response(401) - # Send headers - self.send_header('Content-type','text/html') - self.send_header('WWW-Authenticate', 'Basic') #Always ask login pass - lFlagIsKerberos=False - lHeaderAuthorization = self.headers.get("Authorization","").split(" ") - import base64 - if len(lHeaderAuthorization) == 2: - llHeaderAuthorizationDecodedUserPasswordList = base64.b64decode(lHeaderAuthorization[1]).decode("utf-8").split(":") - lUser = llHeaderAuthorizationDecodedUserPasswordList[0] - lPassword = llHeaderAuthorizationDecodedUserPasswordList[1] - lDomain = None - if "\\" in lUser: - lDomain = lUser.split("\\")[0] - lUser = lUser.split("\\")[1] - #print(f"Header Authorization, domain: {lDomain}, login: {lUser}, password: {lPassword}") - #self.send_header('Vary', 'negotiative') - #self.send_header('TCN', 'choice') - self.end_headers() - # Write content as utf-8 data - self.wfile.write(bytes("", "utf8")) - #pdb.set_trace() + ##################################### + #Do authentication + #Check if authentication is turned on + ##################################### + lFlagAccessUserBlock=False + lAuthenticateDict = {"Domain": "", "User": ""} + if mGlobalDict.get("Server", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False): + lAuthenticateDict = AuthenticateVerify(self) + if not lAuthenticateDict["User"]: + lFlagAccessUserBlock=True + if lFlagAccessUserBlock: + AuthenticateBlock(self) + ##################################### + else: + #Мост между файлом и http запросом (новый формат) + if self.path == "/": + self.SendResponseContentTypeFile('text/html', "Web\\Index.xhtml") + #Мост между файлом и http запросом (новый формат) + if self.path == '/3rdParty/Semantic-UI-CSS-master/semantic.min.css': + self.SendResponseContentTypeFile('text/css',"..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.css") + #Мост между файлом и http запросом (новый формат) + if self.path == '/3rdParty/Semantic-UI-CSS-master/semantic.min.js': + self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.js") + #Мост между файлом и http запросом (новый формат) + if self.path == '/3rdParty/jQuery/jquery-3.1.1.min.js': + self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\jQuery\\jquery-3.1.1.min.js") + #Мост между файлом и http запросом (новый формат) + if self.path == '/3rdParty/Google/LatoItalic.css': + self.SendResponseContentTypeFile('font/css',"..\\Resources\\Web\\Google\\LatoItalic.css") + #Мост между файлом и http запросом (новый формат) + if self.path == '/3rdParty/Semantic-UI-CSS-master/themes/default/assets/fonts/icons.woff2': + self.SendResponseContentTypeFile('font/woff2',"..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default\\assets\\fonts\\icons.woff2") + #Мост между файлом и http запросом (новый формат) + if self.path == '/favicon.ico': + self.SendResponseContentTypeFile('image/x-icon',"Web\\favicon.ico") + #Мост между файлом и http запросом (новый формат) + if self.path == '/3rdParty/Handlebars/handlebars-v4.1.2.js': + self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\Handlebars\\handlebars-v4.1.2.js") + #Получить скриншот + if self.path.split("?")[0] == '/GetScreenshot': + #Сохранить файл на диск + SaveScreenshot("Screenshot.png") + self.SendResponseContentTypeFile('image/png',"Screenshot.png") + #Monitor + if self.path == '/Monitor/JSONDaemonListGet': + # Send response status code + self.send_response(200) + # Send headers + self.send_header('Content-type','application/json') + self.end_headers() + # Send message back to client + message = json.dumps(mGlobalDict) + # Write content as utf-8 data + self.wfile.write(bytes(message, "utf8")) + if self.path == '/Monitor/ControlPanelDictGet': + # Send response status code + self.send_response(200) + # Send headers + self.send_header('Content-type','application/json') + self.end_headers() + #Create result JSON + lResultJSON={"RenderRobotList":[]} + lRenderFunctionsRobotList=mGlobalDict["ControlPanelDict"]["RobotList"] + for lItem in lRenderFunctionsRobotList: + #Подключить модуль для вызова + #print(globals()) + lModuleImportName = lItem.get("RenderFunctionModuleSubfolderName","") + if lModuleImportName!="": + lModuleImportName = f'{lItem["RenderFunctionModuleSubfolderName"]}.{lItem["RenderFunctionModuleName"]}' + else: + lModuleImportName = lItem["RenderFunctionModuleName"] + lModule=importlib.import_module(lModuleImportName) + #Найти функцию + lFunction=getattr(lModule, lItem["RenderFunctionName"]) + #Выполнить вызов и записать результат + lItemResultDict=lFunction(mGlobalDict) + #RunFunction + lResultJSON["RenderRobotList"].append(lItemResultDict) + # Send message back to client + message = json.dumps(lResultJSON) + # Write content as utf-8 data + self.wfile.write(bytes(message, "utf8")) + #Filemanager function + if self.path.lower().startswith('/filemanager/'): + lFileURL=self.path[13:] + # check if file in FileURL - File Path Mapping Dict + if lFileURL.lower() in mGlobalDict["FileManager"]["FileURLFilePathDict"]: + self.SendResponseContentTypeFile('application/octet-stream',mGlobalDict["FileManager"]["FileURLFilePathDict"][lFileURL]) + # Auth function # POST def do_POST(self): - #Централизованная функция получения запросов/отправки - if self.path == '/Utils/Processor': - #ReadRequest - lInputObject={} - if self.headers.get('Content-Length') is not None: - lInputByteArrayLength = int(self.headers.get('Content-Length')) - lInputByteArray=self.rfile.read(lInputByteArrayLength) - #Превращение массива байт в объект - lInputObject=json.loads(lInputByteArray.decode('utf8')) - # Send response status code - self.send_response(200) - # Send headers - self.send_header('Content-type','application/json') - self.end_headers() - # Send message back to client - message = json.dumps(Processor.ActivityListOrDict(lInputObject)) - # Write content as utf-8 data - self.wfile.write(bytes(message, "utf8")) - return - -#print(ChildProcessReadWaitString(p)) + ##################################### + #Do authentication + #Check if authentication is turned on + ##################################### + lFlagAccessUserBlock=False + lAuthenticateDict = {"Domain": "", "User": ""} + if mGlobalDict.get("Server", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False): + lAuthenticateDict = AuthenticateVerify(self) + if not lAuthenticateDict["User"]: + lFlagAccessUserBlock=True + if lFlagAccessUserBlock: + AuthenticateBlock(self) + ##################################### + else: + #Централизованная функция получения запросов/отправки + if self.path == '/Utils/Processor': + #ReadRequest + lInputObject={} + if self.headers.get('Content-Length') is not None: + lInputByteArrayLength = int(self.headers.get('Content-Length')) + lInputByteArray=self.rfile.read(lInputByteArrayLength) + #Превращение массива байт в объект + lInputObject=json.loads(lInputByteArray.decode('utf8')) + # Send response status code + self.send_response(200) + # Send headers + self.send_header('Content-type','application/json') + self.end_headers() + # Send message back to client + message = json.dumps(Processor.ActivityListOrDict(lInputObject)) + # Write content as utf-8 data + self.wfile.write(bytes(message, "utf8")) + return \ No newline at end of file diff --git a/Orchestrator/Settings/Settings.py b/Orchestrator/Settings/Settings.py index e452fc47..0b4aa877 100644 --- a/Orchestrator/Settings/Settings.py +++ b/Orchestrator/Settings/Settings.py @@ -64,77 +64,111 @@ def CheckIfProcessRunning(processName): return False; #Orchestrator settings -mDict = { - "Server":{ - "ListenPort_":"Порт, по которому можно подключиться к демону", - "ListenPort":8081, - "ListenURLList":[ - { - "Description":"Local machine test", - "URL_":"Сетевое расположение сервера демона", - "URL":"http://127.0.0.1:8081" - } - ] - }, - "Scheduler":{ - "ActivityTimeCheckLoopSeconds":5, #Количество секунд, между циклами проверки действий - "ActivityTimeList":[ - { - "TimeHH:MM":"19:25", #Time [HH:MM] to trigger activity - "WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7] - "Activity":{ - "Type":"ProcessStart", #Activity type - "Path":"Notepad", #Executable file path - "ArgList": [] #List of the arguments +def Settings(): + mDict = { + "Server": { + "ListenPort_": "Порт, по которому можно подключиться к демону", + "ListenPort": 8081, + "ListenURLList": [ + { + "Description": "Local machine test", + "URL_": "Сетевое расположение сервера демона", + "URL": "http://127.0.0.1:8081" + } + ], + "AccessUsers": { + "FlagCredentialsAsk": True, #Turn on Authentication + "FlagNoRulesBlock": True, #Block request, if user has no rules for requested URL (if FlagCredentialAsk is turned on) + "UserDict": { + #("Domain", "User"): { + # "FunctionDict": { + # # ("Method|GET|POST", "Match type|BeginWith|Contains", "URL"): def function (inRequest, inGlobalDict, inAuthenticateDict) + # } + #} + }, + "AuthTokensDict": { + #"":{"User":"", "Domain":"", "TokenDatetime":} } - }, - { - "TimeHH:MM":"19:20", #Time [HH:MM] to trigger activity - "WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7] - "Activity":{ - "Type":"ProcessStop", #Activity type - "Name":"OpenRPARobotDaemon.exe", #Process name - "FlagForce":True, #Force process close - "User": "%username%" #Empty, user or %username% + } + }, + "Scheduler": { + "ActivityTimeCheckLoopSeconds":5, #Количество секунд, между циклами проверки действий + "ActivityTimeList":[ + { + "TimeHH:MM":"19:25", #Time [HH:MM] to trigger activity + "WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7] + "Activity":{ + "Type":"ProcessStart", #Activity type + "Path":"Notepad", #Executable file path + "ArgList": [] #List of the arguments + } + }, + { + "TimeHH:MM":"19:20", #Time [HH:MM] to trigger activity + "WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7] + "Activity":{ + "Type":"ProcessStop", #Activity type + "Name":"OpenRPARobotDaemon.exe", #Process name + "FlagForce":True, #Force process close + "User": "%username%" #Empty, user or %username% + } + }, + { + "TimeHH:MMStart":"19:20", #Time [HH:MM] to trigger activity + "TimeHH:MMStop":"19:20", + "ActivityIntervalSeconds":5, + "WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7] + "Activity":{ + "Type": "PythonStart", #Activity type + "ModuleName": "CheckActivity", #Python function module name + "FunctionName": "test_activity", #Python function name + "ArgList": ["TestArg1","TestArg2"] #Input python function args + } } - }, - { - "TimeHH:MMStart":"19:20", #Time [HH:MM] to trigger activity - "TimeHH:MMStop":"19:20", - "ActivityIntervalSeconds":5, - "WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7] - "Activity":{ - "Type": "PythonStart", #Activity type - "ModuleName": "CheckActivity", #Python function module name - "FunctionName": "test_activity", #Python function name - "ArgList": ["TestArg1","TestArg2"] #Input python function args + ], + "LogList":[] + }, + "Processor":{ + "LogType_CMDStart":True, #LogType_: if Trace for command is selected for False, the tracing will be off for such activity type. Default True + "LogList":[] #List of processor activity, which was executed. Fill list when orchestrator is running + }, + "ControlPanelDict":{ + "RefreshSeconds": 5, + "RobotList": [ + { + "RenderFunctionName":"RenderRobotR01", + "RenderFunctionModuleName":"Settings", + "RenderFunctionModuleSubfolderName":"Settings" } + ] + }, + "FileManager": { + "FileURLFilePathDict_help":"https://localhost:8081/filemanager/. All FileURL s must be set in lowercase", + "FileURLFilePathDict":{ + "r01/report.xlsx":"C:\\RPA\\R01_IntegrationOrderOut\\Data\\Reestr_otgruzok.xlsx" } - ], - "LogList":[] - }, - "Processor":{ - "LogType_CMDStart":True, #LogType_: if Trace for command is selected for False, the tracing will be off for such activity type. Default True - "LogList":[] #List of processor activity, which was executed. Fill list when orchestrator is running - }, - "ControlPanelDict":{ - "RefreshSeconds": 5, - "RobotList": [ - { - "RenderFunctionName":"RenderRobotR01", - "RenderFunctionModuleName":"Settings", - "RenderFunctionModuleSubfolderName":"Settings" - } - ] - }, - "FileManager": { - "FileURLFilePathDict_help":"https://localhost:8081/filemanager/. All FileURL s must be set in lowercase", - "FileURLFilePathDict":{ - "r01/report.xlsx":"C:\\RPA\\R01_IntegrationOrderOut\\Data\\Reestr_otgruzok.xlsx" - } - }, - "Storage":{ - "Robot_R01_help":"Robot data storage in orchestrator env", - "Robot_R01":{} - } -} \ No newline at end of file + }, + "Storage":{ + "Robot_R01_help":"Robot data storage in orchestrator env", + "Robot_R01":{} + } + } + ################################### + #Init .py files from Settings folder + #################################### + #Get file list from Settings folder + import os + import pdb + lSettingsPath = os.path.join(os.getcwd(), "Settings") + #pdb.set_trace() + #Lambda function to get files .py from settings folder except Settings.py + lFileList = [f for f in os.listdir(lSettingsPath) if os.path.isfile(os.path.join(lSettingsPath, f)) and f.split(".")[-1]=="py" and f != "Settings.py"] + #TODO Import files as python module and call SettingsUpdate() + import importlib.util + for lModuleFilePathItem in lFileList + spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py") + foo = importlib.util.module_from_spec(spec) + #spec.loader.exec_module(foo) + #foo.MyClass() + #Return Global settings + return mDict \ No newline at end of file diff --git a/Orchestrator/Settings/Test.py b/Orchestrator/Settings/Test.py new file mode 100644 index 00000000..0b9f3008 --- /dev/null +++ b/Orchestrator/Settings/Test.py @@ -0,0 +1,2 @@ +def SettingsUpdate(): + print("Everything is ok!") \ No newline at end of file