Compare commits

...

12 Commits

@ -44,7 +44,7 @@ else:
Orchestrator.OrchestratorLoggerGet().setLevel(logging.INFO)
# TEST Add User ND - Add Login ND to superuser of the Orchestrator
lUACClientDict = SettingsTemplate.__UACClientAdminCreate__()
gSettings["ServerDict"]["AccessUsers"]["FlagCredentialsAsk"]=False
gSettings["ServerDict"]["AccessUsers"]["FlagCredentialsAsk"]=True
Orchestrator.UACUpdate(inGSettings=gSettings, inADLoginStr="ND", inADStr="", inADIsDefaultBool=True, inURLList=[], inRoleHierarchyAllowedDict=lUACClientDict)
Orchestrator.UACUpdate(inGSettings=gSettings, inADLoginStr="rpa00", inADStr="", inADIsDefaultBool=True, inURLList=[], inRoleHierarchyAllowedDict=lUACClientDict)
# TEST Add User IMaslov - Add Login IMaslov to superuser of the Orchestrator

@ -18,9 +18,11 @@ from . import ServerBC
# объявление import
from fastapi import FastAPI, Form, Request, HTTPException, Depends, Header, Response, Body
from fastapi.responses import PlainTextResponse, HTMLResponse, FileResponse
from fastapi.responses import PlainTextResponse, HTMLResponse, FileResponse, RedirectResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.encoders import jsonable_encoder
from starlette.datastructures import MutableHeaders
from pydantic import BaseModel
import uvicorn
import io
@ -28,6 +30,7 @@ from starlette.responses import StreamingResponse
from typing import Union
from pyOpenRPA import __version__
import requests
import base64
import uuid
import datetime
@ -43,6 +46,8 @@ app = FastAPI(
swagger_ui_oauth2_redirect_url = "/orpa/fastapi/docs/oauth2-redirect",
)
def IdentifyAuthorize(inRequest:Request, inResponse:Response,
inCookiesStr: Union[str, None] = Header(default=None,alias="Cookie"),
inAuthorizationStr: Union[str, None] = Header(default="",alias="Authorization")):
@ -70,60 +75,62 @@ def IdentifyAuthorize(inRequest:Request, inResponse:Response,
return lCookieAuthToken
######################################
#Way 2 - try to logon
if len(lHeaderAuthorization) == 2:
llHeaderAuthorizationDecodedUserPasswordList = base64.b64decode(lHeaderAuthorization[1]).decode("utf-8").split(
":")
lUser = llHeaderAuthorizationDecodedUserPasswordList[0]
lPassword = llHeaderAuthorizationDecodedUserPasswordList[1]
lDomain = ""
if "\\" in lUser:
lDomain = lUser.split("\\")[0]
lUser = lUser.split("\\")[1]
elif "@" in lUser:
lDomain = lUser.split("@")[1]
lUser = lUser.split("@")[0]
lLogonBool = __Orchestrator__.OSCredentialsVerify(inUserStr=lUser, inPasswordStr=lPassword, inDomainStr=lDomain)
#Check result
if lLogonBool: # check user in gsettings rules
lLogonBool = False
gSettings = __Orchestrator__.GSettingsGet() # Set the global settings
lUserTurple = (lDomain.upper(),lUser.upper()) # Create turple key for inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"]
lUserTurple2 = ("",lUser.upper()) # Create turple key for inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"]
if lUserTurple in gSettings.get("ServerDict",{}).get("AccessUsers", {}).get("RuleDomainUserDict", {}): lLogonBool = True
elif lUserTurple2 in gSettings.get("ServerDict",{}).get("AccessUsers", {}).get("RuleDomainUserDict", {}): lLogonBool = True
if lLogonBool: # If user exists in UAC Dict
lResult["Domain"] = lDomain
lResult["User"] = lUser
#Create token
lAuthToken=str(uuid.uuid1())
__Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken] = {}
__Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["Domain"] = lResult["Domain"]
__Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["User"] = lResult["User"]
__Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["FlagDoNotExpire"] = False
__Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["TokenDatetime"] = datetime.datetime.now()
#Set-cookie
inResponse.set_cookie(key="AuthToken",value=lAuthToken)
mOpenRPA={}
mOpenRPA["AuthToken"] = lAuthToken
mOpenRPA["Domain"] = lResult["Domain"]
mOpenRPA["User"] = lResult["User"]
mOpenRPA["IsSuperToken"] = __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(mOpenRPA["AuthToken"], {}).get("FlagDoNotExpire", False)
return lAuthToken
#inRequest.OpenRPASetCookie = {}
#New engine of server
#inRequest.OpenRPAResponseDict["SetCookies"]["AuthToken"] = lAuthToken
else:
raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (для пользователя не заявлен доступ к оркестратору pyOpenRPA. Обратитесь в техническую поддержку)", headers={})
if lHeaderAuthorization != ['']:
if "AuthExc" in lCookies:
raise AuthException()
else:
raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (неверная пара логин / пароль)", headers={})
######################################
llHeaderAuthorizationDecodedUserPasswordList = base64.b64decode(lHeaderAuthorization[0]).decode("utf-8").split(":")
lUser = llHeaderAuthorizationDecodedUserPasswordList[0]
lPassword = llHeaderAuthorizationDecodedUserPasswordList[1]
lDomain = ""
if "\\" in lUser:
lDomain = lUser.split("\\")[0]
lUser = lUser.split("\\")[1]
elif "@" in lUser:
lDomain = lUser.split("@")[1]
lUser = lUser.split("@")[0]
lLogonBool = __Orchestrator__.OSCredentialsVerify(inUserStr=lUser, inPasswordStr=lPassword, inDomainStr=lDomain)
#Check result
if lLogonBool: # check user in gsettings rules
lLogonBool = False
gSettings = __Orchestrator__.GSettingsGet() # Set the global settings
lUserTurple = (lDomain.upper(),lUser.upper()) # Create turple key for inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"]
lUserTurple2 = ("",lUser.upper()) # Create turple key for inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"]
if lUserTurple in gSettings.get("ServerDict",{}).get("AccessUsers", {}).get("RuleDomainUserDict", {}): lLogonBool = True
elif lUserTurple2 in gSettings.get("ServerDict",{}).get("AccessUsers", {}).get("RuleDomainUserDict", {}): lLogonBool = True
if lLogonBool: # If user exists in UAC Dict
lResult["Domain"] = lDomain
lResult["User"] = lUser
#Create token
lAuthToken=str(uuid.uuid1())
__Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken] = {}
__Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["Domain"] = lResult["Domain"]
__Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["User"] = lResult["User"]
__Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["FlagDoNotExpire"] = False
__Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["TokenDatetime"] = datetime.datetime.now()
#Set-cookie
inResponse.set_cookie(key="AuthToken",value=lAuthToken)
mOpenRPA={}
mOpenRPA["AuthToken"] = lAuthToken
mOpenRPA["Domain"] = lResult["Domain"]
mOpenRPA["User"] = lResult["User"]
mOpenRPA["IsSuperToken"] = __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(mOpenRPA["AuthToken"], {}).get("FlagDoNotExpire", False)
raise ReloadPage(token=lAuthToken)
#inRequest.OpenRPASetCookie = {}
#New engine of server
#inRequest.OpenRPAResponseDict["SetCookies"]["AuthToken"] = lAuthToken
else:
errorMsg = "Попытка авторизации не прошла успешно (для пользователя не заявлен доступ к оркестратору pyOpenRPA. Обратитесь в техническую поддержку)"
raise ErrorException(text=errorMsg)
else:
errorMsg = "Попытка авторизации не прошла успешно (неверная пара логин / пароль)"
raise ErrorException(text=errorMsg)
else:
raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (неполная пара логин / пароль)", headers={'Content-type':'text/html', 'WWW-Authenticate':'Basic'})
raise AuthException()
else: return None # Credentials are not required - return none
# Перевод встроенных fastapi функций на авторизацию
lRouteList =[]
for lItem in app.router.routes:
lRouteList.append(lItem)
@ -137,6 +144,49 @@ for lItem in lRouteList:
tags=["FastAPI"]
)
# объявление классов для дальнейшей обработки вызываемых исключений (обязательно должны наследоваться от EXception)
class ErrorException(Exception):
def __init__(self, text :str, name: str="AuthExc"):
self.name = name
self.text = text
class AuthException(Exception):
def __init__(self, name: str="AuthTryWindowCreate"):
self.name = name
class ReloadPage(Exception):
def __init__(self, token :str, name: str="AuthToken"):
self.name = name
self.token = token
templates = Jinja2Templates(directory=CrossOS.PathJoinList(CrossOS.PathSplitList(__file__)[:-2] + ["Resources","Web","orpa"]))
# Обработчик ошибки авторизации (вывод информации о причинах неудачной авторизации)
@app.exception_handler(ErrorException)
async def unicorn_exception_handler(request: Request, exc:ErrorException):
response = templates.TemplateResponse(status_code=401,name="badAuth.xhtml", context={"request":request, "errorMsg":exc.text, "title":"ОРКЕСТРАТОР PYOPENRPA", "subtitle":"АВТОРИЗАЦИЯ", "version":__version__})
response.set_cookie(key="AuthExc",value="True")
return response
# Обработчик успешной попытки авторизации (обновление страницы + установки куки-токена)
@app.exception_handler(ReloadPage)
async def unicorn_exception_handler_3(request: Request, exc:ReloadPage):
response = HTMLResponse(content="", status_code=200)
response.set_cookie(key=exc.name, value=exc.token)
try:response.delete_cookie(key="AuthExc")
except Exception:pass
return response
# Обработчик попытки авторизации (отвечает за рендер формы для ввода пары логин / пароль)
@app.exception_handler(AuthException)
def unicorn_exception_handler_2(request: Request, exc: AuthException):
response = templates.TemplateResponse(status_code=401,name="auth.xhtml", context={"request":request, "title":"ОРКЕСТРАТОР PYOPENRPA", "subtitle":"АВТОРИЗАЦИЯ", "version":__version__})
try:response.delete_cookie(key="AuthExc")
except Exception:pass
return response
from . import ServerSettings
@ -185,13 +235,17 @@ def InitFastAPI():
ServerSettings.SettingsUpdate()
BCURLUpdate()
def BCURLUpdate():
def BCURLUpdate(inExceptionFlagBool:bool=True):
for lConnectItemDict in __Orchestrator__.GSettingsGet()["ServerDict"]["URLList"]:
if "BCBool" not in lConnectItemDict:
if "ResponseFolderPath" in lConnectItemDict:
app.mount(lConnectItemDict["URL"],
StaticFiles(directory=CrossOS.PathStr(lConnectItemDict["ResponseFolderPath"])),
name=lConnectItemDict["URL"].replace('/',"_"))
try:
app.mount(lConnectItemDict["URL"],
StaticFiles(directory=CrossOS.PathStr(lConnectItemDict["ResponseFolderPath"])),
name=lConnectItemDict["URL"].replace('/',"_"))
except:
if inExceptionFlagBool: raise RuntimeError("Fatal error. Bad FolderPath")
else: pass
else:
if lConnectItemDict.get("MatchType") in ["BeginWith", "EqualCase", "Equal","EqualNoParam"]:
if lConnectItemDict.get("UACBool",True):

@ -498,7 +498,7 @@ def SettingsUpdate():
#}
#Orchestrator basic dependencies # Index page in server.py because of special settings
{"Method":"GET", "URL": gSettingsDict["ServerDict"]["URLIndexStr"],"MatchType": "EqualNoParam", "ResponseDefRequestGlobal": pyOpenRPA_Index},
{"Method":"GET", "URL": "/metadata.json", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, CrossOS.PathStr("..\\Resources\\Web\\orpa\\metadata.json")), "ResponseContentType": "application/json"},
{"Method":"GET", "URL": "/metadata.json", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, CrossOS.PathStr("..\\Resources\\Web\\orpa\\metadata.json")), "ResponseContentType": "application/json","UACBool":False,},
#{"Method":"GET", "URL": "/Index.js", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "Web\\Index.js"), "ResponseContentType": "text/javascript"},
{"Method":"GET", "URL": "/orpa/resources/", "MatchType": "BeginWith", "ResponseFolderPath": os.path.join(lOrchestratorFolder, CrossOS.PathStr("..\\Resources")),"UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/orpa/client/resources/", "MatchType": "BeginWith", "ResponseFolderPath": os.path.join(lOrchestratorFolder, "Web"),"UACBool":False, "UseCacheBool": True},

@ -623,6 +623,14 @@ def OrchestratorIsAdmin():
elif CrossOS.IS_LINUX_BOOL: return os.getuid()==0
else: return True
def OrchestratorIsCredentialsAsk():
"""L+,W+: Проверить, активирована ли авторизация при переходе к Оркестратору.
:return: True - Активирована; False - Деактивирована
"""
inGSettings = GSettingsGet()
return inGSettings["ServerDict"]["AccessUsers"]["FlagCredentialsAsk"]
def OrchestratorIsInited() -> bool:
"""L+,W+: Проверить, было ли проинициализировано ядро Оркестратора
@ -1069,7 +1077,12 @@ def WebUserLoginGet(inAuthTokenStr: str=None) -> str:
:return: Логин пользователя
:rtype: str
"""
if inAuthTokenStr is None: return None
isCredentialAsk = OrchestratorIsCredentialsAsk()
if isCredentialAsk:
if inAuthTokenStr is None: raise ConnectionError("Не удается получить токен для авторизации")
else:
if inAuthTokenStr is None: return None
inGS = GSettingsGet() # Get the global settings
return inGS.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(inAuthTokenStr, {}).get("User", None)
@ -1081,18 +1094,22 @@ def WebUserDomainGet(inAuthTokenStr: str=None) -> str:
:return: Домен пользователя
:rtype: str
"""
if inAuthTokenStr is None: return None
isCredentialAsk = OrchestratorIsCredentialsAsk()
if isCredentialAsk:
if inAuthTokenStr is None: raise ConnectionError("Не удается получить токен для авторизации")
else:
if inAuthTokenStr is None: return None
inGS = GSettingsGet() # Get the global settings
return inGS.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(inAuthTokenStr, {}).get("Domain", None)
def WebUserInfoGet(inAuthTokenStr=None):
"""L+,W+: Информация о пользователе, который отправил HTTP запрос.
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
:return: Сведения в формате {"DomainUpperStr": "PYOPENRPA", "UserNameUpperStr": "IVAN.MASLOV"}
"""
try:
lResultDict = {
"DomainUpperStr": WebUserDomainGet(inAuthTokenStr=inAuthTokenStr).upper(),
@ -1109,6 +1126,7 @@ def WebUserIsSuperToken(inAuthTokenStr: str=None):
:type inAuthTokenStr: str, опционально
:return: True - является супертокеном; False - не является супертокеном; None - авторизация не производилась
"""
if inAuthTokenStr is None: return None
inGSettings = GSettingsGet() # Get the global settings
lIsSuperTokenBool = False
@ -1123,7 +1141,13 @@ def WebUserUACHierarchyGet(inAuthTokenStr: str=None) -> dict:
:type inAuthTokenStr: str, опционально
:return: UAC словарь доступа или {}, что означает полный доступ
"""
if inAuthTokenStr is None: return {}
isCredentialAsk = OrchestratorIsCredentialsAsk()
if isCredentialAsk:
if inAuthTokenStr is None: raise ConnectionError("Не удается получить токен для авторизации")
else:
if inAuthTokenStr is None: return {}
lDomainUpperStr = WebUserDomainGet(inAuthTokenStr=inAuthTokenStr).upper()
lUserUpperStr = WebUserLoginGet(inAuthTokenStr=inAuthTokenStr).upper()
if lUserUpperStr is None: return {}
@ -1137,7 +1161,14 @@ def WebUserUACCheck(inAuthTokenStr:str=None, inKeyList:list=None) -> bool:
:return: True - доступ имеется, False - доступа нет
:rtype: bool
"""
if inAuthTokenStr is None: return True # Если авторизации не происходило - супердоступ
isCredentialAsk = OrchestratorIsCredentialsAsk()
# Если авторизации не происходило - супердоступ
if isCredentialAsk:
if inAuthTokenStr is None: return False
else:
if inAuthTokenStr is None: return True
lResult = True # Init flag
lRoleHierarchyDict = WebUserUACHierarchyGet(inAuthTokenStr=inAuthTokenStr) # get the Hierarchy
# Try to get value from key list
@ -1192,13 +1223,14 @@ def WebURLConnectDef(inMethodStr, inURLStr, inMatchTypeStr, inDef, inContentType
Server.BCURLUpdate()
def WebURLConnectFolder(inMethodStr, inURLStr, inMatchTypeStr, inFolderPathStr, inGSettings = None, inUACBool = None, inUseCacheBool= False):
def WebURLConnectFolder(inMethodStr, inURLStr, inMatchTypeStr, inFolderPathStr, inExceptionFlagBool=False, inGSettings = None, inUACBool = None, inUseCacheBool= False):
"""L+,W+: Подключить папку к URL.
:param inMethodStr: Метод доступа по URL "GET" || "POST"
:param inURLStr: URL адрес. Пример "/index"
:param inMatchTypeStr: Тип соответсвия строки URL с inURLStr: "BeginWith" || "Contains" || "Equal" || "EqualCase" || "EqualNoParam"
:param inFolderPathStr: Путь к папке на диске, в которой искать файл и возвращать пользователю по HTTP
:param inExceptionFlagBool: Флаг на обработку ошибки. True - показывать ошибку в терминале (остановка инициализации), False - не показывать
:param inUACBool: True - Выполнять проверку прав доступа пользователя перед отправкой ответа; False - не выполнять проверку прав доступа пользователя
:param inUseCacheBool: True - выполнить кэширование страницы, чтобы в следющих запросах открыть быстрее; False - не кэшировать
:param inGSettings: Глобальный словарь настроек Оркестратора (синглтон)
@ -1220,7 +1252,7 @@ def WebURLConnectFolder(inMethodStr, inURLStr, inMatchTypeStr, inFolderPathStr,
"UseCacheBool": inUseCacheBool
}
inGSettings["ServerDict"]["URLList"].append(lURLItemDict)
Server.BCURLUpdate()
Server.BCURLUpdate(inExceptionFlagBool)
def WebURLConnectFile(inMethodStr, inURLStr, inMatchTypeStr, inFilePathStr, inContentTypeStr=None, inGSettings = None, inUACBool = None, inUseCacheBool = False):

@ -0,0 +1,142 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<!-- Yandex.Metrika counter -->
<script async="" src="https://mc.yandex.ru/metrika/tag.js"></script>
<script type="text/javascript">
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
ym(88079149, "init", {
clickmap:true,
trackLinks:true,
accurateTrackBounce:true,
webvisor:true
});
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/88079149" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
<!-- /Yandex.Metrika counter -->
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Оркестратор pyOpenRPA</title>
<meta name="description" content="Ведущий RPA разработчик российского программного обеспечения. RPA платформа позволяет решать любые бизнес-задачи. Комплексное решение от компании RPA pyOpenRPA. Первое открытое российское RPA решение для крупного / среднего / малого бизнеса. Доступная автоматизация для каждого." />
<meta name="keywords" content="rpa, программные роботы, автоматизация бизнес-процессов, цифровые сотрудники, виртуальные сотрудники" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/semantic.min.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/orpa/styleset/home.css" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<script
src="/orpa/resources/Web/jQuery/jquery-3.1.1.min.js"
crossorigin="anonymous"></script>
<script src="/orpa/resources/Web/Semantic-UI-CSS-master/semantic.min.js"></script>
<script src="/orpa/resources/Web/Handlebars/handlebars-v4.1.2.js"></script>
</head>
<style type="text/css">
h4 {
display: inline-block;
}
button.ui.green.button {
font-family:'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
background: #368279
}
</style>
<body>
{% include 'header.xhtml' %}
<center>
<div class="ui placeholder segment">
<div class="ui icon header">
<i class="user icon"></i>
Для доступа к панели управления, пожалуйста, пройдите авторизацию
</div>
<div class="inline">
<br />
<div class="head"><h4 class="ui header">
<div align="left">Логин от учетной записи</div>
<div class="ui input">
<input type="text" class="headIn" name="uname" style="width: 400px;" autocomplete="off" onchange="inputFormCheck()"/>
</div>
</h4></div>
<br />
<div class="head"><h4 class="ui header">
<div align="left">Пароль от учетной записи</div>
<div class="ui input">
<input type="password" class="headIn" id="pasInp" name="pasw" style="width: 400px;" autocomplete="off" onchange="inputFormCheck()" />
</div>
</h4></div>
<div class="pasChbView" align="left">
<label><input type="checkbox" class="pasChb" /> Показать пароль</label>
</div>
<br />
<button class="ui green button" id="logBtn" onclick="do_auth()" style="width: 200px;">Войти</button>
</div>
</div>
</center>
{% include 'footer.xhtml' %}
<script type="text/javascript">
async function do_auth() {
var uname = document.getElementsByClassName("headIn")[0].value
var psw = document.getElementsByClassName("headIn")[1].value
var authByteArray = (uname+":"+psw).toString('utf8')
getText(authByteArray);
}
async function getText(authByteArray) {
const response = await fetch("/",{
method: "GET",
headers: { "Accept": "application/json", "Content-Type": "application/json", "Authorization":btoa(authByteArray)},
credentials: "include"
});
const responseText = await response.text();
if (response.ok) {
location.reload()
}
else {
document.querySelector('html').innerHTML = responseText
}
}
function inputFormCheck() {
var inputForms = document.getElementsByClassName("headIn")
var flag = false
for (var i = 0; i < inputForms.length; i++) {
result = inputForms[i].value.match(/[А-Я]/gi)
if (result) {
flag = true
document.getElementsByClassName("headIn")[i].style.backgroundColor = "#F08080"
}
else {
document.getElementsByClassName("headIn")[i].style.backgroundColor = "white"
}
}
if (flag) {
document.getElementById("logBtn").disabled = true
}
else {
document.getElementById("logBtn").disabled = false
}
}
$('body').on('click', '.pasChb', function(){
if ($(this).is(':checked')){
$('#pasInp').attr('type', 'text');
} else {
$('#pasInp').attr('type', 'password');
}
});
</script>
</body>
<style type="text/css">
.ui.footer.segment {
margin: 0em 0em 0em;
padding: 5.05em 0em;
}
</style>
</html>

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<!-- Yandex.Metrika counter -->
<script async="" src="https://mc.yandex.ru/metrika/tag.js"></script>
<script type="text/javascript">
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
ym(88079149, "init", {
clickmap:true,
trackLinks:true,
accurateTrackBounce:true,
webvisor:true
});
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/88079149" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
<!-- /Yandex.Metrika counter -->
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Оркестратор pyOpenRPA</title>
<meta name="description" content="Ведущий RPA разработчик российского программного обеспечения. RPA платформа позволяет решать любые бизнес-задачи. Комплексное решение от компании RPA pyOpenRPA. Первое открытое российское RPA решение для крупного / среднего / малого бизнеса. Доступная автоматизация для каждого." />
<meta name="keywords" content="rpa, программные роботы, автоматизация бизнес-процессов, цифровые сотрудники, виртуальные сотрудники" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/semantic.min.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/orpa/styleset/home.css" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<script
src="/orpa/resources/Web/jQuery/jquery-3.1.1.min.js"
crossorigin="anonymous"></script>
<script src="/orpa/resources/Web/Semantic-UI-CSS-master/semantic.min.js"></script>
<script src="/orpa/resources/Web/Handlebars/handlebars-v4.1.2.js"></script>
</head>
<style type="text/css">
button.ui.green.button {
font-family:'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
background: #368279
}
</style>
<body>
{% include 'header.xhtml' %}
<br /><br /><br /><br /><br />
<center>
<div class="ui message">
<div class="header">Внимание</div>
<p>{{errorMsg}}</p>
</div>
<button class="ui green button" onclick="document.location='/'" style="width: 200px;">Повторить попытку</button>
</center>
{% include 'footer.xhtml' %}
</body>
<style type="text/css">
.ui.footer.segment {
margin: 10.7em 0em 0em;
padding: 5em 0em;
}
</style>
</html>

@ -1,21 +1,21 @@
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/reset.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/site.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/reset.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/site.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/container.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/grid.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/header.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/image.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/menu.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/container.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/grid.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/header.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/image.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/menu.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/divider.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/dropdown.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/segment.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/button.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/list.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/icon.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/sidebar.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/transition.css">
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/divider.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/dropdown.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/segment.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/button.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/list.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/icon.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/sidebar.css" />
<link rel="stylesheet" type="text/css" href="/orpa/resources/Web/Semantic-UI-CSS-master/components/transition.css" />
<div class="pusher tag-top">

@ -120,7 +120,7 @@ def UIOSelector_Get_UIOList (inSpecificationList,inElement=None,inFlagRaiseExcep
:type inSpecificationList: list, обязательный
:param inElement: Родительский элемент, от которого выполнить поиск UIO объектов по заданному UIO селектору. Если аргумент не задан, платформа выполнит поиск UIO объектов среди всех доступных приложений windows, которые запущены на текущей сессии
:type inElement: UIO объект, опциональный
:param inFlagRaiseException: True - формировать ошибку exception, если платформа не обнаружина ни одного UIO объекта по заданному UIO селектору. False - обратный случай. По умолчанию True
:param inFlagRaiseException: True - формировать ошибку exception, если платформа не обнаружина ни одного UIO объекта по заданному UIO селектору. False - обратный случай (может привести к ошибочным результатам). По умолчанию True.
:type inFlagRaiseException: bool, опциональный
:return: Список UIO объектов, которые удовлетворяют условиям UIO селектора
'''
@ -128,123 +128,127 @@ def UIOSelector_Get_UIOList (inSpecificationList,inElement=None,inFlagRaiseExcep
inSpecificationList=copy.deepcopy(inSpecificationList)
lResultList=[]
lChildrenList=[]
#Получить родительский объект если на вход ничего не поступило
if inElement is None:
#сформировать спецификацию на получение элемента
lRootElementSpecification=[inSpecificationList[0]]
lRootElementList=PWASpecification_Get_UIO(lRootElementSpecification)
for lRootItem in lRootElementList:
if lRootItem is not None:
lChildrenList.append(lRootItem.wrapper_object())
#Елемент на вход поступил - выполнить его анализ
else:
#Получить список элементов
lElementChildrenList=inElement.children()
#Поступил index - точное добавление
if 'index' in inSpecificationList[0]:
if inSpecificationList[0]['index']<len(lElementChildrenList):
#Получить дочерний элемент - точное добавление
lChildrenList.append(lElementChildrenList[inSpecificationList[0]['index']])
else:
if inFlagRaiseException:
raise ValueError('Object has no children with index: ' + str(inSpecificationList[0]['index']))
#Поступил ctrl_index - точное добавление
elif 'ctrl_index' in inSpecificationList[0]:
if inSpecificationList[0]['ctrl_index']<len(lElementChildrenList):
#Получить дочерний элемент
lChildrenList.append(lElementChildrenList[inSpecificationList[0]['ctrl_index']])
try:
#Получить родительский объект если на вход ничего не поступило
if inElement is None:
#сформировать спецификацию на получение элемента
lRootElementSpecification=[inSpecificationList[0]]
lRootElementList=PWASpecification_Get_UIO(lRootElementSpecification)
for lRootItem in lRootElementList:
if lRootItem is not None:
lChildrenList.append(lRootItem.wrapper_object())
#Елемент на вход поступил - выполнить его анализ
else:
#Получить список элементов
lElementChildrenList=inElement.children()
#Поступил index - точное добавление
if 'index' in inSpecificationList[0]:
if inSpecificationList[0]['index']<len(lElementChildrenList):
#Получить дочерний элемент - точное добавление
lChildrenList.append(lElementChildrenList[inSpecificationList[0]['index']])
else:
if inFlagRaiseException:
raise ValueError('Object has no children with index: ' + str(inSpecificationList[0]['index']))
#Поступил ctrl_index - точное добавление
elif 'ctrl_index' in inSpecificationList[0]:
if inSpecificationList[0]['ctrl_index']<len(lElementChildrenList):
#Получить дочерний элемент
lChildrenList.append(lElementChildrenList[inSpecificationList[0]['ctrl_index']])
else:
if inFlagRaiseException:
raise ValueError('Object has no children with index: ' + str(inSpecificationList[0]['ctrl_index']))
#Если нет точного обозначения элемента
else:
if inFlagRaiseException:
raise ValueError('Object has no children with index: ' + str(inSpecificationList[0]['ctrl_index']))
#Если нет точного обозначения элемента
lFlagGoCheck=True
#Учесть поле depth_start (если указано)
if 'depth_start' in inSpecificationList[0]:
if inSpecificationList[0]["depth_start"]>1:
lFlagGoCheck=False
#Циклический обход по детям, на предмет соответствия всем условиям
for lChildrenItem in lElementChildrenList:
#Обработка глубины depth (рекурсивный вызов для всех детей с занижением индекса глубины)
#По умолчанию значение глубины 1
if 'depth_end' in inSpecificationList[0]:
if inSpecificationList[0]['depth_end']>1:
#Подготовка новой версии спецификации
lChildrenItemNewSpecificationList=inSpecificationList.copy()
lChildrenItemNewSpecificationList[0]=lChildrenItemNewSpecificationList[0].copy()
lChildrenItemNewSpecificationList[0]["depth_end"]=lChildrenItemNewSpecificationList[0]["depth_end"]-1
if 'depth_start' in lChildrenItemNewSpecificationList[0]:
lChildrenItemNewSpecificationList[0]["depth_start"]=lChildrenItemNewSpecificationList[0]["depth_start"]-1
#Циклический вызов для всех детей со скорректированной спецификацией
lResultList.extend(UIOSelector_Get_UIOList(lChildrenItemNewSpecificationList,lChildrenItem,inFlagRaiseException))
#Фильтрация
#TODO Сделать поддержку этих атрибутов для первого уровня селектора
if lFlagGoCheck:
lFlagAddChild=True
#Фильтрация по title
if 'title' in inSpecificationList[0]:
if lChildrenItem.element_info.name != inSpecificationList[0]["title"]:
lFlagAddChild=False
#Фильтрация по title_re (regexp)
if 'title_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["title_re"],lChildrenItem.element_info.name) is None:
lFlagAddChild=False
#Фильтрация по rich_text
if 'rich_text' in inSpecificationList[0]:
if lChildrenItem.element_info.rich_text != inSpecificationList[0]["rich_text"]:
lFlagAddChild=False
#Фильтрация по rich_text_re (regexp)
if 'rich_text_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["rich_text_re"],lChildrenItem.element_info.rich_text) is None:
lFlagAddChild=False
#Фильтрация по class_name
if 'class_name' in inSpecificationList[0]:
if lChildrenItem.element_info.class_name != inSpecificationList[0]["class_name"]:
lFlagAddChild=False
#Фильтрация по class_name_re (regexp)
if 'class_name_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["class_name_re"],lChildrenItem.element_info.class_name) is None:
lFlagAddChild=False
#Фильтрация по friendly_class_name
if 'friendly_class_name' in inSpecificationList[0]:
if lChildrenItem.friendly_class_name() != inSpecificationList[0]["friendly_class_name"]:
lFlagAddChild=False
#Фильтрация по friendly_class_name_re (regexp)
if 'friendly_class_name_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["friendly_class_name_re"],lChildrenItem.friendly_class_name) is None:
lFlagAddChild=False
#Фильтрация по control_type
if 'control_type' in inSpecificationList[0]:
if lChildrenItem.element_info.control_type != inSpecificationList[0]["control_type"]:
lFlagAddChild=False
#Фильтрация по control_type_re (regexp)
if 'control_type_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["control_type_re"],lChildrenItem.element_info.control_type) is None:
lFlagAddChild=False
#Фильтрация по is_enabled (bool)
if 'is_enabled' in inSpecificationList[0]:
if lChildrenItem.is_enabled()!=inSpecificationList[0]["is_enabled"]:
lFlagAddChild=False
#Фильтрация по is_visible (bool)
if 'is_visible' in inSpecificationList[0]:
if lChildrenItem.is_visible()!=inSpecificationList[0]["is_visible"]:
lFlagAddChild=False
#####
#Все проверки пройдены - флаг добавления
if lFlagAddChild:
lChildrenList.append(lChildrenItem)
#Выполнить рекурсивный вызов (уменьшение количества спецификаций), если спецификация больше одного элемента
#????????Зачем в условии ниже is not None ???????????
if len(inSpecificationList)>1 and len(lChildrenList)>0:
#Вызвать рекурсивно функцию получения следующего объекта, если в спецификации есть следующий объект
for lChildElement in lChildrenList:
lResultList.extend(UIOSelector_Get_UIOList(inSpecificationList[1:],lChildElement,inFlagRaiseException))
else:
lFlagGoCheck=True
#Учесть поле depth_start (если указано)
if 'depth_start' in inSpecificationList[0]:
if inSpecificationList[0]["depth_start"]>1:
lFlagGoCheck=False
#Циклический обход по детям, на предмет соответствия всем условиям
for lChildrenItem in lElementChildrenList:
#Обработка глубины depth (рекурсивный вызов для всех детей с занижением индекса глубины)
#По умолчанию значение глубины 1
if 'depth_end' in inSpecificationList[0]:
if inSpecificationList[0]['depth_end']>1:
#Подготовка новой версии спецификации
lChildrenItemNewSpecificationList=inSpecificationList.copy()
lChildrenItemNewSpecificationList[0]=lChildrenItemNewSpecificationList[0].copy()
lChildrenItemNewSpecificationList[0]["depth_end"]=lChildrenItemNewSpecificationList[0]["depth_end"]-1
if 'depth_start' in lChildrenItemNewSpecificationList[0]:
lChildrenItemNewSpecificationList[0]["depth_start"]=lChildrenItemNewSpecificationList[0]["depth_start"]-1
#Циклический вызов для всех детей со скорректированной спецификацией
lResultList.extend(UIOSelector_Get_UIOList(lChildrenItemNewSpecificationList,lChildrenItem,inFlagRaiseException))
#Фильтрация
#TODO Сделать поддержку этих атрибутов для первого уровня селектора
if lFlagGoCheck:
lFlagAddChild=True
#Фильтрация по title
if 'title' in inSpecificationList[0]:
if lChildrenItem.element_info.name != inSpecificationList[0]["title"]:
lFlagAddChild=False
#Фильтрация по title_re (regexp)
if 'title_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["title_re"],lChildrenItem.element_info.name) is None:
lFlagAddChild=False
#Фильтрация по rich_text
if 'rich_text' in inSpecificationList[0]:
if lChildrenItem.element_info.rich_text != inSpecificationList[0]["rich_text"]:
lFlagAddChild=False
#Фильтрация по rich_text_re (regexp)
if 'rich_text_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["rich_text_re"],lChildrenItem.element_info.rich_text) is None:
lFlagAddChild=False
#Фильтрация по class_name
if 'class_name' in inSpecificationList[0]:
if lChildrenItem.element_info.class_name != inSpecificationList[0]["class_name"]:
lFlagAddChild=False
#Фильтрация по class_name_re (regexp)
if 'class_name_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["class_name_re"],lChildrenItem.element_info.class_name) is None:
lFlagAddChild=False
#Фильтрация по friendly_class_name
if 'friendly_class_name' in inSpecificationList[0]:
if lChildrenItem.friendly_class_name() != inSpecificationList[0]["friendly_class_name"]:
lFlagAddChild=False
#Фильтрация по friendly_class_name_re (regexp)
if 'friendly_class_name_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["friendly_class_name_re"],lChildrenItem.friendly_class_name) is None:
lFlagAddChild=False
#Фильтрация по control_type
if 'control_type' in inSpecificationList[0]:
if lChildrenItem.element_info.control_type != inSpecificationList[0]["control_type"]:
lFlagAddChild=False
#Фильтрация по control_type_re (regexp)
if 'control_type_re' in inSpecificationList[0]:
if re.fullmatch(inSpecificationList[0]["control_type_re"],lChildrenItem.element_info.control_type) is None:
lFlagAddChild=False
#Фильтрация по is_enabled (bool)
if 'is_enabled' in inSpecificationList[0]:
if lChildrenItem.is_enabled()!=inSpecificationList[0]["is_enabled"]:
lFlagAddChild=False
#Фильтрация по is_visible (bool)
if 'is_visible' in inSpecificationList[0]:
if lChildrenItem.is_visible()!=inSpecificationList[0]["is_visible"]:
lFlagAddChild=False
#####
#Все проверки пройдены - флаг добавления
if lFlagAddChild:
lChildrenList.append(lChildrenItem)
#Выполнить рекурсивный вызов (уменьшение количества спецификаций), если спецификация больше одного элемента
#????????Зачем в условии ниже is not None ???????????
if len(inSpecificationList)>1 and len(lChildrenList)>0:
#Вызвать рекурсивно функцию получения следующего объекта, если в спецификации есть следующий объект
for lChildElement in lChildrenList:
lResultList.extend(UIOSelector_Get_UIOList(inSpecificationList[1:],lChildElement,inFlagRaiseException))
else:
lResultList.extend(lChildrenList)
#Условие, если результирующий список пустой и установлен флаг создания ошибки (и inElement is None - не следствие рекурсивного вызова)
if inElement is None and len(lResultList)==0 and inFlagRaiseException:
raise pywinauto.findwindows.ElementNotFoundError("Robot can't find element by the UIOSelector")
return lResultList
lResultList.extend(lChildrenList)
#Условие, если результирующий список пустой и установлен флаг создания ошибки (и inElement is None - не следствие рекурсивного вызова)
if inElement is None and len(lResultList)==0 and inFlagRaiseException:
raise pywinauto.findwindows.ElementNotFoundError("Robot can't find element by the UIOSelector")
return lResultList
except Exception as e:
if inFlagRaiseException: raise e
else: return []
#old:PywinautoExtElementGet
def UIOSelector_Get_UIO (inSpecificationList,inElement=None,inFlagRaiseException=True):
@ -268,16 +272,13 @@ def UIOSelector_Get_UIO (inSpecificationList,inElement=None,inFlagRaiseException
'''
lResult=None
#Получить родительский объект если на вход ничего не поступило
lResultList=UIOSelector_Get_UIOList(inSpecificationList,inElement,False)
lResultList=UIOSelector_Get_UIOList(inSpecificationList,inElement,inFlagRaiseException)
if len(lResultList)>0:
lResult=lResultList[0]
#Условие, если результирующий список пустой и установлен флаг создания ошибки (и inElement is None - не следствие рекурсивного вызова)
if lResult is None and inFlagRaiseException:
raise pywinauto.findwindows.ElementNotFoundError("Robot can't find element by the UIOSelector")
return lResult
#old:-
def UIOSelector_Exist_Bool (inUIOSelector):
def UIOSelector_Exist_Bool (inUIOSelector, inFlagRaiseException=True):
'''L-,W+: Проверить существование хотя бы 1-го UIO объекта по заданному UIO селектору
!ВНИМАНИЕ! ДАННАЯ ФУНКЦИОНАЛЬНОСТЬ В АВТОМАТИЧЕСКОМ РЕЖИМЕ ПОДДЕРЖИВАЕТ ВСЕ РАЗРЯДНОСТИ ПРИЛОЖЕНИЙ (32|64), КОТОРЫЕ ЗАПУЩЕНЫ В СЕСИИ. PYTHON x64 ИМЕЕТ ВОЗМОЖНОСТЬ ВЗЗАИМОДЕЙСТВИЯ С x32 UIO ОБЪЕКТАМИ, НО МЫ РЕКОМЕНДУЕМ ДОПОЛНИТЕЛЬНО ИСПОЛЬЗОВАТЬ ИНТЕРПРЕТАТОР PYTHON x32 (ПОДРОБНЕЕ СМ. ФУНКЦИЮ Configure())
@ -292,6 +293,8 @@ def UIOSelector_Exist_Bool (inUIOSelector):
:param inUIOSelector: UIO Селектор, который определяет критерии поиска UIO объектов
:type inUIOSelector: list, обязательный
:param inFlagRaiseException: True - формировать ошибку exception, если платформа не обнаружина ни одного UIO объекта по заданному UIO селектору. False - обратный случай (может привести к ошибочным результатам). По умолчанию True.
:type inFlagRaiseException: bool, опциональный
:return: True - существует хотя бы 1 UIO объект. False - не существует ни одного UIO объекта по заданному UIO селектору
'''
lResult=False
@ -299,7 +302,9 @@ def UIOSelector_Exist_Bool (inUIOSelector):
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
#Получить родительский объект если на вход ничего не поступило
lResultList=UIOSelector_Get_UIOList(inUIOSelector, None, False)
try:
lResultList=UIOSelector_Get_UIOList(inUIOSelector, None, inFlagRaiseException)
except pywinauto.findwindows.ElementNotFoundError: return False
if len(lResultList)>0:
lResult=True
else:
@ -319,7 +324,7 @@ def UIOSelector_Exist_Bool (inUIOSelector):
return lResult
#old: -
def UIOSelectorsSecs_WaitAppear_List (inSpecificationListList,inWaitSecs=86400.0,inFlagWaitAllInMoment=False):
def UIOSelectorsSecs_WaitAppear_List (inSpecificationListList,inWaitSecs=86400.0,inFlagWaitAllInMoment=False, inFlagRaiseException=True):
'''L-,W+: Ожидать появление хотя бы 1-го / всех UIO объектов по заданным UIO селекторам
!ВНИМАНИЕ! ДАННАЯ ФУНКЦИОНАЛЬНОСТЬ В АВТОМАТИЧЕСКОМ РЕЖИМЕ ПОДДЕРЖИВАЕТ ВСЕ РАЗРЯДНОСТИ ПРИЛОЖЕНИЙ (32|64), КОТОРЫЕ ЗАПУЩЕНЫ В СЕСИИ. PYTHON x64 ИМЕЕТ ВОЗМОЖНОСТЬ ВЗЗАИМОДЕЙСТВИЯ С x32 UIO ОБЪЕКТАМИ, НО МЫ РЕКОМЕНДУЕМ ДОПОЛНИТЕЛЬНО ИСПОЛЬЗОВАТЬ ИНТЕРПРЕТАТОР PYTHON x32 (ПОДРОБНЕЕ СМ. ФУНКЦИЮ Configure())
@ -342,6 +347,8 @@ def UIOSelectorsSecs_WaitAppear_List (inSpecificationListList,inWaitSecs=86400.0
:type inSpecificationListList: list, обязательный
:param inWaitSecs: Количество секунд, которые отвести на ожидание UIO объектов. По умолчанию 24 часа (86400 секунд)
:type inWaitSecs: float, необязательный
:param inFlagRaiseException: True - формировать ошибку exception, если платформа не обнаружина ни одного UIO объекта по заданному UIO селектору. False - обратный случай (может привести к ошибочным результатам). По умолчанию True.
:type inFlagRaiseException: bool, опциональный
:param inFlagWaitAllInMoment: True - Ожидать до того момента, пока не появятся все запрашиваемые UIO объекты на рабочей области
:return: Список индексов, которые указывают на номер входящих UIO селекторов, которые были обнаружены на рабочей области. Пример: [0,2]
'''
@ -356,7 +363,7 @@ def UIOSelectorsSecs_WaitAppear_List (inSpecificationListList,inWaitSecs=86400.0
#Итерация проверки
lIndex = 0
for lItem in inSpecificationListList:
lItemResultFlag=UIOSelector_Exist_Bool(lItem)
lItemResultFlag=UIOSelector_Exist_Bool(lItem, inFlagRaiseException=True)
#Если обнаружен элемент - добавить его индекс в массив
if lItemResultFlag:
lResultList.append(lIndex)
@ -376,7 +383,7 @@ def UIOSelectorsSecs_WaitAppear_List (inSpecificationListList,inWaitSecs=86400.0
return lResultList
#old: -
def UIOSelectorsSecs_WaitDisappear_List (inSpecificationListList,inWaitSecs=86400.0,inFlagWaitAllInMoment=False):
def UIOSelectorsSecs_WaitDisappear_List (inSpecificationListList,inWaitSecs=86400.0,inFlagWaitAllInMoment=False, inFlagRaiseException=True):
'''L-,W+: Ожидать исчезновение хотя бы 1-го / всех UIO объектов по заданным UIO селекторам
!ВНИМАНИЕ! ДАННАЯ ФУНКЦИОНАЛЬНОСТЬ В АВТОМАТИЧЕСКОМ РЕЖИМЕ ПОДДЕРЖИВАЕТ ВСЕ РАЗРЯДНОСТИ ПРИЛОЖЕНИЙ (32|64), КОТОРЫЕ ЗАПУЩЕНЫ В СЕСИИ. PYTHON x64 ИМЕЕТ ВОЗМОЖНОСТЬ ВЗЗАИМОДЕЙСТВИЯ С x32 UIO ОБЪЕКТАМИ, НО МЫ РЕКОМЕНДУЕМ ДОПОЛНИТЕЛЬНО ИСПОЛЬЗОВАТЬ ИНТЕРПРЕТАТОР PYTHON x32 (ПОДРОБНЕЕ СМ. ФУНКЦИЮ Configure())
@ -399,6 +406,8 @@ def UIOSelectorsSecs_WaitDisappear_List (inSpecificationListList,inWaitSecs=8640
:type inSpecificationListList: list, обязательный
:param inWaitSecs: Количество секунд, которые отвести на ожидание исчезновения UIO объектов. По умолчанию 24 часа (86400 секунд)
:type inWaitSecs: float, необязательный
:param inFlagRaiseException: True - формировать ошибку exception, если платформа не обнаружина ни одного UIO объекта по заданному UIO селектору. False - обратный случай (может привести к ошибочным результатам). По умолчанию True.
:type inFlagRaiseException: bool, опциональный
:param inFlagWaitAllInMoment: True - Ожидать до того момента, пока не исчезнут все запрашиваемые UIO объекты на рабочей области
:return: Список индексов, которые указывают на номер входящих UIO селекторов, которые были обнаружены на рабочей области. Пример: [0,2]
'''
@ -414,7 +423,7 @@ def UIOSelectorsSecs_WaitDisappear_List (inSpecificationListList,inWaitSecs=8640
#Итерация проверки
lIndex = 0
for lItem in inSpecificationListList:
lItemResultFlag=UIOSelector_Exist_Bool(lItem)
lItemResultFlag=UIOSelector_Exist_Bool(lItem,inFlagRaiseException=inFlagRaiseException)
#Если обнаружен элемент - добавить его индекс в массив
if not lItemResultFlag:
lResultList.append(lIndex)
@ -434,7 +443,7 @@ def UIOSelectorsSecs_WaitDisappear_List (inSpecificationListList,inWaitSecs=8640
return lResultList
#old: -
def UIOSelectorSecs_WaitAppear_Bool (inSpecificationList,inWaitSecs):
def UIOSelectorSecs_WaitAppear_Bool (inSpecificationList,inWaitSecs, inFlagRaiseException=True):
'''L-,W+: Ожидать появление 1-го UIO объекта по заданному UIO селектору
!ВНИМАНИЕ! ДАННАЯ ФУНКЦИОНАЛЬНОСТЬ В АВТОМАТИЧЕСКОМ РЕЖИМЕ ПОДДЕРЖИВАЕТ ВСЕ РАЗРЯДНОСТИ ПРИЛОЖЕНИЙ (32|64), КОТОРЫЕ ЗАПУЩЕНЫ В СЕСИИ. PYTHON x64 ИМЕЕТ ВОЗМОЖНОСТЬ ВЗЗАИМОДЕЙСТВИЯ С x32 UIO ОБЪЕКТАМИ, НО МЫ РЕКОМЕНДУЕМ ДОПОЛНИТЕЛЬНО ИСПОЛЬЗОВАТЬ ИНТЕРПРЕТАТОР PYTHON x32 (ПОДРОБНЕЕ СМ. ФУНКЦИЮ Configure())
@ -451,16 +460,18 @@ def UIOSelectorSecs_WaitAppear_Bool (inSpecificationList,inWaitSecs):
:type inSpecificationList: list, обязательный
:param inWaitSecs: Количество секунд, которые отвести на ожидание UIO объекта. По умолчанию 24 часа (86400 секунд)
:type inWaitSecs: float, необязательный
:param inFlagRaiseException: True - формировать ошибку exception, если платформа не обнаружина ни одного UIO объекта по заданному UIO селектору. False - обратный случай (может привести к ошибочным результатам). По умолчанию True.
:type inFlagRaiseException: bool, опциональный
:return: True - UIO объект был обнаружен. False - обратная ситуациая
'''
lWaitAppearList=UIOSelectorsSecs_WaitAppear_List([inSpecificationList],inWaitSecs)
lWaitAppearList=UIOSelectorsSecs_WaitAppear_List([inSpecificationList],inWaitSecs, inFlagRaiseException=inFlagRaiseException)
lResult=False
if len(lWaitAppearList)>0:
lResult=True
return lResult
#old name - -
def UIOSelectorSecs_WaitDisappear_Bool (inSpecificationList,inWaitSecs):
def UIOSelectorSecs_WaitDisappear_Bool (inSpecificationList,inWaitSecs,inFlagRaiseException=True):
'''L-,W+: Ожидать исчезновение 1-го UIO объекта по заданному UIO селектору
!ВНИМАНИЕ! ДАННАЯ ФУНКЦИОНАЛЬНОСТЬ В АВТОМАТИЧЕСКОМ РЕЖИМЕ ПОДДЕРЖИВАЕТ ВСЕ РАЗРЯДНОСТИ ПРИЛОЖЕНИЙ (32|64), КОТОРЫЕ ЗАПУЩЕНЫ В СЕСИИ. PYTHON x64 ИМЕЕТ ВОЗМОЖНОСТЬ ВЗЗАИМОДЕЙСТВИЯ С x32 UIO ОБЪЕКТАМИ, НО МЫ РЕКОМЕНДУЕМ ДОПОЛНИТЕЛЬНО ИСПОЛЬЗОВАТЬ ИНТЕРПРЕТАТОР PYTHON x32 (ПОДРОБНЕЕ СМ. ФУНКЦИЮ Configure())
@ -477,9 +488,11 @@ def UIOSelectorSecs_WaitDisappear_Bool (inSpecificationList,inWaitSecs):
:type inSpecificationList: list, обязательный
:param inWaitSecs: Количество секунд, которые отвести на исчезновение UIO объекта. По умолчанию 24 часа (86400 секунд)
:type inWaitSecs: float, необязательный
:param inFlagRaiseException: True - формировать ошибку exception, если платформа не обнаружина ни одного UIO объекта по заданному UIO селектору. False - обратный случай (может привести к ошибочным результатам). По умолчанию True.
:type inFlagRaiseException: bool, опциональный
:return: True - UIO объект был обнаружен. False - обратная ситуациая
'''
lWaitDisappearList=UIOSelectorsSecs_WaitDisappear_List([inSpecificationList],inWaitSecs)
lWaitDisappearList=UIOSelectorsSecs_WaitDisappear_List([inSpecificationList],inWaitSecs,inFlagRaiseException=inFlagRaiseException)
lResult=False
if len(lWaitDisappearList)>0:
lResult=True
@ -908,7 +921,7 @@ def UIO_GetCtrlIndex_Int(inElement):
return lResult
#old: - PywinautoExtElementsGetInfo
def UIOSelector_Get_UIOInfoList (inUIOSelector, inElement=None):
def UIOSelector_Get_UIOInfoList (inUIOSelector, inElement=None, inFlagRaiseException=True):
"""L-,W+: Техническая функция: Получить список параметров последних уровней UIO селектора по UIO объектам, которые удовлетворяют входящим inUIOSelector, поиск по которым будет производится от уровня inElement.
!ВНИМАНИЕ! ДАННАЯ ФУНКЦИОНАЛЬНОСТЬ В АВТОМАТИЧЕСКОМ РЕЖИМЕ ПОДДЕРЖИВАЕТ ВСЕ РАЗРЯДНОСТИ ПРИЛОЖЕНИЙ (32|64), КОТОРЫЕ ЗАПУЩЕНЫ В СЕСИИ. PYTHON x64 ИМЕЕТ ВОЗМОЖНОСТЬ ВЗЗАИМОДЕЙСТВИЯ С x32 UIO ОБЪЕКТАМИ, НО МЫ РЕКОМЕНДУЕМ ДОПОЛНИТЕЛЬНО ИСПОЛЬЗОВАТЬ ИНТЕРПРЕТАТОР PYTHON x32 (ПОДРОБНЕЕ СМ. ФУНКЦИЮ Configure())
@ -925,13 +938,15 @@ def UIOSelector_Get_UIOInfoList (inUIOSelector, inElement=None):
:type inUIOSelector: list, обязательный
:param inElement: UIO объект, от которого выполнить поиск дочерних UIO объектов по UIO селектору inUIOSelector. По умолчанию None - поиск среди всех приложений.
:type inElement: UIO объект, необязательный
:param inFlagRaiseException: True - формировать ошибку exception, если платформа не обнаружина ни одного UIO объекта по заданному UIO селектору. False - обратный случай (может привести к ошибочным результатам). По умолчанию True.
:type inFlagRaiseException: bool, опциональный
:return: dict, пример: {"title":None,"rich_text":None,"process_id":None,"process":None,"handle":None,"class_name":None,"control_type":None,"control_id":None,"rectangle":{"left":None,"top":None,"right":None,"bottom":None}, 'runtime_id':None}
"""
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
#Получить родительский объект если на вход ничего не поступило
lResultList=UIOSelector_Get_UIOList(inUIOSelector, inElement)
lResultList=UIOSelector_Get_UIOList(inUIOSelector, inElement, inFlagRaiseException)
lIterator = 0
for lItem in lResultList:
lResultList[lIterator]=UIOEI_Convert_UIOInfo(lResultList[lIterator].element_info)
@ -985,7 +1000,7 @@ def UIOSelector_TryRestore_Dict(inSpecificationList):
return lResult
#old: - ElementActionGetList
def UIOSelector_Get_UIOActivityList (inUIOSelector):
def UIOSelector_Get_UIOActivityList (inUIOSelector,inFlagRaiseException=True):
"""L-,W+: Получить список доступных действий/функций по UIO селектору inUIOSelector. Описание возможных активностей см. ниже.
!ВНИМАНИЕ! ДАННАЯ ФУНКЦИОНАЛЬНОСТЬ В АВТОМАТИЧЕСКОМ РЕЖИМЕ ПОДДЕРЖИВАЕТ ВСЕ РАЗРЯДНОСТИ ПРИЛОЖЕНИЙ (32|64), КОТОРЫЕ ЗАПУЩЕНЫ В СЕСИИ. PYTHON x64 ИМЕЕТ ВОЗМОЖНОСТЬ ВЗЗАИМОДЕЙСТВИЯ С x32 UIO ОБЪЕКТАМИ, НО МЫ РЕКОМЕНДУЕМ ДОПОЛНИТЕЛЬНО ИСПОЛЬЗОВАТЬ ИНТЕРПРЕТАТОР PYTHON x32 (ПОДРОБНЕЕ СМ. ФУНКЦИЮ Configure())
@ -1000,12 +1015,14 @@ def UIOSelector_Get_UIOActivityList (inUIOSelector):
:param inUIOSelector: UIO селектор, который определяет UIO объект, для которого будет представлен перечень доступных активностей.
:type inUIOSelector: list, обязательный
:param inFlagRaiseException: True - формировать ошибку exception, если платформа не обнаружина ни одного UIO объекта по заданному UIO селектору. False - обратный случай (может привести к ошибочным результатам). По умолчанию True.
:type inFlagRaiseException: bool, опциональный
"""
#Check the bitness
lSafeOtherProcess = UIOSelector_SafeOtherGet_Process(inUIOSelector)
if lSafeOtherProcess is None:
#Получить объект
lObject=UIOSelector_Get_UIO(inUIOSelector)
lObject=UIOSelector_Get_UIO(inUIOSelector,inFlagRaiseException=inFlagRaiseException)
lActionList=dir(lObject)
lResult=dir(lObject)
#Выполнить чистку списка от неактуальных методов
@ -1033,7 +1050,7 @@ def UIOSelector_Get_UIOActivityList (inUIOSelector):
return lResult
#old: - ElementRunAction
def UIOSelectorUIOActivity_Run_Dict(inUIOSelector, inActionName, inArgumentList=None, inkwArgumentObject=None):
def UIOSelectorUIOActivity_Run_Dict(inUIOSelector, inActionName, inFlagRaiseException=True, inArgumentList=None, inkwArgumentObject=None):
"""L-,W+: Выполнить активность inActionName над UIO объектом, полученным с помощью UIO селектора inUIOSelector. Описание возможных активностей см. ниже.
!ВНИМАНИЕ! ДАННАЯ ФУНКЦИОНАЛЬНОСТЬ В АВТОМАТИЧЕСКОМ РЕЖИМЕ ПОДДЕРЖИВАЕТ ВСЕ РАЗРЯДНОСТИ ПРИЛОЖЕНИЙ (32|64), КОТОРЫЕ ЗАПУЩЕНЫ В СЕСИИ. PYTHON x64 ИМЕЕТ ВОЗМОЖНОСТЬ ВЗЗАИМОДЕЙСТВИЯ С x32 UIO ОБЪЕКТАМИ, НО МЫ РЕКОМЕНДУЕМ ДОПОЛНИТЕЛЬНО ИСПОЛЬЗОВАТЬ ИНТЕРПРЕТАТОР PYTHON x32 (ПОДРОБНЕЕ СМ. ФУНКЦИЮ Configure())
@ -1050,6 +1067,8 @@ def UIOSelectorUIOActivity_Run_Dict(inUIOSelector, inActionName, inArgumentList=
:type inUIOSelector: list, обязательный
:param inActionName: наименование активности, которую требуется выполнить над UIO объектом
:type inActionName: str, обязательный
:param inFlagRaiseException: True - формировать ошибку exception, если платформа не обнаружина ни одного UIO объекта по заданному UIO селектору. False - обратный случай (может привести к ошибочным результатам). По умолчанию True.
:type inFlagRaiseException: bool, опциональный
:param inArgumentList: список передаваемых неименованных аргументов в функцию inActionName
:type inArgumentList: list, необязательный
:param inkwArgumentObject: словарь передаваемых именованных аргументов в функцию inActionName
@ -1064,7 +1083,7 @@ def UIOSelectorUIOActivity_Run_Dict(inUIOSelector, inActionName, inArgumentList=
#Run activity if SafeOtherProcess is None
if lSafeOtherProcess is None:
#Определить объект
lObject=UIOSelector_Get_UIO(inUIOSelector)
lObject=UIOSelector_Get_UIO(inUIOSelector,inFlagRaiseException=inFlagRaiseException)
#Получить метод для вызова
lFunction = getattr(lObject, inActionName)
#Выполнить действие

@ -1,8 +1,10 @@
from selenium import *
from selenium import webdriver, common
from selenium.webdriver.common.by import By
from selenium.common.exceptions import JavascriptException
import os
import sys
import json
from pyOpenRPA.Tools import CrossOS
import time
@ -14,8 +16,8 @@ UIO_WAIT_INTERVAL_SEC_FLOAT = 1.0
gBrowser:webdriver.Chrome = None
def BrowserChromeStart(inDriverExePathStr:str = None, inChromeExePathStr:str = None, inExtensionPathList:list = None, inProfilePathStr:str=None) -> webdriver.Chrome:
"""L+,W+: Выполнить запуск браузера Chrome. Если вы скачали pyOpenRPA вместе с репозиторием, то будет использоваться встроенный браузер Google Chrome. Если установка pyOpenRPA производилась другим способом, то требуется указать расположение браузера Google Chrome и соответствующего WebDriver.
def BrowserChromeStart(inDriverExePathStr:str = None, inChromeExePathStr:str = None, inExtensionPathList:list = None, inProfilePathStr:str=None, inSaveAsPDFBool = False, inSavefileDefaultDirStr = None) -> webdriver.Chrome:
"""L+,W+: Выполнить запуск браузера Chrome. Если вы скачали pyOpenRPA вместе с репозиторием, то будет использоваться встроенный браузер Google Chrome. Если установка pyOpenRPA производилась другим способом, то требуется указать расположение браузера Google Chrome и соответствующего WebDriver
.. code-block:: python
@ -32,6 +34,10 @@ def BrowserChromeStart(inDriverExePathStr:str = None, inChromeExePathStr:str = N
:type inExtensionPathList: list, опционально
:param inProfilePathStr: Путь, по которому выполнить сохранения профиля Chrome (история, куки и т.д.), по умолчанию None (профиль не сохраняется)
:type inProfilePathStr: str, опционально
:param inSaveAsPDFBool: Флаг, который обеспечивает настройки окна печати вэб-страницы как "Сохранить как PDF", по умолчанию False (настройки по умолчанию)
:type inSaveAsPDFBool: bool, опционально
:param inSavefileDefaultDirStr: Путь, по которому выполнить сохранения файла (после работы с окном печать вэб-страницы браузера) (история, куки и т.д.), по умолчанию None (файл не сохраняется)
:type inSavefileDefaultDirStr: str, опционально
:return: Объект браузера Google Chrome
:rtype: webdriver.Chrome
"""
@ -54,8 +60,31 @@ def BrowserChromeStart(inDriverExePathStr:str = None, inChromeExePathStr:str = N
if CrossOS.IS_WINDOWS_BOOL: inChromeExePathStr = os.path.join(lResourcePathStr, "WChrome64-840414730", "App", "Chrome-bin", "chrome.exe")
elif CrossOS.IS_LINUX_BOOL: inChromeExePathStr = os.path.join(lResourcePathStr, "LChrome64-10305060114", "data", "chrome")
if inExtensionPathList == None: inExtensionPathList = []
# Set full path to exe of the chrome
# Установка настроек окна печати, если необходимо
lWebDriverChromeOptionsInstance = webdriver.ChromeOptions()
if inSaveAsPDFBool == True and inSavefileDefaultDirStr is not None:
print_settings = {
"recentDestinations": [{
"id": "Save as PDF",
"origin": "local",
"account": "",
}],
"selectedDestinationId": "Save as PDF",
"version": 2, # в chrome - это номер варинта "сохранить как PDF"
"isHeaderFooterEnabled": False, # хедеры HTML на странице
"isLandscapeEnabled": False # ориентация (True - альбомная)
}
prefs = {'printing.print_preview_sticky_settings.appState': json.dumps(print_settings),
"download.prompt_for_download": False,
"profile.default_content_setting_values.automatic_downloads": 1,
"download.default_directory": inSavefileDefaultDirStr,
"savefile.default_directory": inSavefileDefaultDirStr,
"download.directory_upgrade": True,
"safebrowsing.enabled": True}
lWebDriverChromeOptionsInstance.add_experimental_option('prefs', prefs)
lWebDriverChromeOptionsInstance.add_argument('--kiosk-printing')
# Set full path to exe of the chrome
lWebDriverChromeOptionsInstance.binary_location = inChromeExePathStr
#lWebDriverChromeOptionsInstance2 = webdriver.ChromeOptions()
if inProfilePathStr is not None:
@ -94,6 +123,8 @@ def BrowserChange(inBrowser):
def PageOpen(inURLStr: str):
"""L+,W+: Открыть страницу inURLStr в браузере и дождаться ее загрузки.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
# UIWeb: Взаимодействие с ui web
@ -107,9 +138,30 @@ def PageOpen(inURLStr: str):
"""
global gBrowser
if gBrowser is not None: gBrowser.get(inURLStr)
def PagePrint():
"""L+,W+: Открыть окно печати браузера.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
import time
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
time.sleep(1)
UIWeb.PagePrint()
UIWeb.BrowserClose()
"""
PageJSExecute(inJSStr=f"window.print()")
def PageScrollTo(inVerticalPxInt=0, inHorizontalPxInt=0):
"""L+,W+: Выполнить прокрутку страницы (по вертикали или по горизонтали)
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -129,6 +181,8 @@ def PageScrollTo(inVerticalPxInt=0, inHorizontalPxInt=0):
def PageJSExecute(inJSStr, *inArgList):
"""L+,W+: Отправить на выполнение на сторону браузера код JavaScript.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
!ВНИМАНИЕ! Данная функция поддерживает передачу переменных в область кода JavaScript (*inArgList). Обратиться к переданным переменным из JavaScript можно с помощью ключевого слова: arguments[i], где i - это порядковый номер переданной переменной
@ -169,6 +223,8 @@ def BrowserClose():
def UIOSelectorList(inUIOSelectorStr, inUIO=None) -> list:
"""L+,W+: Получить список UIO объектов по UIO селектору.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -176,7 +232,7 @@ def UIOSelectorList(inUIOSelectorStr, inUIO=None) -> list:
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOList = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
@ -204,6 +260,8 @@ def UIOSelectorList(inUIOSelectorStr, inUIO=None) -> list:
def UIOSelectorFirst(inUIOSelectorStr, inUIO=None) -> list:
"""L+,W+: Получить UIO объект по UIO селектору.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -211,7 +269,7 @@ def UIOSelectorFirst(inUIOSelectorStr, inUIO=None) -> list:
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIWeb.UIOSelectorFirst(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
@ -229,6 +287,8 @@ def UIOSelectorFirst(inUIOSelectorStr, inUIO=None) -> list:
def UIOTextGet(inUIO) -> str:
"""L+,W+: Получить текст UI элемента.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -236,7 +296,7 @@ def UIOTextGet(inUIO) -> str:
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
lTextStr = UIWeb.UIOTextGet(inUIO=lUIO)
UIWeb.BrowserClose()
@ -250,6 +310,8 @@ def UIOTextGet(inUIO) -> str:
def UIOAttributeGet(inUIO, inAttributeStr) -> str:
"""L+,W+: Получить обычный (нестилевой) атрибут у UI элемента.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -257,7 +319,7 @@ def UIOAttributeGet(inUIO, inAttributeStr) -> str:
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeGet(inUIO=lUIO, inAttributeStr = "href")
UIWeb.BrowserClose()
@ -273,6 +335,8 @@ def UIOAttributeGet(inUIO, inAttributeStr) -> str:
def UIOAttributeStyleGet(inUIO, inAttributeStr) -> str:
"""L+,W+: Получить стилевой атрибут у UI элемента.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -280,7 +344,7 @@ def UIOAttributeStyleGet(inUIO, inAttributeStr) -> str:
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeStyleGet(inUIO=lUIO, inAttributeStr = "href")
UIWeb.BrowserClose()
@ -296,6 +360,8 @@ def UIOAttributeStyleGet(inUIO, inAttributeStr) -> str:
def UIOAttributeSet(inUIO, inAttributeStr, inValue):
"""L+,W+: Установить обычный (нестилевой) атрибут у UI элемента.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -303,7 +369,7 @@ def UIOAttributeSet(inUIO, inAttributeStr, inValue):
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeSet(inUIO=lUIO, inAttributeStr = "href", inValue = "https://mail.ru")
UIWeb.BrowserClose()
@ -321,6 +387,8 @@ def UIOAttributeSet(inUIO, inAttributeStr, inValue):
def UIOAttributeRemove(inUIO, inAttributeStr):
"""L+,W+: Удалить обычный (нестилевой) атрибут у UI элемента.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -328,7 +396,7 @@ def UIOAttributeRemove(inUIO, inAttributeStr):
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeRemove(lUIO, "href")
UIWeb.BrowserClose()
@ -344,6 +412,8 @@ def UIOAttributeRemove(inUIO, inAttributeStr):
def UIOAttributeStyleSet(inUIO, inAttributeStr, inValue):
"""L+,W+: Установить стилевой атрибут у UI элемента.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -351,7 +421,7 @@ def UIOAttributeStyleSet(inUIO, inAttributeStr, inValue):
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeStyleSet(inUIO=lUIO, inAttributeStr = "color", inValue = "grey")
UIWeb.BrowserClose()
@ -369,6 +439,8 @@ def UIOAttributeStyleSet(inUIO, inAttributeStr, inValue):
def UIOAttributeStyleRemove(inUIO, inAttributeStr:str):
"""L+,W+: Удалить стилевой атрибут у UI элемента.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -376,7 +448,7 @@ def UIOAttributeStyleRemove(inUIO, inAttributeStr:str):
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeStyleRemove(lUIO, "color")
UIWeb.BrowserClose()
@ -392,6 +464,8 @@ def UIOAttributeStyleRemove(inUIO, inAttributeStr:str):
def UIOClick(inUIO):
"""L+,W+: Выполнить нажатие по элементу inUIO.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -399,7 +473,7 @@ def UIOClick(inUIO):
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIOClick(inUIO = lUIO)
UIWeb.BrowserClose()
@ -411,6 +485,8 @@ def UIOClick(inUIO):
def UIOSelectorHighlight(inUIOSelectorStr: str, inIsFirst:bool=False, inDurationSecFloat:float=3.0, inColorStr:str="green"):
"""L+,W+: Выполнить подсвечивание UI элемента с селектором inUIOSelectorStr.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -418,7 +494,7 @@ def UIOSelectorHighlight(inUIOSelectorStr: str, inIsFirst:bool=False, inDuration
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
UIWeb.UIOSelectorHighlight(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
@ -467,6 +543,8 @@ def UIOSelectorHighlight(inUIOSelectorStr: str, inIsFirst:bool=False, inDuration
def UIOSelectorClick(inUIOSelectorStr: str):
"""L+,W+: Выполнить нажатие по элементу с селектором inUIOSelectorStr.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -474,25 +552,56 @@ def UIOSelectorClick(inUIOSelectorStr: str):
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
UIWeb.UIOSelectorClick(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI элемента на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
:type inUIOSelectorStr: str
"""
PageJSExecute(inJSStr=f"document.querySelector('{inUIOSelectorStr}').click()")
if UIOSelectorDetect(inUIOSelectorStr=inUIOSelectorStr) == "CSS":
PageJSExecute(inJSStr=f"document.querySelector('{inUIOSelectorStr}').click()")
else:
PageJSExecute(inJSStr=f"document.evaluate('{inUIOSelectorStr}', document, null , XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.click()")
def UIOSelectorSetValue(inUIOSelectorStr: str, inValue: str):
"""L+,W+: Установить значение элемента с селектором inUIOSelectorStr.
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://www.google.com/")
lUIOSelectorStr = "/html/body/div[1]/div[3]/form/div[1]/div[1]/div[1]/div/div[2]/input"
lValue = "pyOpenRPA"
UIWeb.UIOSelectorSetValue(inUIOSelectorStr = lUIOSelectorStr, inValue = lValue)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI элемента на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
:type inUIOSelectorStr: str
:param inValue: Значение, которое необходимо установить
:type inValue: str
"""
if UIOSelectorDetect(inUIOSelectorStr=inUIOSelectorStr) == "CSS":
PageJSExecute(inJSStr=f"document.querySelector('{inUIOSelectorStr}').value='{inValue}'")
else:
PageJSExecute(inJSStr=f"document.evaluate('{inUIOSelectorStr}', document, null , XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.value='{inValue}'")
def UIOSelectorWaitAppear(inUIOSelectorStr:str, inWaitSecFloat:float=UIO_WAIT_SEC_FLOAT, inWaitIntervalSecFloat:float = UIO_WAIT_INTERVAL_SEC_FLOAT):
"""L+,W+: Ожидать появление UI элемента на веб странице (блокирует выполнение потока), заданного по UIO селектору inUIOSelectorStr. Выполнять ожидание на протяжении inWaitSecFloat (по умолчанию 60 сек.). Проверка производится с интервалом inWaitIntervalSecFloat (по умолчанию 1 сек.)
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lAppearUIOList = UIWeb.UIOSelectorWaitAppear(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
@ -517,14 +626,16 @@ def UIOSelectorWaitAppear(inUIOSelectorStr:str, inWaitSecFloat:float=UIO_WAIT_SE
def UIOSelectorWaitDisappear(inUIOSelectorStr:str, inWaitSecFloat:float=UIO_WAIT_SEC_FLOAT, inWaitIntervalSecFloat:float = UIO_WAIT_INTERVAL_SEC_FLOAT):
"""L+,W+: Ожидать исчезновение UI элемента с веб страницы (блокирует выполнение потока), заданного по UIO селектору inUIOSelectorStr. Выполнять ожидание на протяжении inWaitSecFloat (по умолчанию 60 сек.). Проверка производится с интервалом inWaitIntervalSecFloat (по умолчанию 1 сек.)
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
UIWeb.UIOSelectorWaitDisappear(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
@ -556,7 +667,7 @@ def UIOSelectorDetect(inUIOSelectorStr:str) -> str:
# UIWeb: Взаимодействие с ui web
from pyOpenRPA.Robot import UIWeb
lUIOSelectorStr = "#grid > div.grid-middle > div.grid__main-col.svelte-2y66pa > div.grid_newscol.grid_newscol__more-pulse.svelte-1yvqfic > div.grid__ccol.svelte-1yvqfic > ul > li:nth-child(5) > div > a"
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOSelectorStr = "//*[@id=\'grid\']/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lResultStr = UIWeb.UIOSelectorDetect(inUIOSelectorStr = lUIOSelectorStr)
:param inUIOSelectorStr: XPATH или CSS селектор UI объекта на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
@ -576,6 +687,8 @@ def UIOSelectorDetect(inUIOSelectorStr:str) -> str:
def UIOMouseSearchInit():
"""L+,W+: Инициализирует процесс поиска UI элемента с помощью мыши. Для прекращения поиска необходимо использовать функцию: UIOMouseSearchReturn
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -603,6 +716,8 @@ def UIOMouseSearchInit():
def UIOMouseSearchReturn():
"""L+,W+: Возвращает UIO объект, над которым находится указатель мыши. Предварительно должна быть вызвана функция UIWeb.UIOMouseSearchInit
!ВНИМАНИЕ! Для работы необходимо проинициализировать webdriver
.. code-block:: python
@ -623,5 +738,7 @@ def UIOMouseSearchReturn():
document.removeEventListener('mousemove', document.ORPASearch);
return document.elementFromPoint(document.ORPAMouseXInt,document.ORPAMouseYInt);
"""
return PageJSExecute(lJSStr)
try:
return PageJSExecute(lJSStr)
except JavascriptException: raise JavascriptException("Отсутствуют координаты для идентификации веб-элемента. Пожалуйста, в следующий раз двигайте мышью")

@ -69,6 +69,7 @@ setup(name='pyOpenRPA',
'fastapi>=0.81.0',
'uvicorn>=0.18.3',
'python-multipart>=0.0.6'
'autodocsumm>=0.2.10'
],
extras_require={
':sys_platform == "win32"': [

@ -1,3 +1,3 @@
chcp 65001
call init-python-env.cmd jupyter-notebook.exe jupyter-notebook.exe
jupyter-notebook.exe -m notebook --notebook-dir=%~dp0
jupyter-notebook.exe -m notebook --notebook-dir="%~dp0

@ -11,6 +11,10 @@ AGT - AGENT
******************************
- ОБЩЕЕ
- - Utils: Disk - подготовка файлов / папок, если они не обнаружены (полезно при первом запуске, если требуются БД/ Файлы хранилищ)
- - Jupyter: запуск из директорий, пути к которым содержат пробелы
- - PyOpenRPA: autodocsumm - добавлено к пакетам
- - Исправлено неправильное поведение оркестратора при авторизации в браузере Firefox
- - Исправлено описание функци в документации, раздел UIWeb
- ОРКЕСТРАТОР
- - Исправление совместимости URL путей с некорыми ресурсами для отработки в LINUX
- - Поддержка многотысячной аудитории, одновременно работающей в панели управления (async server-data server-log with fastapi)
@ -22,9 +26,19 @@ AGT - AGENT
- - AgentProcessList исправление (hotfix)
- - Права доступа в случае незаявленного пользователя (Hotfix)
- - Возможность авторизации в формате login@domain
- - Переработана система обработки AuthTokenStr==None. Теперь поднимается exception в случаях, когда авторизация производилась, но AuthTokenStr==None
- - Добавлена новая функция - Orchestrator.OrchestratorIsCredentialsAsk. Определяет была ли произведена авторизация пользователя
- - WebURLConnectFolder - переработано. Добавлен флаг обработки ошибки, позволяющий продолжить инициализацию оркестратора, если папки не существует
- - Переработана система авторизации. Добавлена стартовая страница оркестратора с формой для ввода пары логин / пароль
- РОБОТ
- - Убрали лишний print из Screen.BoxAnchorRuleCheck
- - pyOpenRPA.Robot.UIWeb.BrowserChromeStart - добавлен флаг печати в PDF и директория для сохранения при инициализации браузера
- - Добавлена новая функция - pyOpenRPA.Robot.UIWeb.PagePrint. Вызывает окно печати браузера
- - pyOpenRPA.Robot.UIWeb.UIOSelectorClick - исправлено. Теперь работает и с XPath, и с CSS
- - Добавлена новая функция - pyOpenRPA.Robot.UIWeb.UIOSelectorSetValue. Изменение атрибута value по заданному UIOSelector элемента
- - pyOpenRPA.Robot.UIDesktop.UIOSelector_Get_UIOList - исправлена обработка exceptions. Теперь флаг ловит все исключения
- - Флаг inFlagRaiseException добавлен во все функции, связанные с pyOpenRPA.Robot.UIDesktop.UIOSelector_Get_UIOList. Теперь режим работы с исключениями задается глобально. Исключения отлавливаются - inFlagRaiseException=True, не отлавливаются - inFlagRaiseException=False
- - pyOpenRPA.Robot.UIWeb.UIOMouseSearchReturn добавлена обработка JavascriptException
[1.3.1]
- ОРКЕСТРАТОР
- - минорные правки в дизайн

Loading…
Cancel
Save