Compare commits

...

57 Commits
dev ... prd

Author SHA1 Message Date
Иван Маслов 793d353c48 license ref update
2 years ago
Ivan Maslov 28078f8b41 fix unix new line in win cmd files
2 years ago
Ivan Maslov 5b9274570f v1.3.1 релиз!
2 years ago
Ivan Maslov b5996fad45 final fixes for 1.3.1
2 years ago
Ivan Maslov ab959bce2e Audio minor fix
2 years ago
Ivan Maslov c4e7dcba43 prepare to 1 3 1
2 years ago
Ivan Maslov 0e5c0521de CaptureWait - add wait options (wait callbacks)
2 years ago
Ivan Maslov 0741fa69fb Win audio: add virtual audio driver (for VM with no audio)
2 years ago
Ivan Maslov 5b7c24d855 audio capture stop fixes
2 years ago
Ivan Maslov e8a0cd1b7b Audio fixes in threads
2 years ago
Ivan Maslov 9ad4d7a026 Audio fixes - add chunk shift in time
2 years ago
Ivan Maslov 7b7f246dab #Audio - make folder if not exists
2 years ago
Ivan Maslov 136c7d87a5 UIWeb minor fix when close
2 years ago
Ivan Maslov da97936b1d linux dependencies
2 years ago
Ivan Maslov 99911bf643 Audio wiki help
2 years ago
Ivan Maslov 0d97ec4ff2 Audio module done!
2 years ago
Ivan Maslov 20988db521 robot.audio added and tested!
2 years ago
Ivan Maslov acc59de61b Class done - need test / fix silent mode and callback
2 years ago
Ivan Maslov 297e3ba9fa Audio in progress
2 years ago
Ivan Maslov 5cea74b940 Audio in progress
2 years ago
Ivan Maslov f55bd55003 minor fixes in BC
2 years ago
Ivan Maslov c8d8ea2620 orc: fix bc WebConnect
2 years ago
Ivan Maslov 6efc358b99 favico fix
2 years ago
Ivan Maslov 6bfa44d291 add some utils
2 years ago
Ivan Maslov 1078c8ee2c orc.web: Восстановлена кнопка "Перезагрузить оркестратор"
2 years ago
Ivan Maslov 3dad37a1bf OrchestratorPySearchInit add relative import
2 years ago
Ivan Maslov 60ca4e5bcb Robot.Audio in progress
2 years ago
Ivan Maslov 285262ad77 Create Robot quickstart + minor fixes in Jupyter (add nbextension allow on the new PC)
2 years ago
robo-bo 67323432b7 std minor fixes
2 years ago
robo-bo 4a674f7068 minor fixes
2 years ago
robo-bo 1999fa03e4 studio new ui, jupyter start fix
2 years ago
robo-bo 02694add30 orc cp hello upd
2 years ago
Иван Маслов b5dfd586db wiki upd
2 years ago
robo-bo 4115f461f9 orc demo in progress
2 years ago
robo-bo c79749096e Add merge requests - some fixes
2 years ago
Alex Kuralesov f3ef7145cb Merge branch 'dev-fastapi' into 'dev-fastapi'
2 years ago
robo-bo cb9af1f6e8 orc add version label
2 years ago
Vladislav Klychkov 1043e44ca4 + Робот-казначей
2 years ago
Vladislav Klychkov f34d675f90 Структурирование названий файлов
2 years ago
Vladislav Klychkov aebcace966 Добавлена карточка HR в демо-стенд
2 years ago
Ivan Maslov c37d714814 agent fix
2 years ago
Ivan Maslov 6c04e81bdd orc tested
2 years ago
Ivan Maslov 1c77c8d078 orc fixes
2 years ago
Ivan Maslov 8830d6eba3 orc: API goes to FASTAPI
2 years ago
Ivan Maslov b3331d3782 some fixes
2 years ago
Ivan Maslov cce7f1ebe3 orc update methods (add what will be deprecated in 1.3.2)
2 years ago
Ivan Maslov fec135e16f orc add ssl
2 years ago
robo-bo b3d04a6393 Orc server is tested TODO js lib orpa
2 years ago
robo-bo cfbdefd161 orc server fixes (before renamings)
2 years ago
robo-bo 1baacac24e fixes
2 years ago
robo-bo 1c175f109e fixes
2 years ago
robo-bo 77c27f526b Fix Managers.Process add timeout to status check
2 years ago
Ivan Maslov d15cec2277 comment prints
2 years ago
Ivan Maslov 23b17e0d1f prototype in progress
2 years ago
Ivan Maslov 5ece90166f minor
2 years ago
Ivan Maslov a12de50131 fastapi inprogress
2 years ago
robo-bo 2b06170730 Start to go to fast api (with backward compatibility)
2 years ago

3
.gitignore vendored

@ -17,6 +17,9 @@
/**/OpenRPA_Orchestrator.exe
/**/OpenRPAOrchestrator.exe
/**/OpenRPARobotGUIx32.exe
/**/orpa-std.exe
/**/orpa-rbt.exe
/**/orpa-agt.exe
/**/orpa-orc.exe
/**/orpa-agent.exe
/**/OpenRPARobotGUIx64.exe

@ -1,3 +1,3 @@
cd %~dp0
taskkill /F /FI "USERNAME eq %username%" /IM orpa-agent.exe
cd %~dp0
taskkill /F /FI "USERNAME eq %username%" /IM orpa-agent.exe
pause >nul

@ -1,5 +1,6 @@
cd %~dp0
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\pythonw.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
chcp 65001
cd /d "%~dp0"
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\pythonw.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
pause >nul

@ -1,5 +1,6 @@
cd %~dp0
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
chcp 65001
cd /d "%~dp0"
taskkill /im "orpa-agent.exe" /F /fi "username eq %username%"
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-agent.exe "config.py"
pause >nul

@ -1,2 +0,0 @@
git pull
pause>nul

@ -1,9 +1,9 @@
cd %~dp0
git push origin devIvan
git checkout master
git reset --hard
git pull
git merge devIvan
git push origin master
git checkout devIvan
cd %~dp0
git push origin devIvan
git checkout master
git reset --hard
git pull
git merge devIvan
git push origin master
git checkout devIvan
pause >nul

@ -1,3 +1,3 @@
git clean -f -d
git reset --hard
git clean -f -d
git reset --hard
pause>nul

@ -0,0 +1,2 @@
git pull
pause>nul

@ -1,4 +1,4 @@
cd %~dp0
git reset --hard
git pull
cd %~dp0
git reset --hard
git pull
pause >nul

@ -1,8 +1,5 @@
pyOpenRPA open license
pyOpenRPA открытая лицензия
Copyright (c) 2019 Ivan Maslov
Правообладатель: 2019 Маслов Иван Дмитриевич
Copyright (c) 2019 Ivan Maslov, 2022 LLC pyOpenRPA
Правообладатель: 2019 Маслов Иван Дмитриевич, 2022 ООО ОПЕН РПА
Текст лицензии см. в файле: LICENSE.PDF (в корне репозитория) или по адресу: https://pyopenrpa.ru/license/oferta.pdf

@ -1,57 +0,0 @@
# !ATTENTION - Current Control panel works only from pyOpenRPA v1.2.0!
from pyOpenRPA import Orchestrator
def CPRender(inGSettings):
lJSCheckVersion="""
lT = 9;
lY="123";
console.log(lT+1);
if (lT==9) {
alert(123)
}
"""
lResultDict={
"HeaderLeftText":"CP_TEST",
"HeaderRightText":"NAME",
"DataStorageKey":"", #Use key for set current dict in mGlobal.DataStorage["DataStorageKey"] on client side
"SubheaderText":"",
"BodyKeyValueList":[
{"Key": "HTMLLinkURL", "Value": Orchestrator.Web.Basic.HTMLLinkURL(inURLStr="test",inColorStr="orange")},
{"Key": "HTMLLinkJSOnClick", "Value": Orchestrator.Web.Basic.HTMLLinkJSOnClick(inJSOnClickStr=lJSCheckVersion, inTitleStr="!Click me!",inColorStr="green")},
],
"FooterText":"",
"FooterButtonX2List":[],
"FooterButtonX1List":[],
"GlobalStorage": "" # UNCOMMENT FOR DEBUG PURPOSE TO WATCH inGSettings on client side
}
return lResultDict
# Check in control panel, that process is runnning
# Test JSON generator when page init
def JSONGenerator():
lJSONDict=[1,2,2,4,2,2,2]
return lJSONDict
# Test JS when page init
def JSInitGenerator():
lJSCheckVersion="""
lT = 9;
lY="123";
//console.log(lT+1);
if (lT==9) {
//alert(123)
}
"""
return lJSCheckVersion
def test():
#Orchestrator.WebRequestGet()
Orchestrator.WebRequestResponseSend("Hello my friend!")
#Orchestrator settings
def SettingsUpdate(inGSettings):
# New way to add CP defs in orchestrator - no gSettings..
Orchestrator.WebCPUpdate(inGSettings=inGSettings, inCPKeyStr="TEST", inHTMLRenderDef=CPRender, inJSONGeneratorDef=JSONGenerator, inJSInitGeneratorDef=JSInitGenerator)
Orchestrator.WebURLConnectDef(inMethodStr="GET", inURLStr="/test", inMatchTypeStr="Equal", inDef=test, inUACBool=False)
return inGSettings

@ -1,54 +0,0 @@
# !ATTENTION - Current Control panel works only from pyOpenRPA v1.2.0!
from pyOpenRPA import Orchestrator
from pyOpenRPA.Orchestrator import Managers
def ControlPanelRenderDict(inGSettings):
# Example of the JS code in Python code
lJSCheckVersion = f"""
if (!('VersionStr' in mGlobal)) {{
window.location.reload(true);
}} else {{
if (mGlobal.VersionStr != "{inGSettings["VersionStr"]}") {{
window.location.reload(true);
}} else {{
$('div.orchestrator-version').html(mGlobal['VersionStr']);
}}
}}
"""
lResultDict={
"HeaderLeftText":"Version check",
"HeaderRightText":"Orchestrator",
"DataStorageKey":"", #Use key for set current dict in mGlobal.DataStorage["DataStorageKey"] on client side
"SubheaderText":f"<script>{lJSCheckVersion}</script>",
"BodyKeyValueList":[
{"Key": "Client", "Value": '<div class="orchestrator-version" style="display:inline;"></div>'},
{"Key": "Server", "Value": inGSettings["VersionStr"]},
],
"FooterText":"",
"FooterButtonX2List":[],
"FooterButtonX1List":[],
"GlobalStorage": "" # UNCOMMENT FOR DEBUG PURPOSE TO WATCH inGSettings on client side
}
return lResultDict
# Check in control panel, that process is runnning
#Orchestrator settings
def SettingsUpdate(inGSettings):
#Add RobotRDPActive in control panel
Orchestrator.WebCPUpdate(inCPKeyStr="VersionCheck", inHTMLRenderDef=ControlPanelRenderDict)
#Orchestrator.Managers.ControlPanel(inControlPanelNameStr="TestTTT",inRefreshHTMLJinja2TemplatePathStr="ControlPanel\\test.html", inJinja2TemplateRefreshBool = True)
lProcess = Orchestrator.Managers.ProcessInitSafe(inAgentHostNameStr="IVANMASLOV-DESKTOP",inAgentUserNameStr="ND",
inProcessNameWOExeStr="notepad",inStartCMDStr="notepad",inStopSafeTimeoutSecFloat=3)
# Some test
Orchestrator.OrchestratorInitWait()
#lProcess.ScheduleStatusCheckEverySeconds(inIntervalSecondsInt=5)
#lProcess.Start(inIsManualBool=False)
lProcess.StartCheck()
#Orchestrator.OrchestratorScheduleGet().every(2).seconds.do(Orchestrator.OrchestratorThreadStart,
# lProcess.StartCheck)
#Orchestrator.OrchestratorScheduleGet().every(5).seconds.do(Orchestrator.OrchestratorThreadStart,lProcess.StatusCheckStart)
#lProcess.Start()
lGit = Orchestrator.Managers.Git(inAgentHostNameStr="IVANMASLOV-DESKTOP",inAgentUserNameStr="ND",inGitPathStr="C:\Abs\Archive\scopeSrcUL\pyStore")
lGit.BranchRevLastGetInterval(inBranchLocalStr="prd", inBranchRemoteStr="origin/prd", inPreviousBranchRestoreBool=False,inIntervalSecFloat=10.0)
lGit.ProcessConnect(inProcess=lProcess)
return inGSettings

@ -0,0 +1,28 @@
from pyOpenRPA import Orchestrator
from pyOpenRPA.Orchestrator.Managers import ControlPanel
from pyOpenRPA.Orchestrator.Managers import Process
from pyOpenRPA.Tools import CrossOS
import os
import sys
import socket
import datetime
import urllib.parse
import time
g_cp_name_str = "HELLO"
g_repo_name_str = os.path.abspath(__file__).split("\\")[-5]
g_repo_package_path_str = os.path.abspath("\\".join(os.path.abspath(__file__).split("\\")[:-1]))
# User settings
g_host_str = socket.gethostname().upper() # Identify PC
g_control_panel = ControlPanel(inControlPanelNameStr=g_cp_name_str,
inRefreshHTMLJinja2TemplatePathStr=os.path.join(g_repo_package_path_str, "html_jinja2.xhtml"),
inJinja2TemplateRefreshBool=True,inRobotNameStr=g_repo_name_str)
g_jinja_context_dict = {"settings": sys.modules[__name__],
"urllib_parse_quote_plus": urllib.parse.quote_plus, "g_host_str": g_host_str, "g_repo_name_str": g_repo_name_str}
g_control_panel.Jinja2DataUpdateDictSet(inJinja2DataUpdateDict=g_jinja_context_dict)
g_control_panel.InitJSJinja2TemplatePathSet(inJinja2TemplatePathStr=os.path.join(g_repo_package_path_str, "js.js"))

@ -0,0 +1,79 @@
<div class="card" style="width:100%">
<div class="content">
<div class="right floated mini ui ">
</div>
<center>
<h1>Добро пожаловать <span>
{% if UserInfoDict["UserNameUpperStr"] %}
{{UserInfoDict["UserNameUpperStr"]}}
{% endif %}
</span>!</h1>
</center>
<div class="ui divider"></div>
<div class="description cp-hello-description">
<div>
<h5>Предлагаем ознакомиться с демонстрационным стэндом оркестратора pyOpenRPA.</h5>
pyOpenRPA это прогрессивная RPA платформа, которая позволяет сочетать в рамках одного проекта все перспективные технологии, такие как:
<ul>
<li>OCR / NLP / NER: Распознавание изображений и текста</li>
<li>CV: Компьютерное зрение</li>
<li>AI: Искусственный интеллект</li>
<li>ML: Машинное обучение</li>
<li>BIGDATA: Большие данные</li>
<li>VOICE & CHAT: Голосовые и чат-боты</li>
</ul>
Роботов pyOpenRPA невозможно отключить принудительно - управлять ими будете только вы!
<br><br>
Здесь представлены примеры роботов для таких специалистов как:
<ul>
<li>Экономистов</li>
<li>Финансистов</li>
<li>Продавцов</li>
<li>Закупщиков</li>
<li>Маркетологов</li>
<li>Кадровиков</li>
<li>Рекрутёров</li>
<li>Казначеев</li>
<li>Юристов</li>
<li>Бухгалтеров</li>
<li>Операционистов</li>
<li>Безопасников</li>
<li>ИТ специалистов</li>
</ul>
<h5>pyOpenRPA - роботы помогут!</h5>
</div>
</div>
<br>
<center>
<button class="ui primary basic button cp-hello-button-1">Свернуть</button>
</center>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
cp_hello_expand=function() {
$("button.cp-hello-button-1")[0].innerHTML="СВЕРНУТЬ ПРИВЕТСТВИЕ"
$("button.cp-hello-button-1")[0].setAttribute("onclick","cp_hello_collapse();")
$("div.cp-hello-description")[0].style["display"]=""
}
cp_hello_collapse=function() {
$("button.cp-hello-button-1")[0].innerHTML="РАЗВЕРНУТЬ ПРИВЕТСТВИЕ"
$("button.cp-hello-button-1")[0].setAttribute("onclick","cp_hello_expand();")
$("div.cp-hello-description")[0].style["display"]="none"
}
cp_hello_expand();
});
</script>

@ -0,0 +1,4 @@
$(document).ready(function() {
$('#test8-progress').progress();
});

@ -0,0 +1,3 @@
Ник;Полное имя;Логика;Язык;Время(мин);Решение
@floidd;Влад Клычков;5;9;0;Отказ
@floidd;Влад Клычков;8;8;0;Отказ
1 Ник Полное имя Логика Язык Время(мин) Решение
2 @floidd Влад Клычков 5 9 0 Отказ
3 @floidd Влад Клычков 8 8 0 Отказ

@ -0,0 +1,39 @@
Button {
color: white;
background: rgb(82, 105, 95);
border-radius: 5px;
border-color: #FFFFFF #FFFFFF #FFFFFF;
border-width: 2px 2px;
padding: .5em 2em;
cursor: pointer;
}
Button:hover { background: rgb(53, 167, 110); }
i#first {
position: absolute;
top: 280px;
left: 135px;
}
i#second {
position: absolute;
top: 340px;
left: 135px;
}
h4 {
width: 100%;
text-align: center;
border-bottom: 5px solid teal;
line-height: 0.1em;
margin: 10px 0 20px;
}
h4 span {
background:#fff;
padding:0 7px;
}

@ -0,0 +1,74 @@
$('.ui.selection.dropdown')
.dropdown("clear")
;
var switchFlag = "1,";
document.getElementById("Vacancy1Status").innerHTML = "Робот на вакансию 1 оффлайн"
document.getElementById("Vacancy2Status").innerHTML = "Робот на вакансию 2 оффлайн"
var btn1 = document.createElement("Button")
btn1.innerHTML = "Старт"
btn1.onclick = function() {
if (switchFlag == "1,"){
document.getElementById("Vacancy1Status").innerHTML = "Робот на вакансию 1 онлайн"
}
else {
document.getElementById("Vacancy2Status").innerHTML = "Робот на вакансию 2 онлайн"
}
}
var btn2 = document.createElement("Button")
btn2.innerHTML = "Стоп"
btn2.onclick = function() {
if (switchFlag == "1,"){
document.getElementById("Vacancy1Status").innerHTML = "Робот на вакансию 1 оффлайн"
}
else {
document.getElementById("Vacancy2Status").innerHTML = "Робот на вакансию 2 оффлайн"
}
}
function get_data(i)
{
var link = document.createElement('a');
if (i == 'question') {
link.setAttribute('href', '/HR_officer01/questions');
link.setAttribute('download', 'Questions.csv');
link.click();
} else if (i == "results") {
link.setAttribute('href', '/HR_officer01/results');
link.setAttribute('download', 'All_results.csv');
link.click();
} else if (i == "logs"){
link.setAttribute('href', '/HR_officer01/logs');
link.setAttribute('download', 'log.txt');
link.click();
}
return false;
}
function bot_change() {
switchFlag = $('.ui.selection.dropdown').dropdown("get value")
if ($('.ui.selection.dropdown').dropdown("get value") == "1,") {
document.getElementById("conteiner").appendChild(btn1)
document.getElementById("conteiner").appendChild(btn2)
}
else if ($('.ui.selection.dropdown').dropdown("get value") == "0,"){
document.getElementById("conteiner").appendChild(btn1)
document.getElementById("conteiner").appendChild(btn2)
}
}

@ -0,0 +1,31 @@
Вопрос;Ответ1 ;Ответ2;Ответ3;Ответ4;Правильный ответ
Шмурдик боится как мышей, так и тараканов;Шмурдик не боится тараканов;Шмурдик боится мышей;Шмурдик боится мышей больше, чем тараканов, но и тараканов боится тоже;;2
Известно, что грымзик обязательно или полосат, или рогат, или то и другое вместе;Грымзик не может быть однотонным и безрогим одновременно;Грымзик не может быть безрогим;Грымзик не может быть полосатым и безрогим одновременно;;1
Если запырку отравить, то она сразу начнет пускать пузыри;Если запырка пускает пузыри, то она была отравлена;Если запырку не отравить, то она не будет пускать пузыри;Если запырка не пускает пузыри, то она не отравлена;;3
Все охлотушки умеют играть в шашки;Не бывает охлотушек, которые не умеют играть в шашки;Все, кто умеет играть в шашки, являются охлотушками;Не бывает охлотушек, которые умеют играть в шашки;;1
Дубараторы бывают либо хорошими, либо плохими\. Неправда, что этот дубаратор не плохой;Этот дубаратор хороший;Этот дубаратор средненький;Этот дубаратор плохой;;3
В природе обнаружено более десятка тиалей\. Все обнаруженные тиали сплошь красного цвета;По крайней мере некоторые из тиалей красного цвета;По крайней мере некоторые из тиалей зеленые;Некоторые тиали \(из тех, что уже обнаружены\) могут оказаться не красными;;1
Существуют шакалы с больной мухропендией\. Мухропендия может быть, или больной, или здоровой;Не всякий шакал может похвастаться здоровой мухропендией;У некоторых шакалов мухропендия нездоровая;Существуют шакалы со здоровой мухропендией;;2
Неправда, что наша тумельница большая и круглая;Наша тумельница маленькая и некруглая;Наша тумельница маленькая, или некруглая, или то и другое вместе;Наша тумельница маленькая, или некруглая, но не то и другое вместе;;2
Джон всегда либо урдит, либо мурлит;Джон иногда урдит и мурлит;Джон иногда урдит, иногда мурлит, а иногда не мурлит;Джон никогда не занимается одновременно и урдением, и мурлением;;3
Журналисты наврали, что бздыш болотный безграмотен и нахален;На самом деле бздыш болотный образован и тактичен;На самом деле бздыш болотный безграмотен, но не нахален;Те журналисты солгали;;3
Если тряхнуть перпелькой, то немедленно начнется стрельба\. За последний час стрельбы не было;В течении последнего часа перпелькой не трясли;В течении последнего часа перпелькой трясли;А нечего было трясти чем попало;;1
Огромный бутряк напугал деревенского старосту;Старосте приснился ночной кошмар;Староста попробовал некачественной выпивки;Староста был напуган;;3
Если почесать угубку за ухом, он начнет довольно шипеть\. Если угубок довольно зашипит, то молоко поблизости скиснет;Если не чесать угубка за ухом, то молоко поблизости не скиснет;Если почесать угубка за ухом, молоко поблизости скиснет;Молоко вдалеке никогда не скисает от чесания угубков;;2
Всех, кто громко обуривает, обязательно съедают\. Все ухмырки постоянно громко обуривают;Все, кто громко обуривает \- ухмырки;Всех ухмырков обязательно съедают;Некоторых ухмырков не съедают;;2
В реках близ Тимуграда обитает и вобла, и щука;В реках близ Тимуграда не бывает воблы;В реках близ Тимуграда обитает щука;В реках близ Тимуграда обитает только вобла и щука;;2
Выберите верное утверждение;Бизнес\-процесс ничем не отличается от алгоритма;Бизнес\-процесс является агрегацией алгоритмов;Алгоритм является агерацией бизнес\-процессов;;2
Выберите верное утверждение;Задача и цель \- это синонимы;"Задача, чтобы задавать\; Цель, чтобы целиться";Цель \- это итоговая метрика, задача \- это действие, которое направлено на достижение цели;;3
Как правильно произносится\?;пАйтон;пайтОн;питОн;пифОн;1
Что является операндом в выражении\: x \= 6 \* 8 \/ y;x,y,6,8;\=,\*,\/;6,8,\*;6,8,y,\=;1
Как определяется working directory при запуске процесса\?;По расположению запускаемого файла;По местонахождению папки с исходными кодами;По текущему установленному пути оболочки Shell\/cmd, из под которой происходит запуск;;3
Список является массивом\?;Да;Нет;;;2
Какое отличие между str и bytes\?;Отличий нет \- типы являются синонимами;Str является декодированным представлением bytes;Str является енкодированным представлением bytes;;2
Какая операция в GIT отвечает за скачивание перечня ревизий с сервера\?;git push;git pull;git fetch;;3
Какое отличие между многопроцессными и многопроцессорными вычислениями\?;Отличий нет;В многопроцессных вычислениях может использоваться только 1 процессор, а в многопроцессорных вычислениях всегда может использоваться от 2\-х и более процессоров;В многопроцессорных вычислениях может использоваться только 1 процессор, а в многопроцессных вычислениях может использоваться от 2\-х и более процессоров;;2
Что является эквивалентом выражения\: \(X and Y\) or \(Z and Y\);not X and not Y and not Z;\(not X or not Y\) and \(not Z or not Y\);\(X and Y\) or \(X and Y\);not \(not X or not Y\) or not \(not Z or not Y\);4
Какое число в десятичной системе счисления является образом числа 11 в пятиричной системе счисления\?;3;11;6;7;3
Укажите корректное определение для SQL иснтрукции: SELECT \* FROM T1 INNER JOIN T2 ON T1\.ID \=\= T2\.T1\_ID;Выбрать все строки из таблицы T1;Выбрать все строки из таблицы T1, по связке с которыми имеется строка в T2;Выбрать все строки из таблицы T1 и показать строки из таблицы T2, если по ним имеется связь с T1;Выбрать все строки из таблицы T2 и показать строки из таблицы T1, если по ним имеется связь с T2;2
Что делает команда SQL\: DELETE\?;Удаляет таблицу из БД;Чистит строки внутри таблицы;;;2
Чем отличается относительный путь от абсолютного\?;Нет разницы \- оба пути указывают на один и тот же файл;Абсолютный путь в любом окружении будет указывать на один и тот же файл\. Относительный \- нет;Относительный путь в любом окружении будет указывать на один и тот же файл\. Абсолютный \- нет;;2
Можно ли из ветки except повторно сгенерировать то же самое исключение exception \(тот же самый экземпляр\)\?;Нет \- исключение уже было сформировано, повторно его вызвать будет невозможно;Да \- исключение уже было сформировано, но его можно будет вызвать повторно;Наверное \- не понимаю как работает try \.\. Except;;2
1 Вопрос Ответ1 Ответ2 Ответ3 Ответ4 Правильный ответ
2 Шмурдик боится как мышей, так и тараканов Шмурдик не боится тараканов Шмурдик боится мышей Шмурдик боится мышей больше, чем тараканов, но и тараканов боится тоже 2
3 Известно, что грымзик обязательно или полосат, или рогат, или то и другое вместе Грымзик не может быть однотонным и безрогим одновременно Грымзик не может быть безрогим Грымзик не может быть полосатым и безрогим одновременно 1
4 Если запырку отравить, то она сразу начнет пускать пузыри Если запырка пускает пузыри, то она была отравлена Если запырку не отравить, то она не будет пускать пузыри Если запырка не пускает пузыри, то она не отравлена 3
5 Все охлотушки умеют играть в шашки Не бывает охлотушек, которые не умеют играть в шашки Все, кто умеет играть в шашки, являются охлотушками Не бывает охлотушек, которые умеют играть в шашки 1
6 Дубараторы бывают либо хорошими, либо плохими\. Неправда, что этот дубаратор не плохой Этот дубаратор хороший Этот дубаратор средненький Этот дубаратор плохой 3
7 В природе обнаружено более десятка тиалей\. Все обнаруженные тиали сплошь красного цвета По крайней мере некоторые из тиалей красного цвета По крайней мере некоторые из тиалей зеленые Некоторые тиали \(из тех, что уже обнаружены\) могут оказаться не красными 1
8 Существуют шакалы с больной мухропендией\. Мухропендия может быть, или больной, или здоровой Не всякий шакал может похвастаться здоровой мухропендией У некоторых шакалов мухропендия нездоровая Существуют шакалы со здоровой мухропендией 2
9 Неправда, что наша тумельница большая и круглая Наша тумельница маленькая и некруглая Наша тумельница маленькая, или некруглая, или то и другое вместе Наша тумельница маленькая, или некруглая, но не то и другое вместе 2
10 Джон всегда либо урдит, либо мурлит Джон иногда урдит и мурлит Джон иногда урдит, иногда мурлит, а иногда не мурлит Джон никогда не занимается одновременно и урдением, и мурлением 3
11 Журналисты наврали, что бздыш болотный безграмотен и нахален На самом деле бздыш болотный образован и тактичен На самом деле бздыш болотный безграмотен, но не нахален Те журналисты солгали 3
12 Если тряхнуть перпелькой, то немедленно начнется стрельба\. За последний час стрельбы не было В течении последнего часа перпелькой не трясли В течении последнего часа перпелькой трясли А нечего было трясти чем попало 1
13 Огромный бутряк напугал деревенского старосту Старосте приснился ночной кошмар Староста попробовал некачественной выпивки Староста был напуган 3
14 Если почесать угубку за ухом, он начнет довольно шипеть\. Если угубок довольно зашипит, то молоко поблизости скиснет Если не чесать угубка за ухом, то молоко поблизости не скиснет Если почесать угубка за ухом, молоко поблизости скиснет Молоко вдалеке никогда не скисает от чесания угубков 2
15 Всех, кто громко обуривает, обязательно съедают\. Все ухмырки постоянно громко обуривают Все, кто громко обуривает \- ухмырки Всех ухмырков обязательно съедают Некоторых ухмырков не съедают 2
16 В реках близ Тимуграда обитает и вобла, и щука В реках близ Тимуграда не бывает воблы В реках близ Тимуграда обитает щука В реках близ Тимуграда обитает только вобла и щука 2
17 Выберите верное утверждение Бизнес\-процесс ничем не отличается от алгоритма Бизнес\-процесс является агрегацией алгоритмов Алгоритм является агерацией бизнес\-процессов 2
18 Выберите верное утверждение Задача и цель \- это синонимы Задача, чтобы задавать\; Цель, чтобы целиться Цель \- это итоговая метрика, задача \- это действие, которое направлено на достижение цели 3
19 Как правильно произносится\? пАйтон пайтОн питОн пифОн 1
20 Что является операндом в выражении\: x \= 6 \* 8 \/ y x,y,6,8 \=,\*,\/ 6,8,\* 6,8,y,\= 1
21 Как определяется working directory при запуске процесса\? По расположению запускаемого файла По местонахождению папки с исходными кодами По текущему установленному пути оболочки Shell\/cmd, из под которой происходит запуск 3
22 Список является массивом\? Да Нет 2
23 Какое отличие между str и bytes\? Отличий нет \- типы являются синонимами Str является декодированным представлением bytes Str является енкодированным представлением bytes 2
24 Какая операция в GIT отвечает за скачивание перечня ревизий с сервера\? git push git pull git fetch 3
25 Какое отличие между многопроцессными и многопроцессорными вычислениями\? Отличий нет В многопроцессных вычислениях может использоваться только 1 процессор, а в многопроцессорных вычислениях всегда может использоваться от 2\-х и более процессоров В многопроцессорных вычислениях может использоваться только 1 процессор, а в многопроцессных вычислениях может использоваться от 2\-х и более процессоров 2
26 Что является эквивалентом выражения\: \(X and Y\) or \(Z and Y\) not X and not Y and not Z \(not X or not Y\) and \(not Z or not Y\) \(X and Y\) or \(X and Y\) not \(not X or not Y\) or not \(not Z or not Y\) 4
27 Какое число в десятичной системе счисления является образом числа 11 в пятиричной системе счисления\? 3 11 6 7 3
28 Укажите корректное определение для SQL иснтрукции: SELECT \* FROM T1 INNER JOIN T2 ON T1\.ID \=\= T2\.T1\_ID Выбрать все строки из таблицы T1 Выбрать все строки из таблицы T1, по связке с которыми имеется строка в T2 Выбрать все строки из таблицы T1 и показать строки из таблицы T2, если по ним имеется связь с T1 Выбрать все строки из таблицы T2 и показать строки из таблицы T1, если по ним имеется связь с T2 2
29 Что делает команда SQL\: DELETE\? Удаляет таблицу из БД Чистит строки внутри таблицы 2
30 Чем отличается относительный путь от абсолютного\? Нет разницы \- оба пути указывают на один и тот же файл Абсолютный путь в любом окружении будет указывать на один и тот же файл\. Относительный \- нет Относительный путь в любом окружении будет указывать на один и тот же файл\. Абсолютный \- нет 2
31 Можно ли из ветки except повторно сгенерировать то же самое исключение exception \(тот же самый экземпляр\)\? Нет \- исключение уже было сформировано, повторно его вызвать будет невозможно Да \- исключение уже было сформировано, но его можно будет вызвать повторно Наверное \- не понимаю как работает try \.\. Except 2

@ -0,0 +1,206 @@
import time
time.sleep(1)
splitter1 = 'Рабочий процесс'
splitter2 = 'Пример доступных данных'
splitter3 = 'Пример запуска роботов'
title = 'КАДРОВИК'
introHeader = 'Применение:'
introText = 'Робот-кадровик помогает проводить отбор кандидатов'
workflowHeader1 = 'Шаг 1: Размещение вакансии'
workflowDescription1 = 'Выполняет человек'
workflowHint1 = 'Размещение вакансии в telegram/HH.ru'
workflowHeader2 = 'Шаг 2: Тестирование'
workflowDescription2 = 'Выполняет робот'
workflowHint2 = 'Автоматическое тестирования кандидатов'
workflowHeader3 = 'Шаг 3: Подведение итогов'
workflowDescription3 = 'Выполняет робот'
workflowHint3 = 'Автоматическое подведение итогов тестирования'
workflowHeader4 = 'Шаг 4: Личное собеседование'
workflowDescription4 = 'Выполняет человек'
workflowHint4 = 'Собеседование с лучшими кандидатами'
workflowHeader5 = 'Шаг 5: Оформление будущего сотрудника'
workflowDescription5 = 'Выполняет человек'
workflowHint5 = 'Подписание трудового договора'
questionHeaderMsg = 'Вопросы'
questionDescriptionMsg = 'Список вопросов для первого этапа'
resultHeaderMsg = 'Результаты'
resultDescriptionMsg = 'Сводка по всем рекрутам'
logHeaderMsg = 'Лог'
logDescriptionMsg = 'Лог за текущие сутки'
dropdownInit = 'Выберите робота...'
dropdownRobot1 = 'Робот на вакансию 1'
dropdownRobot2 = 'Робот на вакансию 2'
html = f'''<!doctype html>
<html lang="en">
<head>
<title> Test </title>
<link rel="stylesheet" href="/HR_01/styles">
</head>
<body>
<div class="card" style="width:450px;" id="card1">
<div class="content">
<center><h1>{title}</h1></center><br>
<div class="ui message">
<div class="header">{introHeader}</div>
<p>{introText}</p>
</div>
<h4><span>{splitter1}</span></h4>
<div class="ui relaxed divided list">
<div class="item">
<i class="user icon"></i>
<div class="content">
<div class="header" data-tooltip="{workflowHint1}">{workflowHeader1}</div>
<div class="description">{workflowDescription1}</div>
</div>
</div>
<div class="item">
<i class="reddit alien icon"></i>
<div class="content">
<div class="header" data-tooltip="{workflowHint2}">{workflowHeader2}</div>
<div class="description">{workflowDescription2}</div>
</div>
</div>
<div class="item">
<i class="reddit alien icon"></i>
<div class="content">
<div class="header" data-tooltip="{workflowHint3}">{workflowHeader3}</div>
<div class="description">{workflowDescription3}</div>
</div>
</div>
<div class="item">
<i class="user icon"></i>
<div class="content">
<div class="header" data-tooltip="{workflowHint4}">{workflowHeader4}</div>
<div class="description">{workflowDescription4}</div>
</div>
</div>
<div class="item">
<i class="user icon"></i>
<div class="content">
<div class="header" data-tooltip="{workflowHint5}">{workflowHeader5}</div>
<div class="description">{workflowDescription5}</div>
</div>
</div>
</div>
<h4><span>{splitter2}</span></h4>
<div class="ui list">
<div class="item">
<i class="file icon"></i>
<div class="content">
<a class="header" onclick="get_data('question')">{questionHeaderMsg}</a>
<div class="description">{questionDescriptionMsg}</div>
</div>
</div>
<div class="item">
<i class="adress book icon"></i>
<div class="content">
<a class="header" onclick="get_data('results')">{resultHeaderMsg}</a>
<div class="description">{resultDescriptionMsg}</div>
</div>
</div>
<div class="item">
<i class="file alternate icon"></i>
<div class="content">
<a class="header" onclick="get_data('logs')">{logHeaderMsg}</a>
<div class="description">{logDescriptionMsg}</div>
</div>
</div>
</div>
<h4><span>{splitter3}</span></h4>
<div class="ui list">
<div class="item">
<i class="telegram icon"></i>
<div id="Vacancy1Status" class="content"></div>
</div>
<div class="item">
<i class="telegram icon"></i>
<div id="Vacancy2Status" class="content"></div>
</div>
</div>
<div class="ui selection dropdown" onchange="bot_change()">
<input type="hidden" name="bot">
<i class="dropdown icon"></i>
<div class="default text">{dropdownInit}</div>
<div class="menu">
<div class="item" data-value="1">{dropdownRobot1}</div>
<div class="item" data-value="0">{dropdownRobot2}</div>
</div>
</div>
<p><div id="conteiner"></div></p>
</div>
</div>
<script src="/HR_01/scripts"></script>
<script type="text/javascript" src="https://viewer.diagrams.net/js/viewer-static.min.js"></script>
</body>
</html>'''
from pyOpenRPA.Tools import CrossOS
from pyOpenRPA import Orchestrator # Import orchestrator main
from pyOpenRPA.Orchestrator.Server import app
import threading
from fastapi import Depends
from fastapi.responses import PlainTextResponse
from fastapi.responses import FileResponse
# Подключени файлов связанных с роботом-кадровиком01
@app.get(path="/HR_01/questions",tags=["HR_01"])
def get_file():
return FileResponse(CrossOS.PathStr("Demo\\HR_01\\Questions.csv"))
@app.get(path="/HR_01/results",tags=["HR_01"])
def get_file():
return FileResponse(CrossOS.PathStr("Demo\\HR_01\\All_results.csv"))
@app.get(path="/HR_01/logs",tags=["HR_01"])
def get_file():
return FileResponse(CrossOS.PathStr("Demo\\HR_01\\log.txt"))
@app.get(path="/HR_01/scripts",tags=["HR_01"])
def get_file():
return FileResponse(CrossOS.PathStr("Demo\\HR_01\\HR_officer01.js"))
@app.get(path="/HR_01/styles",tags=["HR_01"])
def get_file():
return FileResponse(CrossOS.PathStr("Demo\\HR_01\\HR_officer01.css"))
lCPManager = Orchestrator.Managers.ControlPanel(inControlPanelNameStr="HR_officer01ControlPanel",
inRefreshHTMLJinja2TemplatePathStr=CrossOS.PathStr("Demo\\HR_01\\index.html"), inJinja2TemplateRefreshBool = True)
with open(CrossOS.PathStr("Demo\\HR_01\\index.html"), 'w', encoding='UTF-8') as file:
file.write(html)

@ -0,0 +1,125 @@
<!doctype html>
<html lang="en">
<head>
<title> Test </title>
<link rel="stylesheet" href="/HR_01/styles">
</head>
<body>
<div class="card" style="width:450px;" id="card1">
<div class="content">
<center><h1>КАДРОВИК</h1></center><br>
<div class="ui message">
<div class="header">Применение:</div>
<p>Робот-кадровик помогает проводить отбор кандидатов</p>
</div>
<h4><span>Рабочий процесс</span></h4>
<div class="ui relaxed divided list">
<div class="item">
<i class="user icon"></i>
<div class="content">
<div class="header" data-tooltip="Размещение вакансии в telegram/HH.ru">Шаг 1: Размещение вакансии</div>
<div class="description">Выполняет человек</div>
</div>
</div>
<div class="item">
<i class="reddit alien icon"></i>
<div class="content">
<div class="header" data-tooltip="Автоматическое тестирования кандидатов">Шаг 2: Тестирование</div>
<div class="description">Выполняет робот</div>
</div>
</div>
<div class="item">
<i class="reddit alien icon"></i>
<div class="content">
<div class="header" data-tooltip="Автоматическое подведение итогов тестирования">Шаг 3: Подведение итогов</div>
<div class="description">Выполняет робот</div>
</div>
</div>
<div class="item">
<i class="user icon"></i>
<div class="content">
<div class="header" data-tooltip="Собеседование с лучшими кандидатами">Шаг 4: Личное собеседование</div>
<div class="description">Выполняет человек</div>
</div>
</div>
<div class="item">
<i class="user icon"></i>
<div class="content">
<div class="header" data-tooltip="Подписание трудового договора">Шаг 5: Оформление будущего сотрудника</div>
<div class="description">Выполняет человек</div>
</div>
</div>
</div>
<h4><span>Пример доступных данных</span></h4>
<div class="ui list">
<div class="item">
<i class="file icon"></i>
<div class="content">
<a class="header" onclick="get_data('question')">Вопросы</a>
<div class="description">Список вопросов для первого этапа</div>
</div>
</div>
<div class="item">
<i class="adress book icon"></i>
<div class="content">
<a class="header" onclick="get_data('results')">Результаты</a>
<div class="description">Сводка по всем рекрутам</div>
</div>
</div>
<div class="item">
<i class="file alternate icon"></i>
<div class="content">
<a class="header" onclick="get_data('logs')">Лог</a>
<div class="description">Лог за текущие сутки</div>
</div>
</div>
</div>
<h4><span>Пример запуска роботов</span></h4>
<div class="ui list">
<div class="item">
<i class="telegram icon"></i>
<div id="Vacancy1Status" class="content"></div>
</div>
<div class="item">
<i class="telegram icon"></i>
<div id="Vacancy2Status" class="content"></div>
</div>
</div>
<div class="ui selection dropdown" onchange="bot_change()">
<input type="hidden" name="bot">
<i class="dropdown icon"></i>
<div class="default text">Выберите робота...</div>
<div class="menu">
<div class="item" data-value="1">Робот на вакансию 1</div>
<div class="item" data-value="0">Робот на вакансию 2</div>
</div>
</div>
<p><div id="conteiner"></div></p>
</div>
</div>
<script src="/HR_01/scripts"></script>
<script type="text/javascript" src="https://viewer.diagrams.net/js/viewer-static.min.js"></script>
</body>
</html>

@ -0,0 +1,31 @@
from pyOpenRPA import Orchestrator
from pyOpenRPA.Orchestrator.Managers import ControlPanel
from pyOpenRPA.Orchestrator.Managers import Process
from pyOpenRPA.Tools import CrossOS
import os
import sys
import socket
import datetime
import urllib.parse
import time
g_cp_name_str = "CP_JUPYTER"
g_repo_name_str = os.path.abspath(__file__).split("\\")[-5]
g_repo_package_path_str = os.path.abspath("\\".join(os.path.abspath(__file__).split("\\")[:-1]))
#g_repo_package_path_str = os.path.join(g_repo_path_str, "Orchestrator","Demo","JUPITER_01")
#g_repo_package_path_str = os.path.join(g_repo_package_path_str, g_cp_name_str)
# User settings
g_host_str = socket.gethostname().upper() # Identify PC
time.sleep(2)
g_control_panel = ControlPanel(inControlPanelNameStr=g_cp_name_str,
inRefreshHTMLJinja2TemplatePathStr=os.path.join(g_repo_package_path_str, "html_jinja2.xhtml"),
inJinja2TemplateRefreshBool=True,inRobotNameStr=g_repo_name_str)
g_jinja_context_dict = {"settings": sys.modules[__name__],
"urllib_parse_quote_plus": urllib.parse.quote_plus, "g_host_str": g_host_str, "g_repo_name_str": g_repo_name_str}
g_control_panel.Jinja2DataUpdateDictSet(inJinja2DataUpdateDict=g_jinja_context_dict)
g_control_panel.InitJSJinja2TemplatePathSet(inJinja2TemplatePathStr=os.path.join(g_repo_package_path_str, "js.js"))

@ -0,0 +1,33 @@
<div class="card" style=""">
<div class="content">
<div class="right floated mini ui ">
{{settings.g_cp_name_str}}
</div>
<div class="header">
ИТ - JUPYTER
</div>
<div class="ui divider"></div>
<div class="meta">
<div>Рабочая область PYTHON JUPYTER. <br> Сотрудник: {{UserInfoDict["UserNameUpperStr"]}}</div>
<div class="ui divider"></div>
<div class="description">
<button class="massive ui button" >
Открыть в новой вкладке
</button>
</div>
</div>
<div class="extra content">
</div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
});
</script>

@ -0,0 +1,4 @@
$(document).ready(function() {
$('#test8-progress').progress();
});

@ -0,0 +1,23 @@
from pyOpenRPA import Orchestrator
from pyOpenRPA.Orchestrator.Managers import ControlPanel
import time
import os
import sys
import socket
import datetime
import urllib.parse
time.sleep(1.5)
g_cp_name_str = "OPER_00"
g_repo_name_str = os.path.abspath(__file__).split("\\")[-5]
g_repo_package_cp_path_str = os.path.abspath("\\".join(os.path.abspath(__file__).split("\\")[:-1]))
# User settings
g_host_str = socket.gethostname().upper() # Identify PC
g_control_panel = ControlPanel(inControlPanelNameStr=g_cp_name_str,
inRefreshHTMLJinja2TemplatePathStr=os.path.join(g_repo_package_cp_path_str, "html_jinja2.xhtml"),
inJinja2TemplateRefreshBool=True,inRobotNameStr=g_repo_name_str)
g_jinja_context_dict = {"settings": sys.modules[__name__],
"urllib_parse_quote_plus": urllib.parse.quote_plus, "g_host_str": g_host_str, "g_repo_name_str": g_repo_name_str}
g_control_panel.Jinja2DataUpdateDictSet(inJinja2DataUpdateDict=g_jinja_context_dict)

@ -0,0 +1,85 @@
<div class="card" style="width: 450px;"">
<div class="content">
<div class="right floated mini ui ">
OPER_00
</div>
<div class="header">
ОПЕРАЦИОНИСТ
</div>
<div class="meta">
<div>Робот бухгалтер.</div>
Сотрудник: {{UserInfoDict["UserNameUpperStr"]}}
</div>
<div class="ui divider"></div>
<div class="description">
<span style="font-weight: bold;">Статус за 05.04.2022:</span>
<div class="ui vertical fluid steps">
<div class="completed step">
<i class="arrow circle right card icon"></i>
<div class="content">
<div class="title">Шаг 1: Выгрузка</div>
<div class="description">Ежедневно в 08:22 (STARTED)</div>
</div>
</div>
<div class="active step">
<i class="arrow circle right card icon"></i>
<div class="content">
<div class="title">Шаг 2: Опрос</div>
<div class="description">Производить до 12:00</div>
</div>
</div>
<div class="step">
<i class="hourglass half card icon"></i>
<div class="content">
<div class="title">Шаг 3: Отчет</div>
<div class="description">Отправка вручную в период с 12:00 до 12:30</div>
</div>
</div>
</div>
<div class="ui divider"></div>
<div class="ui placeholder">
<div class="image header">
<div class="line"></div>
<div class="line"></div>
</div>
<div class="paragraph">
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$('.ui.accordion.per').accordion();
per_log_download = function () {
var l_now = new Date();
l_month_str = String(l_now.getMonth()+1)
if (l_month_str.length == 1){ l_month_str = "0"+l_month_str }
l_day_str = String(l_now.getDate())
if (l_day_str.length == 1){ l_day_str = "0"+l_day_str }
l_log_date_str = String(l_now.getFullYear())+"_"+l_month_str+"_"+l_day_str
l_log_date_str = window.prompt("Укажите дату лог файла для скачивания (формат: гггг_мм_дд)", l_log_date_str);
if (l_log_date_str != null) {
l_url_str = "/logs/"+l_log_date_str+".log"
window.open(l_url_str, '_blank');
}
}
</script>

@ -0,0 +1,207 @@
import time
time.sleep(1)
splitter1 = 'Рабочий процесс'
splitter2 = 'Окно настройки робота'
title = 'КАЗНАЧЕЙ'
introHeader = 'Применение:'
introText = 'Робот-казначей выполняет выгрузку банковских выписок'
workflowHeader1 = 'Шаг 1: Авторизация в клиент-банке'
workflowDescription1 = 'Выполняет робот'
workflowHint1 = 'Авторизация на сайте банка'
workflowHeader2 = 'Шаг 2: Выгрузка выписок'
workflowDescription2 = 'Выполняет робот'
workflowHint2 = 'Скачивание выписок с сайта банка'
workflowHeader3 = 'Шаг 3: Перенаправление выписок'
workflowDescription3 = 'Выполняет робот'
workflowHint3 = 'Например, размещение в сетевой папке'
dropBankTitle = 'Выберите банк...'
dropBank1 = 'Сбербанк'
dropBank2 = 'ВТБ'
dropBank3 = 'Альфа'
dropBankAccountTitle = 'Выберите счет'
dropBankAccount1 = 'Счет 1'
dropBankAccount2 = 'Счет 2'
dropBankAccount3 = 'Счет 3'
dropBankAccountAll = 'Счет XY'
checkboxPerTitle = 'Период:'
checkboxPer1 = 'За день'
checkboxPer2 = 'За месяц'
checkboxFormTitle = ''
checkboxForm1 = 'PDF'
checkboxForm2 = '1C'
checkboxForm3 = 'WORD'
checkboxForm4 = 'EXCEL'
emailTitle = 'Укажите почту (необязательно):'
emailWindow = 'example@mail.ru'
btnDownload = 'Скачать'
btnEmail = 'Отправить'
html = f'''<!doctype html>
<html lang="en">
<head>
<title> Treasurer </title>
</head>
<body>
<div class="card" style="width:450px;" id="card1">
<div class="content">
<center><h1>{title}</h1></center><br>
<div class="ui message">
<div class="header">{introHeader}</div>
<p>{introText}</p>
</div>
<h4><span>{splitter1}</span></h4>
<div class="ui relaxed divided list">
<div class="item">
<i class="reddit alien icon"></i>
<div class="content">
<div class="header" data-tooltip="{workflowHint1}">{workflowHeader1}</div>
<div class="description">{workflowDescription1}</div>
</div>
</div>
<div class="item">
<i class="reddit alien icon"></i>
<div class="content">
<div class="header" data-tooltip="{workflowHint2}">{workflowHeader2}</div>
<div class="description">{workflowDescription2}</div>
</div>
</div>
<div class="item">
<i class="reddit alien icon"></i>
<div class="content">
<div class="header" data-tooltip="{workflowHint3}">{workflowHeader3}</div>
<div class="description">{workflowDescription3}</div>
</div>
</div>
</div>
<h4><span>{splitter2}</span></h4>
<div class="ui selection dropdown">
<input type="hidden" name="bank">
<i class="dropdown icon"></i>
<div class="default text">{dropBankTitle}</div>
<div class="menu">
<div class="item" data-value="2">{dropBank1}</div>
<div class="item" data-value="1">{dropBank2}</div>
<div class="item" data-value="0">{dropBank3}</div>
</div>
</div><br><br>
<select class="ui fluid search dropdown" style="font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif" multiple="">
<option value="">{dropBankAccountTitle}</option>
<option value="All">{dropBankAccountAll}</option>
<option value="Ch1">{dropBankAccount1}</option>
<option value="Ch2">{dropBankAccount2}</option>
<option value="Ch3">{dropBankAccount3}</option>
</select><br>
<div class="ui form">
<div class="inline fields">
<label>{checkboxPerTitle}</label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="time">
<label>{checkboxPer1}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="time">
<label>{checkboxPer2}</label>
</div>
</div>
</div>
</div>
<div class="ui form">
<div class="inline fields">
<label>{checkboxFormTitle}</label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="format">
<label>{checkboxForm1}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="format">
<label>{checkboxForm2}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="format">
<label>{checkboxForm3}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="format">
<label>{checkboxForm4}</label>
</div>
</div>
</div>
</div>
<div><b>{emailTitle}</b></div>
<p><div class="ui input focus">
<input type="text" placeholder="{emailWindow}">
</div></p>
<button class="ui right labeled icon button"><i class="download icon"></i> {btnDownload} </button>
<button class="ui right labeled icon button"><i class="envelope icon"></i> {btnEmail} </button>
</div>
</div>
<script src="/TREASURER_01/scripts"></script>
</body>
</html>'''
from pyOpenRPA.Tools import CrossOS
from pyOpenRPA import Orchestrator # Import orchestrator main
from pyOpenRPA.Orchestrator.Server import app
import threading
from fastapi import Depends
from fastapi.responses import PlainTextResponse
from fastapi.responses import FileResponse
# Подключени файлов связанных с роботом-казначеем01
@app.get(path="/TREASURER_01/scripts",tags=["TREASURER_01"])
def get_file():
return FileResponse("Demo\\TREASURER_01\\Treasurer01.js")
lCPManager = Orchestrator.Managers.ControlPanel(inControlPanelNameStr="Treasurer01ControlPanel",
inRefreshHTMLJinja2TemplatePathStr=CrossOS.PathStr("Demo\\TREASURER_01\\index.html"), inJinja2TemplateRefreshBool = True)
with open("Demo\\TREASURER_01\\index.html", 'w', encoding='UTF-8') as file:
file.write(html)

@ -0,0 +1,135 @@
<!doctype html>
<html lang="en">
<head>
<title> Treasurer </title>
</head>
<body>
<div class="card" style="width:450px;" id="card1">
<div class="content">
<center><h1>КАЗНАЧЕЙ</h1></center><br>
<div class="ui message">
<div class="header">Применение:</div>
<p>Робот-казначей выполняет выгрузку банковских выписок</p>
</div>
<h4><span>Рабочий процесс</span></h4>
<div class="ui relaxed divided list">
<div class="item">
<i class="reddit alien icon"></i>
<div class="content">
<div class="header" data-tooltip="Авторизация на сайте банка">Шаг 1: Авторизация в клиент-банке</div>
<div class="description">Выполняет робот</div>
</div>
</div>
<div class="item">
<i class="reddit alien icon"></i>
<div class="content">
<div class="header" data-tooltip="Скачивание выписок с сайта банка">Шаг 2: Выгрузка выписок</div>
<div class="description">Выполняет робот</div>
</div>
</div>
<div class="item">
<i class="reddit alien icon"></i>
<div class="content">
<div class="header" data-tooltip="Например, размещение в сетевой папке">Шаг 3: Перенаправление выписок</div>
<div class="description">Выполняет робот</div>
</div>
</div>
</div>
<h4><span>Окно настройки робота</span></h4>
<div class="ui selection dropdown">
<input type="hidden" name="bank">
<i class="dropdown icon"></i>
<div class="default text">Выберите банк...</div>
<div class="menu">
<div class="item" data-value="2">Сбербанк</div>
<div class="item" data-value="1">ВТБ</div>
<div class="item" data-value="0">Альфа</div>
</div>
</div><br><br>
<select class="ui fluid search dropdown" style="font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif" multiple="">
<option value="">Выберите счет</option>
<option value="All">Счет XY</option>
<option value="Ch1">Счет 1</option>
<option value="Ch2">Счет 2</option>
<option value="Ch3">Счет 3</option>
</select><br>
<div class="ui form">
<div class="inline fields">
<label>Период:</label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="time">
<label>За день</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="time">
<label>За месяц</label>
</div>
</div>
</div>
</div>
<div class="ui form">
<div class="inline fields">
<label></label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="format">
<label>PDF</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="format">
<label>1C</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="format">
<label>WORD</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="format">
<label>EXCEL</label>
</div>
</div>
</div>
</div>
<div><b>Укажите почту (необязательно):</b></div>
<p><div class="ui input focus">
<input type="text" placeholder="example@mail.ru">
</div></p>
<button class="ui right labeled icon button"><i class="download icon"></i> Скачать </button>
<button class="ui right labeled icon button"><i class="envelope icon"></i> Отправить </button>
</div>
</div>
<script src="/TREASURER_01/scripts"></script>
</body>
</html>

@ -0,0 +1,7 @@
$('.ui.dropdown')
.dropdown('clear')
;
$('.ui.dropdown')
.dropdown('clear')
;

@ -12,6 +12,28 @@ if lPyOpenRPASourceFolderPathStr != "": sys.path.insert(0,os.path.abspath(os.pat
from pyOpenRPA.Orchestrator import SettingsTemplate # Import functionallity
from pyOpenRPA.Tools import CrossOS
from pyOpenRPA import Orchestrator # Import orchestrator main
from pyOpenRPA.Orchestrator.Server import app
import threading
from fastapi import Depends
from fastapi.responses import PlainTextResponse
from fastapi.responses import FileResponse
# Пример создания функции на сервере (FASTAPI) /test/threads
@app.get(path="/test/threads",tags=["Test"],response_class=PlainTextResponse)
def Threads():# inAuthDict:dict=Depends(IdentifyAuthorize)
#def Threads(inAuthDict:dict=Depends(IdentifyAuthorize)):# Используй, если требуется авторизация
lThreadStr = ""
for thread in threading.enumerate():
lThreadStr+=f"ПОТОК: {thread.name}\n"
#print(thread.name)
return lThreadStr
#Run as administrator (ONLY FOR WINDOWS)
if not Orchestrator.OrchestratorIsAdmin() and CrossOS.IS_WINDOWS_BOOL:
Orchestrator.OrchestratorRerunAsAdmin()
@ -22,10 +44,11 @@ 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
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
Orchestrator.UACUpdate(inGSettings=gSettings, inADLoginStr="IMaslov", inADStr="", inADIsDefaultBool=True, inURLList=[])
Orchestrator.UACUpdate(inGSettings=gSettings, inADLoginStr="VLADICK", inADStr="", inADIsDefaultBool=True, inURLList=[])
# TEST Add Supertoken for the all access between robots
Orchestrator.UACSuperTokenUpdate(inGSettings=gSettings, inSuperTokenStr="1992-04-03-0643-ru-b4ff-openrpa52zzz")
# Add first interface!
@ -36,7 +59,7 @@ else:
# Restore DUMP
Orchestrator.OrchestratorSessionRestore(inGSettings=gSettings)
# Autoinit control panels starts with CP_
lPyModules = Orchestrator.OrchestratorPySearchInit(inGlobPatternStr="ControlPanel\\CP_*.py", inDefStr="SettingsUpdate", inDefArgNameGSettingsStr="inGSettings", inAsyncInitBool=True)
lPyModules = Orchestrator.OrchestratorPySearchInit(inGlobPatternStr="Demo\\*\\config.py", inAsyncInitBool=True)
# Call the orchestrator def
Orchestrator.Orchestrator(inGSettings=gSettings, inDumpRestoreBool=False)

@ -0,0 +1,24 @@
chcp 65001
@echo off
echo Формат использования init-python-env.cmd [имя запускаемого процесса.exe] [имя убиваемого процесса.exe]
echo Пример использования init-python-env.cmd orpa-rbt.exe orpa-rbt.exe
if [%1]==[] goto :python-env
goto create-exe
:create-exe
copy /Y "%~dp0..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe" "%~dp0..\Resources\WPy64-3720\python-3.7.2.amd64\%1"
if [%2]==[] goto :python-env
goto taskkill
:taskkill
taskkill /im "%2" /F /fi "username eq %username%"
goto :python-env
:python-env
set CD_PREV=%cd%
cd /d "%~dp0..\Resources\WPy64-3720\python-3.7.2.amd64"
set PATH=%cd%;%cd%\Scripts;%PATH%
cd /d "%~dp0..\Sources"
set PYTHONPATH=%cd%;%PYTHONPATH%
cd %CD_PREV%
:eof
echo Инициализация Python окружения прошла успешно!
@echo on

@ -0,0 +1,4 @@
chcp 65001
cd /d "%~dp0"
call init-python-env.cmd orpa-rbt.exe orpa-rbt.exe
cmd

@ -1,5 +1,5 @@
cd %~dp0
taskkill /im "orpa-orc.exe" /F /fi "username eq %username%"
copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-orc.exe
.\..\Resources\WPy64-3720\python-3.7.2.amd64\orpa-orc.exe "config.py"
chcp 65001
cd /d "%~dp0"
call init-python-env.cmd orpa-orc.exe orpa-orc.exe
orpa-orc.exe "config.py"
pause>nul

@ -0,0 +1 @@
https://github.com/intxcc/pyaudio_portaudio

File diff suppressed because it is too large Load Diff

Binary file not shown.

@ -0,0 +1,172 @@
#pyaudio loopback
import pyaudio
import wave
import os
defaultframes = 512
class textcolors:
if not os.name == 'nt':
blue = '\033[94m'
green = '\033[92m'
warning = '\033[93m'
fail = '\033[91m'
end = '\033[0m'
else:
blue = ''
green = ''
warning = ''
fail = ''
end = ''
recorded_frames = []
device_info = {}
useloopback = False
recordtime = 5
#Use module
p = pyaudio.PyAudio()
#Set default to first in list or ask Windows
try:
default_device_index = p.get_default_input_device_info()
except IOError:
default_device_index = -1
#Select Device
print (textcolors.blue + "Available devices:\n" + textcolors.end)
for i in range(0, p.get_device_count()):
info = p.get_device_info_by_index(i)
print (textcolors.green + str(info["index"]) + textcolors.end + ": \t %s \n \t %s \n" % (info["name"], p.get_host_api_info_by_index(info["hostApi"])["name"]))
if default_device_index == -1:
default_device_index = info["index"]
#Handle no devices available
if default_device_index == -1:
print (textcolors.fail + "No device available. Quitting." + textcolors.end)
exit()
#Get input or default
device_id = int(input("Choose device [" + textcolors.blue + str(default_device_index) + textcolors.end + "]: ") or default_device_index)
print ("")
#Get device info
try:
device_info = p.get_device_info_by_index(device_id)
except IOError:
device_info = p.get_device_info_by_index(default_device_index)
print (textcolors.warning + "Selection not available, using default." + textcolors.end)
#Choose between loopback or standard mode
is_input = device_info["maxInputChannels"] > 0
is_wasapi = (p.get_host_api_info_by_index(device_info["hostApi"])["name"]).find("WASAPI") != -1
if is_input:
print (textcolors.blue + "Selection is input using standard mode.\n" + textcolors.end)
else:
if is_wasapi:
useloopback = True;
print (textcolors.green + "Selection is output. Using loopback mode.\n" + textcolors.end)
else:
print (textcolors.fail + "Selection is input and does not support loopback mode. Quitting.\n" + textcolors.end)
exit()
recordtime = int(input("Record time in seconds [" + textcolors.blue + str(recordtime) + textcolors.end + "]: ") or recordtime)
#Open stream
channelcount = device_info["maxInputChannels"] if (device_info["maxOutputChannels"] < device_info["maxInputChannels"]) else device_info["maxOutputChannels"]
stream = p.open(format = pyaudio.paInt16,
channels = channelcount,
rate = int(device_info["defaultSampleRate"]),
input = True,
frames_per_buffer = defaultframes,
input_device_index = device_info["index"],
as_loopback = useloopback)
#Start Recording
print (textcolors.blue + "Starting..." + textcolors.end)
for i in range(0, int(int(device_info["defaultSampleRate"]) / defaultframes * recordtime)):
recorded_frames.append(stream.read(defaultframes))
print (".")
print (textcolors.blue + "End." + textcolors.end)
#Stop Recording
stream.stop_stream()
stream.close()
#Close module
p.terminate()
filename = input("Save as [" + textcolors.blue + "out.wav" + textcolors.end + "]: ") or "out.wav"
from pydub import AudioSegment
# Advanced usage, if you have raw audio data:
sound = AudioSegment(
# raw audio data (bytes)
data=b''.join(recorded_frames),
# 2 byte (16 bit) samples
sample_width=p.get_sample_size(pyaudio.paInt16),
# 44.1 kHz frame rate
frame_rate=int(device_info["defaultSampleRate"]),
# stereo
channels=channelcount
)
sound.export("out.mp3", format="mp3")
#waveFile = wave.open(filename, 'wb')
#waveFile.setnchannels(channelcount)
#waveFile.setsampwidth(p.get_sample_size(pyaudio.paInt16))
#waveFile.setframerate(int(device_info["defaultSampleRate"]))
#waveFile.writeframes(b''.join(recorded_frames))
#waveFile.close()
#pyaudio classic
from sys import byteorder
from array import array
from struct import pack
import pyaudio
import wave
THRESHOLD = 500
CHUNK_SIZE = 1024
FORMAT = pyaudio.paInt16
RATE = 44100
STOP_BOOL = False
import time
STOP_SEC_INT = 10
TIME_LAST = time.time()
def is_silent(snd_data):
"Returns 'True' if below the 'silent' threshold"
return max(snd_data) < THRESHOLD
def normalize(snd_data):
"Average the volume out"
MAXIMUM = 16384
times = float(MAXIMUM)/max(abs(i) for i in snd_data)
r = array('h')
for i in snd_data:
r.append(int(i*times))
return r
def trim(snd_data):
"Trim the blank spots at the start and end"
def _trim(snd_data):
snd_started = False
r = array('h')
for i in snd_data:
if not snd_started and abs(i)>THRESHOLD:
snd_started = True
r.append(i)
elif snd_started:
r.append(i)
return r
# Trim to the left
snd_data = _trim(snd_data)
# Trim to the right
snd_data.reverse()
snd_data = _trim(snd_data)
snd_data.reverse()
return snd_data

@ -1,13 +1,12 @@
Metadata-Version: 2.1
Name: pyOpenRPA
Version: 1.3.0
Version: 1.3.1
Summary: The powerful open source RPA platform for business
Home-page: https://pyopenrpa.ru/
Author: Ivan Maslov
Author-email: Ivan.Maslov@pyopenrpa.ru
License: Текст лицензии см. в файле: LICENSE.PDF (в корне) или по адресу: https://pyopenrpa.ru/license/oferta.pdf
Keywords: pyOpenRPA OpenRPA RPA Robot Automation Robotization OpenSource IT4Business
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: Free For Educational Use
Classifier: License :: Free For Home Use
@ -92,4 +91,3 @@ Ivan Maslov contacts (CEO & FOUNDER):
- Web: https://pyopenrpa.ru/
- Telegram: https://t.me/pyopenrpa
- WhatsApp | Telegram: +7 906 722 39 25 | @IvanMaslov

@ -1,11 +1,17 @@
pyOpenRPA-1.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
pyOpenRPA-1.3.0.dist-info/METADATA,sha256=V-qwlFCJ26LQShABSS0p5Tm9vxOXMYqrahL40zkthnI,4378
pyOpenRPA-1.3.0.dist-info/RECORD,,
pyOpenRPA-1.3.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pyOpenRPA-1.3.0.dist-info/WHEEL,sha256=qB97nP5e4MrOsXW5bIU5cUn_KSVr10EV0l-GCHG9qNs,97
pyOpenRPA-1.3.0.dist-info/top_level.txt,sha256=RPzwQXgYBRo_m5L3ZLs6Voh8aEkMeT29Xsul1w1qE0g,10
pyOpenRPA/Agent/A2O.py,sha256=PlIZZCTnVrYF2i6DSAi_KbzZfc2gtcBPmOerrEZq68U,1718
pyOpenRPA/Agent/O2A.py,sha256=o-5JF-415L69-vCg0COzK79sWs4btJwOkt53pqVhu0U,6210
pyOpenRPA-1.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
pyOpenRPA-1.3.1.dist-info/METADATA,sha256=oy_kdDRYfQHXr9MUOp_vO-WZ9AOOGvtMKIfYDanGAHY,4359
pyOpenRPA-1.3.1.dist-info/RECORD,,
pyOpenRPA-1.3.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pyOpenRPA-1.3.1.dist-info/WHEEL,sha256=qB97nP5e4MrOsXW5bIU5cUn_KSVr10EV0l-GCHG9qNs,97
pyOpenRPA-1.3.1.dist-info/top_level.txt,sha256=RPzwQXgYBRo_m5L3ZLs6Voh8aEkMeT29Xsul1w1qE0g,10
pyOpenRPA/.idea/inspectionProfiles/profiles_settings.xml,sha256=YXLFmX7rPNGcnKK1uX1uKYPN0fpgskYNe7t0BV7cqkY,174
pyOpenRPA/.idea/misc.xml,sha256=V-fQnOz-bYEZULgfbFgm-8mURphZrKfXMSd0wKjeEyA,188
pyOpenRPA/.idea/modules.xml,sha256=Q__U1JIA2cjxbLRXAv-SfYY00fZA0TNlpkkbY4s3ncg,277
pyOpenRPA/.idea/pyOpenRPA.iml,sha256=EXh41F8lqRiSBMVg-n2tKaEaHC6_3gGSuKkPJA12Na0,408
pyOpenRPA/.idea/vcs.xml,sha256=2HygA1oRAwc3VBf-irxHrX5JJG9DXuQwrN0BlubhoKY,191
pyOpenRPA/.idea/workspace.xml,sha256=6tJZehshdK4And6tEoUvkIB0KE7waL_NnTSkTYYAeFA,3802
pyOpenRPA/Agent/A2O.py,sha256=iSjYhWS2GrL6vdJ0Gc9PVtgfiqzqVwBc5jX0HsOrFdo,1713
pyOpenRPA/Agent/O2A.py,sha256=DpFWS1uKfFNsuyiSwj5LLI862oe58aJbQilRv3Hgm9o,6205
pyOpenRPA/Agent/Processor.py,sha256=xNZfQ_HcV-qm_x90tBLKYJqvnENiTqHSoUk2LhDfqWQ,6346
pyOpenRPA/Agent/__Agent__.py,sha256=rUvtEGEmlsd3ZujkQnPhL3mGaALmM2iNvfWBoF0Puc4,15167
pyOpenRPA/Agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@ -17,12 +23,12 @@ pyOpenRPA/Agent/__pycache__/__init__.cpython-37.pyc,,
pyOpenRPA/Agent/readme.md,sha256=QF_Bnv204OK3t1JUEhjfICkxFmSdX6bvaRl_HI6lH9I,19
pyOpenRPA/Info.md,sha256=u4Nv-PjniSF0Zlbtr6jEJX2vblK3_1zhSLNUgOdtDaA,85
pyOpenRPA/LICENSE.pdf,sha256=8L-9X_kCCt8yWvjy0E_xV-C6Im79hZnH7WcfZHRz8uM,4322139
pyOpenRPA/Orchestrator/BackwardCompatibility.py,sha256=IqVZygd8ab9X1gBfP7WImQrYqKy8snCSbThqrvdT5f0,39633
pyOpenRPA/Orchestrator/BackwardCompatibility.py,sha256=cYsvzgtjpGtD1qW8GRIq7WjHktsPZ0kefN47g9awhDc,39641
pyOpenRPA/Orchestrator/ControlPanel.py,sha256=OzS8HjG__8OZgqhajr8L8owyugXPuSLWHLtXuKdEP78,103
pyOpenRPA/Orchestrator/Core.py,sha256=OHa3mSC3_wRAizqrWBVjlR6ln4-xVVvBpOSnWl6qVvY,529
pyOpenRPA/Orchestrator/Managers/ControlPanel.py,sha256=PZzh_RUke8bIMGghCXTnEuTYICCwW71r2xXk7iTdUnU,17345
pyOpenRPA/Orchestrator/Managers/ControlPanel.py,sha256=VJoWVZoGE0dOQ-_jeezG8NNsk0mETUrJQztE8AfqxBY,17531
pyOpenRPA/Orchestrator/Managers/Git.py,sha256=VCXXc-enJhvrAJvYc8jHK9GcGe02meMdszU0tlJDHiQ,12879
pyOpenRPA/Orchestrator/Managers/Process.py,sha256=4LJNMrt61AJKI3p7SeXnUSEpA61BtqWVxaVYxYS_BaI,42221
pyOpenRPA/Orchestrator/Managers/Process.py,sha256=JW6cMsgAKmTtSkNMlW7zHhLh3N8lBdZI95wDadxCgtw,42537
pyOpenRPA/Orchestrator/Managers/__init__.py,sha256=4my0XiwmI_QLRQVhOzNvWTggCosF3tb2yRxGkehOCq0,71
pyOpenRPA/Orchestrator/Managers/__pycache__/ControlPanel.cpython-37.pyc,,
pyOpenRPA/Orchestrator/Managers/__pycache__/Git.cpython-37.pyc,,
@ -62,8 +68,9 @@ pyOpenRPA/Orchestrator/RobotScreenActive/__pycache__/Monitor.cpython-37.pyc,,
pyOpenRPA/Orchestrator/RobotScreenActive/__pycache__/Screen.cpython-37.pyc,,
pyOpenRPA/Orchestrator/RobotScreenActive/__pycache__/__init__.cpython-37.pyc,,
pyOpenRPA/Orchestrator/RobotScreenActive/__pycache__/__main__.cpython-37.pyc,,
pyOpenRPA/Orchestrator/Server.py,sha256=eYiXKvWsiBG_kfHtzdNX4Zu2xL2DjkJZXqBfqOgHAk4,38600
pyOpenRPA/Orchestrator/ServerSettings.py,sha256=wZe5aEUJOWYol8WzKUQBRq9JEnXj0Z39554PAIoqvbw,34326
pyOpenRPA/Orchestrator/Server.py,sha256=kejcfciuREYHc8Wl0ZgO6_1Kim0QtgfugPWYQq3VD54,12723
pyOpenRPA/Orchestrator/ServerBC.py,sha256=hwaFRtw1XN3D1rankd2HwH139-z5vfYvXO1I9skF0bI,23719
pyOpenRPA/Orchestrator/ServerSettings.py,sha256=1mHElwL46J_dT3qTF3-DaP_u4HekC4C5uGv2rRSngHc,35111
pyOpenRPA/Orchestrator/SettingsTemplate.py,sha256=dVL-JZpAUQP3LLXE_7IUuE7RkLTYBAA-RX4e5FZ_DU0,21499
pyOpenRPA/Orchestrator/Timer.py,sha256=HvYtEeH2Q5WVVjgds9XaBpWRmvZgwgBXurJDdVVq_T0,2097
pyOpenRPA/Orchestrator/Utils/LoggerHandlerDumpLogList.py,sha256=hD47TiOuKR-G8IWu9lJD2kG28qlH7YZV63i3qv1N5Dk,681
@ -71,11 +78,8 @@ pyOpenRPA/Orchestrator/Utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
pyOpenRPA/Orchestrator/Utils/__pycache__/LoggerHandlerDumpLogList.cpython-37.pyc,,
pyOpenRPA/Orchestrator/Utils/__pycache__/__init__.cpython-37.pyc,,
pyOpenRPA/Orchestrator/Web/Basic.py,sha256=pPH55rPwZz1ktpzNIcC51jeV2MgZI10Zf0Q0DncihDw,7757
pyOpenRPA/Orchestrator/Web/Index.js,sha256=YACiZAvjr6NmFlDhQu6urkJp49BX7L8WJU9p-MeIlCI,43508
pyOpenRPA/Orchestrator/Web/Index.xhtml,sha256=5JUAs5rEiU0XtHM9QO6EdNMBGt-W6QOVGY7xJ_HLPFM,19257
pyOpenRPA/Orchestrator/Web/__pycache__/Basic.cpython-37.pyc,,
pyOpenRPA/Orchestrator/Web/favicon.ico,sha256=6S8XwSQ_3FXPpaX6zYkf8uUewVXO9bHnrrDHEoWrEgw,112922
pyOpenRPA/Orchestrator/__Orchestrator__.py,sha256=LmEhNQkdK5SEIqql0ZM9PrjKgQ4-aQOiOheX83GbqmQ,207494
pyOpenRPA/Orchestrator/__Orchestrator__.py,sha256=E7kysFDJmcmBejKt8_0pk624U6ohLqM32CjvJ-FysoQ,214440
pyOpenRPA/Orchestrator/__init__.py,sha256=nJhjYtBXKOUNX_yNu1rRFk5y9cDz6AFiL0M6KgX_utQ,207
pyOpenRPA/Orchestrator/__main__.py,sha256=czJrc7_57WiO3EPIYfPeF_LG3pZsQVmuAYgbl_YXcVU,273
pyOpenRPA/Orchestrator/__pycache__/BackwardCompatibility.cpython-37.pyc,,
@ -84,6 +88,7 @@ pyOpenRPA/Orchestrator/__pycache__/Core.cpython-37.pyc,,
pyOpenRPA/Orchestrator/__pycache__/Processor.cpython-37.pyc,,
pyOpenRPA/Orchestrator/__pycache__/ProcessorOld.cpython-37.pyc,,
pyOpenRPA/Orchestrator/__pycache__/Server.cpython-37.pyc,,
pyOpenRPA/Orchestrator/__pycache__/ServerBC.cpython-37.pyc,,
pyOpenRPA/Orchestrator/__pycache__/ServerSettings.cpython-37.pyc,,
pyOpenRPA/Orchestrator/__pycache__/SettingsTemplate.cpython-37.pyc,,
pyOpenRPA/Orchestrator/__pycache__/Timer.cpython-37.pyc,,
@ -243,7 +248,7 @@ pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/package.js,sha256=BHq6qGCSMdDXDGJ
pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/package.json,sha256=Sy3ZVA4SxFzecOO88h1IVAnBiuDpRtXiUg6ME_YArI0,524
pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/semantic.css,sha256=aje_ma2N_7VT1k2QTOLeqr3SfQ3Cr-7vB05D2-uo-YQ,869203
pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/semantic.js,sha256=t-thIOFbQol3-vcB3C7kRbS49jG2_LC5dEp7NLxBpuA,759089
pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/semantic.min.css,sha256=f5Dpq5VRrbzn8POovxN9HR3S7LhHQIAiCKYz4PB4ZZA,628934
pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/semantic.min.css,sha256=o4apE2I4Au57fkNgduz1J0rhbTPc04IlvCzBmEeapGE,628944
pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/semantic.min.js,sha256=69cT25pJVUmAyniwWMglXlEOEeZYuhmD8Hbj58gCS7Y,275740
pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/themes/default/assets/fonts/brand-icons.eot,sha256=0CPFX94iBJXxOoe3KZXg35jI0ok1dymdGZ2lu_W0_gI,98640
pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/themes/default/assets/fonts/brand-icons.svg,sha256=IcTTApw6luFW48z0iPvWxbD_LpBQYBaIN5LGFInrfpU,508636
@ -264,11 +269,12 @@ pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/themes/default/assets/fonts/outli
pyOpenRPA/Resources/Web/Semantic-UI-CSS-master/themes/default/assets/images/flags.png,sha256=lNXH8WYTAcSm3Ekdct1VmgYgzZF6gm8N8bAju5bqnd0,28123
pyOpenRPA/Resources/Web/jQuery/jquery-3.1.1.min.js,sha256=HPMOWdIdSuVgr3FD9ZE-_MgiK8qk_MdQjrgCtfqp6U4,86713
pyOpenRPA/Resources/Web/orpa/favicon.ico,sha256=6S8XwSQ_3FXPpaX6zYkf8uUewVXO9bHnrrDHEoWrEgw,112922
pyOpenRPA/Resources/Web/orpa/footer.xhtml,sha256=5aNthu2jsINqpgo_BS8UDbRWhA3rlRtlQZIILv_4hKo,3190
pyOpenRPA/Resources/Web/orpa/header.xhtml,sha256=pqVM-N53bz4_DzbG4PoN7B7N7XECZWZe12qVlc68ylk,3617
pyOpenRPA/Resources/Web/orpa/footer.xhtml,sha256=xB-uUWvzdgD686l8rXWYOzwp3y7DKnZrvJeBS0ClPLI,3482
pyOpenRPA/Resources/Web/orpa/header.xhtml,sha256=kJF-9Ea9DV2v6Y3F0r3FvF3odfYmw14F5_SAbbq2uyk,3658
pyOpenRPA/Resources/Web/orpa/logo.png,sha256=7rulXo_C57xJQEaYWmAkChxXb6xbDW2zq-werzVbDbc,4899
pyOpenRPA/Resources/Web/orpa/orc.js,sha256=qlzbbewXWDFbNhNHVVvMEYQFyQbRA7zEHoeaV38fJoQ,43558
pyOpenRPA/Resources/Web/orpa/orc.xhtml,sha256=x6NdMv6HrjUt0rZsz61dJeMl3DNte1KkEULQRkGfsJg,19098
pyOpenRPA/Resources/Web/orpa/orc.js,sha256=aVKy_YTcpcOUiXcUyE1hyW7lpSmfFJY3sVjgEgMdXrw,38487
pyOpenRPA/Resources/Web/orpa/orc.xhtml,sha256=6WQ5fidSF8EW8PzqZ9AMVptVVCJAzQleqEFqfRIqQvo,19324
pyOpenRPA/Resources/Web/orpa/std.xhtml,sha256=lNg0WRW-sM5J2EGCkqtwqtOzv5n21KFZ8vHv9KlquEY,43547
pyOpenRPA/Resources/Web/orpa/styleset/Lato-Bold.woff2,sha256=roj8DXqWGDL4CVJ9ML05g6aGbUL2alat4j9UNoFZTbY,184912
pyOpenRPA/Resources/Web/orpa/styleset/Lato-Italic.woff2,sha256=RGV2Xy9u3c2tNP_XyrVZ5WvA515F4ZL4XpVisHcUgdw,195704
pyOpenRPA/Resources/Web/orpa/styleset/Lato-Regular.woff2,sha256=mDsMrzNuhUIhT8FwGaT8XgNghkuSgGyhTVXB_BwsWg8,182708
@ -303,8 +309,9 @@ pyOpenRPA/Resources/Web/orpa/styleset/sidebar.js,sha256=fnype3guWOx0kmyP7rL2I0mk
pyOpenRPA/Resources/Web/orpa/styleset/transition.js,sha256=esNkWoCwl6ksZewJkNDMLKCE4nDTdJZDJW3tdE7dlAo,36070
pyOpenRPA/Resources/Web/orpa/styleset/visibility.js,sha256=ulme0GrsuqHAHXTQunq96J0mESTl6Lux8msdK1_fcm4,44174
pyOpenRPA/Resources/Web/orpa/styleset/white-image.png,sha256=D7WJFGDtAIp_OXUU6Tz4zeYm_1yHRd8u7aTu0F35Kcw,5951
pyOpenRPA/Robot/Audio.py,sha256=kHvFnb3t_vnw-NjfSf1zaL3w1Th-984KMx1S8-6BX5I,22176
pyOpenRPA/Robot/Clipboard.py,sha256=QXClSH1ccCl8KfKbMY1Wk_SynEgfDZqZ_0h-OZYn4kA,1962
pyOpenRPA/Robot/Keyboard.py,sha256=3RCogucSWPCtq7YbFKIUvJOSBvFE04P6Lo3hjCGUHi8,26071
pyOpenRPA/Robot/Keyboard.py,sha256=0pe4C5vqYD9LRBm61dZmJ39lyNoI0BXMLCA0bWjvm8I,26589
pyOpenRPA/Robot/Mouse.py,sha256=R-2q5Q-nDhvvQjcQxFz8FRM9tHjhPUKdnK3KuPc_OZw,16361
pyOpenRPA/Robot/OrchestratorConnector.py,sha256=JUtdiUXCruqUqBD19gJBl9jk_b-tpWWx_v3MfBoKzoQ,20445
pyOpenRPA/Robot/README.md,sha256=bwiTAygxuMZzBlwpsndw2QgxA2smIjUyOPZnsnR341k,1623
@ -312,7 +319,7 @@ pyOpenRPA/Robot/Screen.py,sha256=nH80ghKw7OseNF_fVwfUrih0y9o1Ul2VPEl_WT5b598,454
pyOpenRPA/Robot/SettingsTemplate.py,sha256=Rp5XPeV2I4tCS2uf4Zkqm_ERJ6pZMg4-e5_lMqGJYLk,1453
pyOpenRPA/Robot/Test.py,sha256=qXr990nXiFZX5SNv6QN9GLb_U4HZRmJnbZR2qSnwilY,2878
pyOpenRPA/Robot/UIDesktop.py,sha256=WLy8wUVDFSUN5S7hXUjijQIH0B71c5tj-pBCVex7uFc,125314
pyOpenRPA/Robot/UIWeb.py,sha256=c3VA8IG2ELuGaWskyFFCbJalBKdDON2sjWhvHjR-2Dk,32353
pyOpenRPA/Robot/UIWeb.py,sha256=DLr6cPvFdUmGMTKGLVrksaVKHqBmbxzFkkT4qT0WLfo,32589
pyOpenRPA/Robot/Utils/JSONNormalize.py,sha256=aIuVzuZDazhxkCOzoOjfhHVz66mp2FWdfPv5E7KWF5Y,3890
pyOpenRPA/Robot/Utils/ProcessBitness.py,sha256=NvzuTxNWL_EMmdU1Isu0bUck1Iud0Kkzn8GsVCzIAAM,4591
pyOpenRPA/Robot/Utils/ProcessCommunicator.py,sha256=8GfmLnOvAdosmt7YNT86uEV9cjhKippssCX62wOMJwM,8039
@ -328,6 +335,7 @@ pyOpenRPA/Robot/Utils/__pycache__/__init__.cpython-37.pyc,,
pyOpenRPA/Robot/Window.py,sha256=UJl-sg4RvvJ35aG9jZOzqGVwE15XK7qPHqoOBD13xFk,431
pyOpenRPA/Robot/__init__.py,sha256=eYyMsU33rGlEZcsO-MH9UtXs47UcTCtg4Uc4lxKmmQo,255
pyOpenRPA/Robot/__main__.py,sha256=xtzw5siZZJV2Nmn-zXPZY5u3Tk0wxe-l8lxq3VPEEiI,2029
pyOpenRPA/Robot/__pycache__/Audio.cpython-37.pyc,,
pyOpenRPA/Robot/__pycache__/Clipboard.cpython-37.pyc,,
pyOpenRPA/Robot/__pycache__/Keyboard.cpython-37.pyc,,
pyOpenRPA/Robot/__pycache__/Mouse.cpython-37.pyc,,
@ -343,10 +351,8 @@ pyOpenRPA/Robot/__pycache__/__main__.cpython-37.pyc,,
pyOpenRPA/Studio/JSONNormalize.py,sha256=g0Z8G2wojCgTAdZtyRiCfe0_FHSeAi72Va7R7mk27gg,3347
pyOpenRPA/Studio/ProcessCommunicator.py,sha256=HD3XASJae31_HV3OznFe8E2MgZFXnwt7YveVN82M8nU,7912
pyOpenRPA/Studio/RobotConnector.py,sha256=CYO0dQoqfs44SYD_VZ_TJh3WFu_DXigHBLHj4GJ2Icc,5038
pyOpenRPA/Studio/Studio.py,sha256=6He68tum4IyMSOv9VXsH2hmvMDnnCf4CDI66L7Wpl_Q,8739
pyOpenRPA/Studio/Studio.py,sha256=pf9feDUyvKRJfhREkxK9uN49dWawYdn3ImkdxuH5mM0,18518
pyOpenRPA/Studio/ValueVerify.py,sha256=ObskxU4fOMoCGw74_nzYt6-a5jjrAckb3sdBLYyhYxY,777
pyOpenRPA/Studio/Web/Index.xhtml,sha256=X_fLnzxdcJ8ML9VRORpaMmQwlu1Pcr0CR2Krzt65Hes,48119
pyOpenRPA/Studio/Web/favicon.ico,sha256=6S8XwSQ_3FXPpaX6zYkf8uUewVXO9bHnrrDHEoWrEgw,112922
pyOpenRPA/Studio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
pyOpenRPA/Studio/__main__.py,sha256=_57Rnq9DKbmmlpGFqIwVrWn_LRcU8jjmMTOny4_zlP8,308
pyOpenRPA/Studio/__pycache__/JSONNormalize.cpython-37.pyc,,
@ -357,7 +363,7 @@ pyOpenRPA/Studio/__pycache__/ValueVerify.cpython-37.pyc,,
pyOpenRPA/Studio/__pycache__/__init__.cpython-37.pyc,,
pyOpenRPA/Studio/__pycache__/__main__.cpython-37.pyc,,
pyOpenRPA/Tools/CrossOS.py,sha256=6yq7cit6dgs0KCk6zknoy699r7a3gC0UrnYYe-ZrobM,3539
pyOpenRPA/Tools/Debugger.py,sha256=2slOSaPnUAwfDlyjhaqqhhchXUBgEKQImTzXagJt4Jw,535
pyOpenRPA/Tools/Debugger.py,sha256=nLDzlghYnRghULjGze6_yVg5Du25I8K5s3uhFw1hh3w,570
pyOpenRPA/Tools/License.py,sha256=r4z9HExUzpk2Q_6WhzgUnHlOPPHK1MLevtdlLwtZ7Hk,10378
pyOpenRPA/Tools/RobotDB/ExcelCom.py,sha256=hp0dvXOEC7Au00ueh7pqxkdixV-PC-km7tCt-wRunYs,343
pyOpenRPA/Tools/RobotDB/HowToUse,sha256=TUXPZAFcse-PSlKFM6jcaYuuZZEmXOSSvgeqrbmIDoc,1473
@ -401,7 +407,14 @@ pyOpenRPA/Tools/__pycache__/StopSafe.cpython-37.pyc,,
pyOpenRPA/Tools/__pycache__/Template.cpython-37.pyc,,
pyOpenRPA/Tools/__pycache__/Usage.cpython-37.pyc,,
pyOpenRPA/Tools/__pycache__/__init__.cpython-37.pyc,,
pyOpenRPA/Utils/Dictionary.py,sha256=dXmsFZoufUxpf_f_grQIsWcto1qwLCRrxN6m93NJCu8,2261
pyOpenRPA/Utils/Disk.py,sha256=MKD9nIGKNOJeQow1qGDTEz-GffkFbWaNxf3Hj76AyTo,364
pyOpenRPA/Utils/Network.py,sha256=UHTvc_ut_Fimeg-tBISUYPgkLKQTBkte3EsIh8BW7a8,600
pyOpenRPA/Utils/Render.py,sha256=VCU5cCCwyCFvMmRQwq7MGllgqzeDAp_s8om3UChIoPk,6703
pyOpenRPA/Utils/Text.py,sha256=ZFIIPaCB8lJ7kwF9bwpVR-lmf_p1o6jz8npBdl_7eNc,214
pyOpenRPA/Utils/__pycache__/Dictionary.cpython-37.pyc,,
pyOpenRPA/Utils/__pycache__/Disk.cpython-37.pyc,,
pyOpenRPA/Utils/__pycache__/Render.cpython-37.pyc,,
pyOpenRPA/__init__.py,sha256=adiLDsNcgYHaK1h_sStFVU0UEQpYGgFRvPiLmmrtvnU,155
pyOpenRPA/Utils/__pycache__/Text.cpython-37.pyc,,
pyOpenRPA/__init__.py,sha256=Uj0nKJ5lCErziv4h_RMQxX1M13lF_oHY2NJIJ9UndSI,155
pyOpenRPA/__pycache__/__init__.cpython-37.pyc,,

@ -15,7 +15,7 @@ def _A2ODataSend(inGSettings, inDataDict):
lProtocolStr= "https" if inGSettings["OrchestratorDict"]["IsHTTPSBool"] else "http"
lHostStr = inGSettings["OrchestratorDict"]["HostStr"]
lPortInt = inGSettings["OrchestratorDict"]["PortInt"]
lURLStr=f"{lProtocolStr}://{lHostStr}:{lPortInt}/pyOpenRPA/Agent/A2O"
lURLStr=f"{lProtocolStr}://{lHostStr}:{lPortInt}/orpa/agent/a2o"
lResponse = requests.post(url= lURLStr, cookies = {"AuthToken":inGSettings["OrchestratorDict"]["SuperTokenStr"]}, json=inDataDict, timeout=inGSettings["A2ODict"]["ConnectionTimeoutSecFloat"])
except Exception as e:
if lL: lL.exception(f"A2O Error handler.")

@ -30,7 +30,7 @@ def O2A_Loop(inGSettings):
lProtocolStr= "https" if inGSettings["OrchestratorDict"]["IsHTTPSBool"] else "http"
lHostStr = inGSettings["OrchestratorDict"]["HostStr"]
lPortInt = inGSettings["OrchestratorDict"]["PortInt"]
lURLStr=f"{lProtocolStr}://{lHostStr}:{lPortInt}/pyOpenRPA/Agent/O2A"
lURLStr=f"{lProtocolStr}://{lHostStr}:{lPortInt}/orpa/agent/o2a"
lDataDict = { "HostNameUpperStr": inGSettings["AgentDict"]["HostNameUpperStr"], "UserUpperStr": inGSettings["AgentDict"]["UserUpperStr"], "ActivityLastGUIDStr": lActivityLastGUIDStr}
lResponse = requests.post(url= lURLStr, cookies = {"AuthToken":inGSettings["OrchestratorDict"]["SuperTokenStr"]}, json=lDataDict, timeout=inGSettings["O2ADict"]["ConnectionTimeoutSecFloat"])
lCEPhaseFastTimeLastGoodFloat = time.time()

@ -540,5 +540,5 @@ def Update(inGSettings):
# "URLIndexStr"
if "URLIndexStr" not in inGSettings["ServerDict"]:
inGSettings["ServerDict"]["URLIndexStr"] = "/"
if lL: lL.warning(
f"Обратная совместимость (v1.2.11 -> v1.2.12): ServerDict > URLIndexStr и URLIndexStr = /") # Log about compatibility
if lL: lL.warning(
f"Обратная совместимость (v1.2.11 -> v1.2.12): ServerDict > URLIndexStr и URLIndexStr = /") # Log about compatibility

@ -86,9 +86,9 @@ class ControlPanel():
if inControlPanelNameStr in Orchestrator.GSettingsGet()["ServerDict"]["ControlPanelDict"]:
raise Exception(f"Ошибка: Ранее уже была инициализирована панель управления с идентификатором: {inControlPanelNameStr}. Устраните ошибку и перезапустите оркестратор")
Orchestrator.GSettingsGet()["ServerDict"]["ControlPanelDict"][inControlPanelNameStr] = self
self.mControlPanelNameStr = inControlPanelNameStr # Set the name of the control panel
self.RefreshHTMLJinja2TemplatePathSet(inJinja2TemplatePathStr = inRefreshHTMLJinja2TemplatePathStr)
self.mJinja2TemplateRefreshBool = inJinja2TemplateRefreshBool
self.mControlPanelNameStr = inControlPanelNameStr # Set the name of the control panel
self.mRobotNameStr = inRobotNameStr # Set the robot name for robot it execute
def Jinja2DataUpdateDictSet(self, inJinja2DataUpdateDict):
@ -116,7 +116,7 @@ class ControlPanel():
self.mRefreshHTMLJinja2Env = jinja2.Environment(loader=self.mRefreshHTMLJinja2Loader, trim_blocks=True)
self.mRefreshHTMLJinja2Template = self.mRefreshHTMLJinja2Env.get_template(lTemplateFileNameStr)
except Exception as e:
Orchestrator.OrchestratorLoggerGet().exception("Ошибка при инициализации Jinja2")
Orchestrator.OrchestratorLoggerGet().exception(f"Ошибка при инициализации Jinja2 ({inJinja2TemplatePathStr}). Панель управления: {self.mControlPanelNameStr}")
def RefreshHTMLJinja2StrGenerate(self, inDataDict):
"""
@ -145,7 +145,7 @@ class ControlPanel():
self.mInitJSJinja2Env = jinja2.Environment(loader=self.mInitJSJinja2Loader, trim_blocks=True)
self.mInitJSJinja2Template = self.mInitJSJinja2Env.get_template(lTemplateFileNameStr)
except Exception as e:
Orchestrator.OrchestratorLoggerGet().exception("Ошибка при инициализации Jinja2")
Orchestrator.OrchestratorLoggerGet().exception(f"Ошибка при инициализации Jinja2 ({inJinja2TemplatePathStr}). Панель управления: {self.mControlPanelNameStr}")
def InitJSJinja2StrGenerate(self, inDataDict):
"""

@ -393,7 +393,7 @@ class Process():
lL.info(f"Модуль Managers.Process ({self.mAgentHostNameStr}, {self.mAgentUserNameStr}, {self.mProcessNameWOExeStr}): Состояние процесса изменилось на {self.mStatusStr})")
def StatusCheck(self):
def StatusCheck(self, inTimeoutSecFloat=9.0, inRaiseExceptionBool=False):
"""
Check if process is alive. The def will save the manual flag is exists. Don't wait mute but set mute if it is not set.
@ -405,21 +405,27 @@ class Process():
#self.MuteWait()
self.mAgentMuteBool=True
lGUIDStr = __Orchestrator__.AgentActivityItemAdd(inHostNameStr=self.mAgentHostNameStr,inUserStr=self.mAgentUserNameStr,inActivityItemDict=lActivityItemUserProcessList)
lUserProcessList = __Orchestrator__.AgentActivityItemReturnGet(inGUIDStr=lGUIDStr)
if self.mProcessNameWOExeStr.upper() in lUserProcessList:
if self.mStatusStr == "1_STOPPED_MANUAL": self.mStatusStr = "5_STARTED_MANUAL"; lLogBool=True
if self.mStatusStr == "0_STOPPED": self.mStatusStr = "4_STARTED"; lLogBool=True
if self.mStatusStr is None: self.mStatusStr = "4_STARTED"; lLogBool=True
else:
if self.mStatusStr == "2_STOP_SAFE": self.mStatusStr = "0_STOPPED"; lLogBool = True
if self.mStatusStr == "3_STOP_SAFE_MANUAL": self.mStatusStr = "1_STOPPED_MANUAL"; lLogBool = True
if self.mStatusStr == "5_STARTED_MANUAL": self.mStatusStr = "1_STOPPED_MANUAL"; lLogBool=True
if self.mStatusStr == "4_STARTED": self.mStatusStr = "0_STOPPED"; lLogBool=True
if self.mStatusStr is None: self.mStatusStr = "0_STOPPED"; lLogBool=True
# Log info about process
if lLogBool == True: self.StatusChangeLog()
self.mAgentMuteBool = False
return self.mStatusStr
try:
lUserProcessList = __Orchestrator__.AgentActivityItemReturnGet(inGUIDStr=lGUIDStr,inTimeoutSecFloat=inTimeoutSecFloat)
if self.mProcessNameWOExeStr.upper() in lUserProcessList:
if self.mStatusStr == "1_STOPPED_MANUAL": self.mStatusStr = "5_STARTED_MANUAL"; lLogBool=True
if self.mStatusStr == "0_STOPPED": self.mStatusStr = "4_STARTED"; lLogBool=True
if self.mStatusStr is None: self.mStatusStr = "4_STARTED"; lLogBool=True
else:
if self.mStatusStr == "2_STOP_SAFE": self.mStatusStr = "0_STOPPED"; lLogBool = True
if self.mStatusStr == "3_STOP_SAFE_MANUAL": self.mStatusStr = "1_STOPPED_MANUAL"; lLogBool = True
if self.mStatusStr == "5_STARTED_MANUAL": self.mStatusStr = "1_STOPPED_MANUAL"; lLogBool=True
if self.mStatusStr == "4_STARTED": self.mStatusStr = "0_STOPPED"; lLogBool=True
if self.mStatusStr is None: self.mStatusStr = "0_STOPPED"; lLogBool=True
# Log info about process
if lLogBool == True: self.StatusChangeLog()
self.mAgentMuteBool = False
return self.mStatusStr
except Exception as e:
self.mAgentMuteBool=False
if inRaiseExceptionBool: raise e
else: return "TIMEOUT"
def StatusCheckStart(self):
"""
Check process status and run it if auto stopped self.mStatusStr is "0_STOPPED"

@ -6,685 +6,221 @@
# lResponseDict = {"Headers": {}, "SetCookies": {}, "Body": b"", "StatusCode": None}
# self.OpenRPAResponseDict = lResponseDict
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
#from http.client import HTTPException
import threading
import json
from threading import Thread
import inspect
from pyOpenRPA.Tools import CrossOS
from . import Processor # Add new processor
from . import ProcessorOld # Support old processor - deprecated defs only for backward compatibility
import urllib.parse # decode URL in string
import importlib
import pdb
import base64
import uuid
import datetime
import os #for path operations
from http import cookies
gSettingsDict = {}
from . import ServerSettings
from . import __Orchestrator__
import copy
import mimetypes
mimetypes.add_type("font/woff2",".woff2")
mimetypes.add_type("text/javascript",".js")
gCacheDict = {}
from http import cookies
from . import ServerBC
# объявление import
from fastapi import FastAPI, Form, Request, HTTPException, Depends, Header, Response, Body
from fastapi.responses import PlainTextResponse, HTMLResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
import uvicorn
import io
from starlette.responses import StreamingResponse
from typing import Union
from pyOpenRPA import __version__
# Tool to merge complex dictionaries
def __ComplexDictMerge2to1__(in1Dict, in2Dict):
lPathList=None
if lPathList is None: lPathList = []
for lKeyStr in in2Dict:
if lKeyStr in in1Dict:
if isinstance(in1Dict[lKeyStr], dict) and isinstance(in2Dict[lKeyStr], dict):
__ComplexDictMerge2to1__(in1Dict[lKeyStr], in2Dict[lKeyStr])
elif in1Dict[lKeyStr] == in2Dict[lKeyStr]:
pass # same leaf value
else:
raise Exception('Conflict at %s' % '.'.join(lPathList + [str(lKeyStr)]))
else:
in1Dict[lKeyStr] = in2Dict[lKeyStr]
return in1Dict
# Tool to merge complex dictionaries - no exceptions, just overwrite dict 2 in dict 1
def __ComplexDictMerge2to1Overwrite__(in1Dict, in2Dict):
"""
Merge in2Dict in in1Dict. In conflict override and get value from dict 2
import base64
import uuid
import datetime
:param in1Dict: Source dict. Save the link (structure)
:param in2Dict: New data dict
:return: Merged dict 1
"""
lPathList=None
if lPathList is None: lPathList = []
for lKeyStr in in2Dict:
if lKeyStr in in1Dict:
if isinstance(in1Dict[lKeyStr], dict) and isinstance(in2Dict[lKeyStr], dict):
__ComplexDictMerge2to1Overwrite__(in1Dict[lKeyStr], in2Dict[lKeyStr])
# ИНИЦИАЛИЗАЦИЯ FASTAPI!
app = FastAPI(
title = "pyOpenRPA (ORPA) Orchestrator",
description = "Сервер оркестратора pyOpenRPA Orchestrator",
version = __version__,
openapi_url="/orpa/fastapi/openapi.json",
docs_url = "/orpa/fastapi/docs",
redoc_url = "/orpa/fastapi/redoc",
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")):
if __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False)==True:
lResult={"Domain": "", "User": ""}
######################################
#Way 1 - try to find AuthToken
lCookies = cookies.SimpleCookie(inCookiesStr) # inRequest.headers.get("Cookie", "")
__Orchestrator__.GSettingsGet()
lHeaderAuthorization = inAuthorizationStr.split(" ")
if "AuthToken" in lCookies:
lCookieAuthToken = lCookies.get("AuthToken", "").value
if lCookieAuthToken:
#Find AuthToken in GlobalDict
if lCookieAuthToken in __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}):
#Auth Token Has Been Founded
lResult["Domain"] = __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["Domain"]
lResult["User"] = __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["User"]
#Set auth token
mOpenRPA={}
mOpenRPA["AuthToken"] = lCookieAuthToken
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 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]
lLogonBool = __Orchestrator__.OSCredentialsVerify(inUserStr=lUser, inPasswordStr=lPassword, inDomainStr=lDomain)
#Check result
if lLogonBool:
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:
in1Dict[lKeyStr] = in2Dict[lKeyStr]
raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (неверная пара логин / пароль)", headers={})
######################################
else:
in1Dict[lKeyStr] = in2Dict[lKeyStr]
return in1Dict
raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (неполная пара логин / пароль)", headers={'Content-type':'text/html', 'WWW-Authenticate':'Basic'})
else: return None # Credentials are not required - return none
lRouteList =[]
for lItem in app.router.routes:
lRouteList.append(lItem)
app.router.routes=[]
for lItem in lRouteList:
app.add_api_route(
path=lItem.path,
endpoint=lItem.endpoint,
methods=["GET"],
dependencies=[Depends(IdentifyAuthorize)],
tags=["FastAPI"]
)
from . import ServerSettings
def BackwardCompatibility(inRequest:Request, inResponse:Response, inBodyStr:str = Body(""), inAuthTokenStr = None):
lHTTPRequest = ServerBC.HTTPRequestOld(inRequest=inRequest, inResponse=inResponse, inAuthTokenStr=inAuthTokenStr)
lHTTPRequest.path = inRequest.url.path
lHTTPRequest.body = inBodyStr
lHTTPRequest.client_address = [inRequest.client.host]
threading.current_thread().request = lHTTPRequest
lResult = lHTTPRequest.do_GET(inBodyStr=inBodyStr)
if lResult is None:
lResult = lHTTPRequest.do_POST(inBodyStr=inBodyStr)
if lHTTPRequest.OpenRPAResponseDict["Headers"]["Content-type"] != None:
return StreamingResponse(io.BytesIO(lResult), media_type=lHTTPRequest.OpenRPAResponseDict["Headers"]["Content-type"])
#WRAPPERS!
def BackwardCompatibityWrapperAuth(inRequest:Request, inResponse:Response, inBodyStr:str = Body(""),
inAuthTokenStr:str=Depends(ServerSettings.IdentifyAuthorize)): # Old from v1.3.1 (updated to FastAPI)
return BackwardCompatibility(inRequest = inRequest, inResponse = inResponse, inBodyStr = inBodyStr, inAuthTokenStr=inAuthTokenStr)
def BackwardCompatibityWrapperNoAuth(inRequest:Request, inResponse:Response, inBodyStr:str = Body("")): # Old from v1.3.1 (updated to FastAPI)
return BackwardCompatibility(inRequest = inRequest, inResponse = inResponse, inBodyStr = inBodyStr, inAuthTokenStr=None)
def BackwardCompatibityBeginWrapperAuth(inBeginTokenStr, inRequest:Request, inResponse:Response, inBodyStr:str = Body(""),
inAuthTokenStr:str=Depends(ServerSettings.IdentifyAuthorize)): # Old from v1.3.1 (updated to FastAPI)
return BackwardCompatibility(inRequest = inRequest, inResponse = inResponse, inBodyStr = inBodyStr, inAuthTokenStr=inAuthTokenStr)
def BackwardCompatibityBeginWrapperNoAuth(inBeginTokenStr, inRequest:Request, inResponse:Response, inBodyStr:str = Body("")): # Old from v1.3.1 (updated to FastAPI)
return BackwardCompatibility(inRequest = inRequest, inResponse = inResponse, inBodyStr = inBodyStr, inAuthTokenStr=None)
#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", ""))
global gSettingsDict
#pdb.set_trace()
if "AuthToken" in lCookies:
lCookieAuthToken = lCookies.get("AuthToken", "").value
if lCookieAuthToken:
#Find AuthToken in GlobalDict
if lCookieAuthToken in gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}):
#Auth Token Has Been Founded
lResult["Domain"] = gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["Domain"]
lResult["User"] = gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["User"]
#Set auth token
inRequest.OpenRPA["AuthToken"] = lCookieAuthToken
inRequest.OpenRPA["Domain"] = lResult["Domain"]
inRequest.OpenRPA["User"] = lResult["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 = ""
if "\\" in lUser:
lDomain = lUser.split("\\")[0]
lUser = lUser.split("\\")[1]
lLogonBool = __Orchestrator__.OSCredentialsVerify(inUserStr=lUser, inPasswordStr=lPassword, inDomainStr=lDomain)
#Check result
if lLogonBool:
lResult["Domain"] = lDomain
lResult["User"] = lUser
#Create token
lAuthToken=str(uuid.uuid1())
gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken] = {}
gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["Domain"] = lResult["Domain"]
gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["User"] = lResult["User"]
gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["FlagDoNotExpire"] = False
gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["TokenDatetime"] = datetime.datetime.now()
#Set-cookie
inRequest.OpenRPA["AuthToken"] = lAuthToken
inRequest.OpenRPA["Domain"] = lResult["Domain"]
inRequest.OpenRPA["User"] = lResult["User"]
inRequest.OpenRPASetCookie = {}
#New engine of server
inRequest.OpenRPAResponseDict["SetCookies"]["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"))
#Check access before execute the action
#return bool True - go execute, False - dont execute
def UserAccessCheckBefore(inMethod, inRequest):
# Help def - Get access flag from dict
#pdb.set_trace()
global gSettingsDict
def HelpGetFlag(inAccessRuleItem, inRequest, inGlobalDict, inAuthenticateDict):
if "FlagAccess" in inAccessRuleItem:
return inAccessRuleItem["FlagAccess"]
elif "FlagAccessDefRequestGlobalAuthenticate" in inAccessRuleItem:
return inAccessRuleItem["FlagAccessDefRequestGlobalAuthenticate"](inRequest, inGlobalDict,
inAuthenticateDict)
##########################################
inMethod=inMethod.upper()
#Prepare result false
lResult = False
lAuthToken = inRequest.OpenRPA["AuthToken"]
#go next if user is identified
lUserDict = None
if lAuthToken:
lUserDict = gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]
#pdb.set_trace()
########################################
########################################
#Check general before rule (without User domain)
#Check rules
inRuleMatchURLList = gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("RuleMethodMatchURLBeforeList", [])
for lAccessRuleItem in inRuleMatchURLList:
#Go next execution if flag is false
if not lResult:
#Check if Method is identical
if lAccessRuleItem["Method"].upper() == inMethod:
#check Match type variant: BeginWith
if lAccessRuleItem["MatchType"].upper() == "BEGINWITH":
lURLPath = inRequest.path
lURLPath = lURLPath.upper()
if lURLPath.startswith(lAccessRuleItem["URL"].upper()):
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
# check Match type variant: Contains
elif lAccessRuleItem["MatchType"].upper() == "CONTAINS":
lURLPath = inRequest.path
lURLPath = lURLPath.upper()
if lURLPath.contains(lAccessRuleItem["URL"].upper()):
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
# check Match type variant: Equal
elif lAccessRuleItem["MatchType"].upper() == "EQUAL":
if lAccessRuleItem["URL"].upper() == inRequest.path.upper():
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
# check Match type variant: EqualCase
elif lAccessRuleItem["MatchType"].upper() == "EQUALCASE":
if lAccessRuleItem["URL"] == inRequest.path:
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
#########################################
#########################################
#Do check if lResult is false
if not lResult:
#Check access by User Domain
#Check rules to find first appicable
#Check rules
lMethodMatchURLList = gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("RuleDomainUserDict", {}).get((lUserDict["Domain"].upper(), lUserDict["User"].upper()), {}).get("MethodMatchURLBeforeList", [])
if len(lMethodMatchURLList) > 0:
for lAccessRuleItem in lMethodMatchURLList:
#Go next execution if flag is false
if not lResult:
#Check if Method is identical
if lAccessRuleItem["Method"].upper() == inMethod:
#check Match type variant: BeginWith
if lAccessRuleItem["MatchType"].upper() == "BEGINWITH":
lURLPath = inRequest.path
lURLPath = lURLPath.upper()
if lURLPath.startswith(lAccessRuleItem["URL"].upper()):
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
#check Match type variant: Contains
elif lAccessRuleItem["MatchType"].upper() == "CONTAINS":
lURLPath = inRequest.path
lURLPath = lURLPath.upper()
if lURLPath.contains(lAccessRuleItem["URL"].upper()):
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
# check Match type variant: Equal
elif lAccessRuleItem["MatchType"].upper() == "EQUAL":
if lAccessRuleItem["URL"].upper() == inRequest.path.upper():
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
# check Match type variant: EqualCase
elif lAccessRuleItem["MatchType"].upper() == "EQUALCASE":
if lAccessRuleItem["URL"] == inRequest.path:
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
else:
return True
#####################################
#####################################
#Return lResult
return lResult
# HTTPRequestHandler class
class testHTTPServer_RequestHandler(BaseHTTPRequestHandler):
# Def to check User Role access grants
def UACClientCheck(self, inRoleKeyList): # Alias
return self.UserRoleAccessAsk(inRoleKeyList=inRoleKeyList)
def UserRoleAccessAsk(self, inRoleKeyList):
lResult = True # Init flag
lRoleHierarchyDict = self.UserRoleHierarchyGet() # get the Hierarchy
# Try to get value from key list
lKeyValue = lRoleHierarchyDict # Init the base
for lItem in inRoleKeyList:
if type(lKeyValue) is dict:
if lItem in lKeyValue: # Has key
lKeyValue = lKeyValue[lItem] # Get the value and go to the next loop iteration
else: # Else branch - true or false
if len(lKeyValue)>0: # False - if Dict has some elements
lResult = False # Set the False Flag
else:
lResult = True # Set the True flag
break # Stop the loop
else: # Has element with no detalization - return True
lResult = True # Set the flag
break # Close the loop
return lResult # Return the result
# Def to get hierarchy of the current user roles
# if return {} - all is available
def UserRoleHierarchyGet(self):
#global gSettingsDict
lDomainUpperStr = self.OpenRPA["Domain"].upper()
lUserUpperStr = self.OpenRPA["User"].upper()
return gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("RuleDomainUserDict", {}).get((lDomainUpperStr, lUserUpperStr), {}).get("RoleHierarchyAllowedDict", {})
#Tech def
#return {"headers":[],"body":"","statuscode":111}
def URLItemCheckDo(self, inURLItem, inMethod, inOnlyFlagUACBool = False):
global gSettingsDict
###############################
#Tech sub def - do item
################################
def URLItemDo(inURLItem,inRequest,inGlobalDict):
global gCacheDict
inResponseDict = inRequest.OpenRPAResponseDict
inResponseDict["Headers"]["Content-type"]= None
#Set status code 200
inResponseDict["StatusCode"] = 200
#Content-type
if "ResponseContentType" in inURLItem:
inResponseDict["Headers"]["Content-type"] = inURLItem["ResponseContentType"]
#If file path is set
if "ResponseFilePath" in inURLItem:
# Check cache
if inURLItem.get("UseCacheBool",False) == True:
if inURLItem["ResponseFilePath"] in gCacheDict:
# Write content as utf-8 data
inResponseDict["Body"] = gCacheDict[inURLItem["ResponseFilePath"]]
from . import __Orchestrator__
import mimetypes
mimetypes.add_type("font/woff2",".woff2")
mimetypes.add_type("text/javascript",".js")
from typing import Union
def InitFastAPI():
global app
lL = __Orchestrator__.OrchestratorLoggerGet()
__Orchestrator__.GSettingsGet()["ServerDict"]["ServerThread"] = app
ServerSettings.SettingsUpdate()
BCURLUpdate()
def BCURLUpdate():
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('/',"_"))
else:
if lConnectItemDict.get("MatchType") in ["EqualCase", "Equal","EqualNoParam"]:
if lConnectItemDict.get("UACBool",True):
app.add_api_route(
path=lConnectItemDict["URL"],
endpoint=BackwardCompatibityWrapperAuth,
response_class=PlainTextResponse,
methods=[lConnectItemDict["Method"]],
tags=["BackwardCompatibility"]
)
else:
if os.path.exists(inURLItem["ResponseFilePath"]) and os.path.isfile(inURLItem["ResponseFilePath"]):
lFileObject = open(CrossOS.PathStr(inURLItem["ResponseFilePath"]), "rb")
# Write content as utf-8 data
gCacheDict[inURLItem["ResponseFilePath"]] = lFileObject.read()
inResponseDict["Body"] = gCacheDict[inURLItem["ResponseFilePath"]]
# Закрыть файловый объект
lFileObject.close()
else: inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
else:
if os.path.exists(inURLItem["ResponseFilePath"]) and os.path.isfile(inURLItem["ResponseFilePath"]):
lFileObject = open(CrossOS.PathStr(inURLItem["ResponseFilePath"]), "rb")
# Write content as utf-8 data
inResponseDict["Body"] = lFileObject.read()
# Закрыть файловый объект
lFileObject.close()
else: inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
# detect MIME type if none
if inResponseDict["Headers"]["Content-type"] is None:
inResponseDict["Headers"]["Content-type"]= mimetypes.guess_type(inURLItem["ResponseFilePath"])[0]
#If function is set
if "ResponseDefRequestGlobal" in inURLItem:
lDef = inURLItem["ResponseDefRequestGlobal"]
lDefSignature = inspect.signature(lDef)
if len(lDefSignature.parameters) == 2:
inURLItem["ResponseDefRequestGlobal"](inRequest, inGlobalDict)
elif len(lDefSignature.parameters) == 1:
inURLItem["ResponseDefRequestGlobal"](inRequest)
else:
inURLItem["ResponseDefRequestGlobal"]()
if "ResponseFolderPath" in inURLItem:
#lRequestPath = inRequest.path
lRequestPath = urllib.parse.unquote(inRequest.path)
if inURLItem["URL"][-1]!="/": inURLItem["URL"]+= "/" # Fix for settings
lFilePathSecondPart = lRequestPath.replace(inURLItem["URL"],"")
lFilePathSecondPart = lFilePathSecondPart.split("?")[0]
lFilePath = CrossOS.PathStr(os.path.join(inURLItem["ResponseFolderPath"],lFilePathSecondPart))
#print(f"File full path {lFilePath}")
#Check if file exist
if os.path.exists(lFilePath) and os.path.isfile(lFilePath):
# Check cache
if inURLItem.get("UseCacheBool",False) == True:
if lFilePath in gCacheDict:
# Write content as utf-8 data
inResponseDict["Body"] = gCacheDict[lFilePath]
else:
lFileObject = open(lFilePath, "rb")
# Write content as utf-8 data
gCacheDict[lFilePath] = lFileObject.read()
inResponseDict["Body"] = gCacheDict[lFilePath]
# Закрыть файловый объект
lFileObject.close()
app.add_api_route(
path=lConnectItemDict["URL"],
endpoint=BackwardCompatibityWrapperNoAuth,
response_class=PlainTextResponse,
methods=[lConnectItemDict["Method"]],
tags=["BackwardCompatibility"]
)
elif lConnectItemDict.get("MatchType") in ["BeginWith", "Contains"]:
lURLStr = lConnectItemDict["URL"]
if lURLStr[-1]!="/": lURLStr+="/"
lURLStr+="{inBeginTokenStr}"
if lConnectItemDict.get("UACBool",True):
app.add_api_route(
path=lURLStr,
endpoint=BackwardCompatibityBeginWrapperAuth,
response_class=PlainTextResponse,
methods=[lConnectItemDict["Method"]],
tags=["BackwardCompatibility"]
)
else:
lFileObject = open(lFilePath, "rb")
# Write content as utf-8 data
inResponseDict["Body"] = lFileObject.read()
# Закрыть файловый объект
lFileObject.close()
# detect MIME type if none
if inResponseDict["Headers"]["Content-type"] is None:
inResponseDict["Headers"]["Content-type"]= mimetypes.guess_type(lFilePath)[0]
else:
inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
# If No content-type
if inResponseDict["Headers"]["Content-type"] is None:
inResponseDict["Headers"]["Content-type"]= "application/octet-stream"
##############################################
# UAC Check
if inOnlyFlagUACBool == True and inURLItem.get("UACBool",None) in [None, True]:
return False
if inURLItem["Method"].upper() == inMethod.upper():
# check Match type variant: BeginWith
if inURLItem["MatchType"].upper() == "BEGINWITH":
lURLPath = urllib.parse.unquote(self.path)
lURLPath = lURLPath.upper()
if lURLPath.startswith(inURLItem["URL"].upper()):
URLItemDo(inURLItem, self, gSettingsDict)
return True
# check Match type variant: Contains
elif inURLItem["MatchType"].upper() == "CONTAINS":
lURLPath = urllib.parse.unquote(self.path)
lURLPath = lURLPath.upper()
if lURLPath.contains(inURLItem["URL"].upper()):
URLItemDo(inURLItem, self, gSettingsDict)
return True
# check Match type variant: Equal
elif inURLItem["MatchType"].upper() == "EQUAL":
if inURLItem["URL"].upper() == urllib.parse.unquote(self.path).upper():
URLItemDo(inURLItem, self, gSettingsDict)
return True
# check Match type variant: EqualNoParam
elif inURLItem["MatchType"].upper() == "EQUALNOPARAM":
if inURLItem["URL"].upper() == urllib.parse.unquote(self.path).upper().split("?")[0]:
URLItemDo(inURLItem, self, gSettingsDict)
return True
# check Match type variant: EqualCase
elif inURLItem["MatchType"].upper() == "EQUALCASE":
if inURLItem["URL"] == urllib.parse.unquote(self.path):
URLItemDo(inURLItem, self, gSettingsDict)
return True
return False
#ResponseContentTypeFile
def SendResponseContentTypeFile(self, inContentType, inFilePath):
# Send response status code
self.send_response(200)
# Send headers
self.send_header('Content-type', inContentType)
#Check if var exist
if hasattr(self, "OpenRPASetCookie"):
self.send_header("Set-Cookie", f"AuthToken={self.OpenRPA['AuthToken']}")
self.end_headers()
lFileObject = open(inFilePath, "rb")
# Write content as utf-8 data
self.wfile.write(lFileObject.read())
#Закрыть файловый объект
lFileObject.close()
# ResponseContentTypeFile
def ResponseDictSend(self):
lL = gSettingsDict["Logger"]
inResponseDict = self.OpenRPAResponseDict
# Send response status code
self.send_response(inResponseDict["StatusCode"])
# Send headers
for lItemKey, lItemValue in inResponseDict["Headers"].items():
self.send_header(lItemKey, lItemValue)
# Send headers: Set-Cookie
for lItemKey, lItemValue in inResponseDict["SetCookies"].items():
self.send_header("Set-Cookie", f"{lItemKey}={lItemValue}")
#Close headers section in response
try:
self.end_headers()
# Write content as utf-8 data
self.wfile.write(inResponseDict["Body"])
except (ConnectionResetError, ConnectionAbortedError) as e:
if lL: lL.warning(f"SERVER: Connection was forcibly closed by the client side - OK for the network interactions (ConnectionResetError: [WinError 10054] or ConnectionAbortedError: [WinError 10053])")
app.add_api_route(
path=lURLStr,
endpoint=BackwardCompatibityBeginWrapperNoAuth,
response_class=PlainTextResponse,
methods=[lConnectItemDict["Method"]],
tags=["BackwardCompatibility"]
)
lConnectItemDict["BCBool"]=True
def InitUvicorn(inHostStr=None, inPortInt=None, inSSLCertPathStr=None, inSSLKeyPathStr=None, inSSLPasswordStr=None):
if inHostStr is None: inHostStr="0.0.0.0"
if inPortInt is None: inPortInt=1024
if inSSLCertPathStr != None: inSSLCertPathStr=CrossOS.PathStr(inSSLCertPathStr)
if inSSLKeyPathStr != None: inSSLKeyPathStr=CrossOS.PathStr(inSSLKeyPathStr)
global app
lL = __Orchestrator__.OrchestratorLoggerGet()
#uvicorn.run('pyOpenRPA.Orchestrator.Server:app', host='0.0.0.0', port=1024)
uvicorn.run(app, host=inHostStr, port=inPortInt,ssl_keyfile=inSSLKeyPathStr,ssl_certfile=inSSLCertPathStr,ssl_keyfile_password=inSSLPasswordStr)
if lL and inSSLKeyPathStr != None: lL.info(f"Сервер инициализирован успешно (с поддержкой SSL):: Слушает URL: {inHostStr}, Слушает порт: {inPortInt}, Путь к файлу сертификата (.pem, base64): {inSSLCertPathStr}")
if lL and inSSLKeyPathStr == None: lL.info(f"Сервер инициализирован успешно (без поддержки SSL):: Слушает URL: {inHostStr}, Слушает порт: {inPortInt}")
def do_GET(self):
try:
global gSettingsDict
#self.timeout=gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"]
self.request.settimeout(gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"])
threading.current_thread().request = self
self.OpenRPA = {}
self.OpenRPA["AuthToken"] = None
self.OpenRPA["Domain"] = None
self.OpenRPA["User"] = None
self.OpenRPA["DefUserRoleAccessAsk"]=self.UserRoleAccessAsk # Alias for def
self.OpenRPA["DefUserRoleHierarchyGet"]=self.UserRoleHierarchyGet # Alias for def
# Prepare result dict
lResponseDict = {"Headers": {}, "SetCookies": {}, "Body": b"", "StatusCode": None}
self.OpenRPAResponseDict = lResponseDict
############################
#First - all with Flag UACBool
############################
for lURLItem in gSettingsDict["ServerDict"]["URLList"]:
#Check if all condition are applied
lFlagURLIsApplied=False
lFlagURLIsApplied=self.URLItemCheckDo(inURLItem=lURLItem, inMethod="GET", inOnlyFlagUACBool=True)
if lFlagURLIsApplied:
self.ResponseDictSend()
return
#####################################
#Do authentication
#Check if authentication is turned on
#####################################
lFlagAccessUserBlock=False
lAuthenticateDict = {"Domain": "", "User": ""}
if gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
lAuthenticateDict = AuthenticateVerify(self)
if not lAuthenticateDict["User"]:
lFlagAccessUserBlock=True
# Logging
# gSettingsDict["Logger"].info(f"HTTP request /. Domain: {lAuthenticateDict['Domain']}, User: {lAuthenticateDict['User']}")
if lFlagAccessUserBlock:
AuthenticateBlock(self)
#####################################
else:
#Check the user access (if flag)
####################################
lFlagUserAccess = True
#If need user authentication
if gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
lFlagUserAccess = UserAccessCheckBefore("GET", self)
######################################
if lFlagUserAccess:
if CrossOS.IS_WINDOWS_BOOL: lOrchestratorFolder = "\\".join(__file__.split("\\")[:-1])
if CrossOS.IS_LINUX_BOOL: lOrchestratorFolder = "/".join(__file__.split("/")[:-1])
############################
#New server engine (url from global dict (URLList))
############################
for lURLItem in gSettingsDict["ServerDict"]["URLList"]:
#Check if all condition are applied
lFlagURLIsApplied=False
lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "GET")
if lFlagURLIsApplied:
self.ResponseDictSend()
return
#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(gSettingsDict)
# 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 gSettingsDict["FileManager"]["FileURLFilePathDict"]:
self.SendResponseContentTypeFile('application/octet-stream', gSettingsDict["FileManager"]["FileURLFilePathDict"][lFileURL])
else:
#Set access denied code
# Send response status code
self.send_response(403)
# Send headers
self.end_headers()
except BrokenPipeError as e:
lL = gSettingsDict["Logger"]
if lL: lL.warning(f"Сервер (do_GET): Возникла ошибка сети - BrokenPipeError: [Errno 32] Broken pipe. Сервер продолжает работу")
except Exception as e:
lL = gSettingsDict["Logger"]
if lL: lL.exception(f"Сервер (do_GET): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")
# POST
def do_POST(self):
try:
global gSettingsDict
#self.timeout=gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"]
self.request.settimeout(gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"])
threading.current_thread().request = self
lL = gSettingsDict["Logger"]
self.OpenRPA = {}
self.OpenRPA["AuthToken"] = None
self.OpenRPA["Domain"] = None
self.OpenRPA["User"] = None
self.OpenRPA["DefUserRoleAccessAsk"]=self.UserRoleAccessAsk # Alias for def
self.OpenRPA["DefUserRoleHierarchyGet"]=self.UserRoleHierarchyGet # Alias for def
# Prepare result dict
#pdb.set_trace()
lResponseDict = {"Headers": {}, "SetCookies":{}, "Body": b"", "StatusCode": None}
self.OpenRPAResponseDict = lResponseDict
############################
#First - all with Flag UACBool
############################
for lURLItem in gSettingsDict["ServerDict"]["URLList"]:
#Check if all condition are applied
lFlagURLIsApplied=False
lFlagURLIsApplied=self.URLItemCheckDo(inURLItem=lURLItem, inMethod="POST", inOnlyFlagUACBool=True)
if lFlagURLIsApplied:
self.ResponseDictSend()
return
#####################################
#Do authentication
#Check if authentication is turned on
#####################################
lFlagAccessUserBlock=False
lAuthenticateDict = {"Domain": "", "User": ""}
lIsSuperToken = False # Is supertoken
if gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
lAuthenticateDict = AuthenticateVerify(self)
# Get Flag is supertoken (True|False)
lIsSuperToken = gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(self.OpenRPA["AuthToken"], {}).get("FlagDoNotExpire", False)
if not lAuthenticateDict["User"]:
lFlagAccessUserBlock=True
if lFlagAccessUserBlock:
AuthenticateBlock(self)
#####################################
else:
#Check the user access (if flag)
####################################
lFlagUserAccess = True
#If need user authentication
if gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
lFlagUserAccess = UserAccessCheckBefore("POST", self)
######################################
if lFlagUserAccess:
lOrchestratorFolder = "\\".join(__file__.split("\\")[:-1])
############################
#New server engine (url from global dict (URLList))
############################
for lURLItem in gSettingsDict["ServerDict"]["URLList"]:
#Check if all condition are applied
lFlagURLIsApplied=False
lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "POST")
if lFlagURLIsApplied:
self.ResponseDictSend()
return
#Централизованная функция получения запросов/отправки
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()
# Logging info about processor activity if not SuperToken ()
if not lIsSuperToken:
lActivityTypeListStr = ""
try:
if type(lInputObject) is list:
for lActivityItem in lInputObject:
lActivityTypeListStr+=f"{lActivityItem['Type']}; "
else:
lActivityTypeListStr += f"{lInputObject['Type']}"
except Exception as e:
lActivityTypeListStr = "Has some error with Activity Type read"
if lL: lL.info(f"Сервер:: !ВНИМАНИЕ! /Utils/Processor через некоторое время перестанет поддерживаться. Используйте /pyOpenRPA/Processor или /pyOpenRPA/ActivityListExecute. Активность поступила от пользователя. Домен: {self.OpenRPA['Domain']}, Логин: {self.OpenRPA['User']}, Тип активности: {lActivityTypeListStr}")
# Send message back to client
message = json.dumps(ProcessorOld.ActivityListOrDict(lInputObject))
# Write content as utf-8 data
self.wfile.write(bytes(message, "utf8"))
return
else:
#Set access denied code
# Send response status code
self.send_response(403)
# Send headers
self.end_headers()
return
except BrokenPipeError as e:
lL = gSettingsDict["Logger"]
if lL: lL.warning(f"Сервер (do_POST): Возникла ошибка сети - BrokenPipeError: [Errno 32] Broken pipe. Сервер продолжает работу")
except Exception as e:
lL = gSettingsDict["Logger"]
if lL: lL.exception(f"Сервер (do_POST): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")
#Logging
#!Turn it on to stop print in console
#def log_message(self, format, *args):
# return
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
daemon_threads = True
"""Handle requests in a separate thread."""
def finish_request(self, request, client_address):
try:
global gSettingsDict
request.settimeout(gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"])
# "super" can not be used because BaseServer is not created from object
HTTPServer.finish_request(self, request, client_address)
except ConnectionResetError as e:
lL = gSettingsDict["Logger"]
if lL: lL.warning(f"Сервер (finish_request): Возникла ошибка сети - ConnectionResetError: [Errno 104] Connection reset by peer. Сервер продолжает работу")
except Exception as e:
lL = gSettingsDict["Logger"]
if lL: lL.exception(f"Сервер (finish_request): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")
#inGlobalDict
# "JSONConfigurationDict":<JSON>
import ssl
class RobotDaemonServer(Thread):
def __init__(self,name,inGlobalDict):
Thread.__init__(self)
self.name = name
# Update the global dict
ServerSettings.SettingsUpdate(inGlobalDict)
def run(self):
global gSettingsDict
lL = gSettingsDict.get("Logger",None)
try:
lServerDict = gSettingsDict["ServerDict"]["ListenDict"][self.name]
lAddressStr=lServerDict["AddressStr"]
lPortInt=lServerDict["PortInt"]
lCertFilePathStr = lServerDict["CertFilePEMPathStr"]
lKeyFilePathStr = lServerDict["KeyFilePathStr"]
if lCertFilePathStr == "": lCertFilePathStr = None
if lKeyFilePathStr == "": lKeyFilePathStr = None
# Server settings
# Choose port 8080, for port 80, which is normally used for a http server, you need root access
server_address = (lAddressStr, lPortInt)
#httpd = HTTPServer(server_address, testHTTPServer_RequestHandler)
#httpd.serve_forever()
httpd = ThreadedHTTPServer(server_address, testHTTPServer_RequestHandler)
if lCertFilePathStr is not None:
if lKeyFilePathStr is not None:
httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, certfile=lCertFilePathStr, keyfile=lKeyFilePathStr)
else:
httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, certfile=lCertFilePathStr)
if lL: lL.info(f"Сервер инициализирован успешно (с поддержкой SSL):: Наименование: {self.name}, Слушает URL: {lAddressStr}, Слушает порт: {lPortInt}, Путь к файлу сертификата (.pem): {lCertFilePathStr}")
else:
if lL: lL.info(f"Сервер инициализирован успешно (без поддержки SSL):: Наименование: {self.name}, Слушает URL: {lAddressStr}, Слушает порт: {lPortInt}")
#print('Starting server, use <Ctrl-C> to stop')
httpd.serve_forever()
except Exception as e:
if lL: lL.exception(f"Сервер:: Ошибка при инициализации - обратитесь в тех. поддержку pyOpenRPA")

@ -0,0 +1,424 @@
import inspect
from pyOpenRPA.Tools import CrossOS
import urllib.parse # decode URL in string
import os #for path operations
from . import __Orchestrator__
import mimetypes
mimetypes.add_type("font/woff2",".woff2")
mimetypes.add_type("application/javascript",".js")
# объявление import
from fastapi import FastAPI, Form, Request, HTTPException, Depends, Header, Response, Body
gCacheDict = {}
# Tool to merge complex dictionaries
def __ComplexDictMerge2to1__(in1Dict, in2Dict):
lPathList=None
if lPathList is None: lPathList = []
for lKeyStr in in2Dict:
if lKeyStr in in1Dict:
if isinstance(in1Dict[lKeyStr], dict) and isinstance(in2Dict[lKeyStr], dict):
__ComplexDictMerge2to1__(in1Dict[lKeyStr], in2Dict[lKeyStr])
elif in1Dict[lKeyStr] == in2Dict[lKeyStr]:
pass # same leaf value
else:
raise Exception('Conflict at %s' % '.'.join(lPathList + [str(lKeyStr)]))
else:
in1Dict[lKeyStr] = in2Dict[lKeyStr]
return in1Dict
# Tool to merge complex dictionaries - no exceptions, just overwrite dict 2 in dict 1
def __ComplexDictMerge2to1Overwrite__(in1Dict, in2Dict):
"""
Merge in2Dict in in1Dict. In conflict override and get value from dict 2
:param in1Dict: Source dict. Save the link (structure)
:param in2Dict: New data dict
:return: Merged dict 1
"""
lPathList=None
if lPathList is None: lPathList = []
for lKeyStr in in2Dict:
if lKeyStr in in1Dict:
if isinstance(in1Dict[lKeyStr], dict) and isinstance(in2Dict[lKeyStr], dict):
__ComplexDictMerge2to1Overwrite__(in1Dict[lKeyStr], in2Dict[lKeyStr])
else:
in1Dict[lKeyStr] = in2Dict[lKeyStr]
else:
in1Dict[lKeyStr] = in2Dict[lKeyStr]
return in1Dict
def AuthenticateBlock(inRequest):
raise HTTPException(status_code=401, detail="here is the details", headers={'Content-type':'text/html', 'WWW-Authenticate':'Basic'})
#Check access before execute the action
#return bool True - go execute, False - dont execute
def UserAccessCheckBefore(inMethod, inRequest):
# Help def - Get access flag from dict
#pdb.set_trace()
def HelpGetFlag(inAccessRuleItem, inRequest, inGlobalDict, inAuthenticateDict):
if "FlagAccess" in inAccessRuleItem:
return inAccessRuleItem["FlagAccess"]
elif "FlagAccessDefRequestGlobalAuthenticate" in inAccessRuleItem:
return inAccessRuleItem["FlagAccessDefRequestGlobalAuthenticate"](inRequest, inGlobalDict,
inAuthenticateDict)
##########################################
inMethod=inMethod.upper()
#Prepare result false
lResult = False
lAuthToken = inRequest.OpenRPA["AuthToken"]
#go next if user is identified
lUserDict = None
#print(f"lAuthToken: {lAuthToken}")
if lAuthToken:
lUserDict = __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]
#print(f"lUserDict: {lUserDict}")
#pdb.set_trace()
########################################
########################################
#Check general before rule (without User domain)
#Check rules
inRuleMatchURLList = __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("RuleMethodMatchURLBeforeList", [])
for lAccessRuleItem in inRuleMatchURLList:
#Go next execution if flag is false
if not lResult:
#Check if Method is identical
if lAccessRuleItem["Method"].upper() == inMethod:
#check Match type variant: BeginWith
if lAccessRuleItem["MatchType"].upper() == "BEGINWITH":
lURLPath = inRequest.path
lURLPath = lURLPath.upper()
if lURLPath.startswith(lAccessRuleItem["URL"].upper()):
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
# check Match type variant: Contains
elif lAccessRuleItem["MatchType"].upper() == "CONTAINS":
lURLPath = inRequest.path
lURLPath = lURLPath.upper()
if lURLPath.contains(lAccessRuleItem["URL"].upper()):
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
# check Match type variant: Equal
elif lAccessRuleItem["MatchType"].upper() == "EQUAL":
if lAccessRuleItem["URL"].upper() == inRequest.path.upper():
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
# check Match type variant: EqualCase
elif lAccessRuleItem["MatchType"].upper() == "EQUALCASE":
if lAccessRuleItem["URL"] == inRequest.path:
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
#########################################
#########################################
#Do check if lResult is false
if not lResult:
#Check access by User Domain
#Check rules to find first appicable
#Check rules
lMethodMatchURLList = __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("RuleDomainUserDict", {}).get((lUserDict["Domain"].upper(), lUserDict["User"].upper()), {}).get("MethodMatchURLBeforeList", [])
if len(lMethodMatchURLList) > 0:
for lAccessRuleItem in lMethodMatchURLList:
#Go next execution if flag is false
if not lResult:
#Check if Method is identical
if lAccessRuleItem["Method"].upper() == inMethod:
#check Match type variant: BeginWith
if lAccessRuleItem["MatchType"].upper() == "BEGINWITH":
lURLPath = inRequest.path
lURLPath = lURLPath.upper()
if lURLPath.startswith(lAccessRuleItem["URL"].upper()):
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
#check Match type variant: Contains
elif lAccessRuleItem["MatchType"].upper() == "CONTAINS":
lURLPath = inRequest.path
lURLPath = lURLPath.upper()
if lURLPath.contains(lAccessRuleItem["URL"].upper()):
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
# check Match type variant: Equal
elif lAccessRuleItem["MatchType"].upper() == "EQUAL":
if lAccessRuleItem["URL"].upper() == inRequest.path.upper():
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
# check Match type variant: EqualCase
elif lAccessRuleItem["MatchType"].upper() == "EQUALCASE":
if lAccessRuleItem["URL"] == inRequest.path:
lResult = HelpGetFlag(lAccessRuleItem, inRequest, __Orchestrator__.GSettingsGet(), lUserDict)
else:
return True
#####################################
#####################################
#Return lResult
return lResult
class HTTPRequestOld():
mRequest:Request = None
mResponse:Response = None
OpenRPA: dict = {}
headers={}
def __init__(self,inRequest,inResponse,inAuthTokenStr):
self.mRequest = inRequest
self.mResponse = inResponse
if inAuthTokenStr != None:
self.OpenRPA = {}
self.OpenRPA["IsSuperToken"] = __Orchestrator__.WebUserIsSuperToken(inAuthTokenStr=inAuthTokenStr)
self.OpenRPA["AuthToken"] = inAuthTokenStr
self.OpenRPA["Domain"] = __Orchestrator__.WebUserDomainGet(inAuthTokenStr=inAuthTokenStr)
self.OpenRPA["User"] = __Orchestrator__.WebUserLoginGet(inAuthTokenStr=inAuthTokenStr)
else: self.OpenRPA = {"IsSuperToken":False, "AuthToken":None, "Domain":None, "User":None}
self.headers=inRequest.headers
# Def to check User Role access grants
def UACClientCheck(self, inRoleKeyList): # Alias
return self.UserRoleAccessAsk(inRoleKeyList=inRoleKeyList)
def UserRoleAccessAsk(self, inRoleKeyList):
lResult = True # Init flag
lRoleHierarchyDict = self.UserRoleHierarchyGet() # get the Hierarchy
# Try to get value from key list
lKeyValue = lRoleHierarchyDict # Init the base
for lItem in inRoleKeyList:
if type(lKeyValue) is dict:
if lItem in lKeyValue: # Has key
lKeyValue = lKeyValue[lItem] # Get the value and go to the next loop iteration
else: # Else branch - true or false
if len(lKeyValue)>0: # False - if Dict has some elements
lResult = False # Set the False Flag
else:
lResult = True # Set the True flag
break # Stop the loop
else: # Has element with no detalization - return True
lResult = True # Set the flag
break # Close the loop
return lResult # Return the result
# Def to get hierarchy of the current user roles
# if return {} - all is available
def UserRoleHierarchyGet(self):
try:
lDomainUpperStr = self.OpenRPA["Domain"].upper()
lUserUpperStr = self.OpenRPA["User"].upper()
return __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("RuleDomainUserDict", {}).get((lDomainUpperStr, lUserUpperStr), {}).get("RoleHierarchyAllowedDict", {})
except Exception as e:
return {}
#Tech def
#return {"headers":[],"body":"","statuscode":111}
def URLItemCheckDo(self, inURLItem, inMethod, inOnlyFlagUACBool = False):
###############################
#Tech sub def - do item
################################
def URLItemDo(inURLItem,inRequest,inGlobalDict):
global gCacheDict
inResponseDict = inRequest.OpenRPAResponseDict
inResponseDict["Headers"]["Content-type"]= None
#Set status code 200
inResponseDict["StatusCode"] = 200
#Content-type
if "ResponseContentType" in inURLItem:
inResponseDict["Headers"]["Content-type"] = inURLItem["ResponseContentType"]
#If file path is set
if "ResponseFilePath" in inURLItem:
# Check cache
if inURLItem.get("UseCacheBool",False) == True:
if inURLItem["ResponseFilePath"] in gCacheDict:
# Write content as utf-8 data
inResponseDict["Body"] = gCacheDict[inURLItem["ResponseFilePath"]]
else:
if os.path.exists(inURLItem["ResponseFilePath"]) and os.path.isfile(inURLItem["ResponseFilePath"]):
lFileObject = open(CrossOS.PathStr(inURLItem["ResponseFilePath"]), "rb")
# Write content as utf-8 data
gCacheDict[inURLItem["ResponseFilePath"]] = lFileObject.read()
inResponseDict["Body"] = gCacheDict[inURLItem["ResponseFilePath"]]
# Закрыть файловый объект
lFileObject.close()
else: inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
else:
if os.path.exists(inURLItem["ResponseFilePath"]) and os.path.isfile(inURLItem["ResponseFilePath"]):
lFileObject = open(CrossOS.PathStr(inURLItem["ResponseFilePath"]), "rb")
# Write content as utf-8 data
inResponseDict["Body"] = lFileObject.read()
# Закрыть файловый объект
lFileObject.close()
else: inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
# detect MIME type if none
if inResponseDict["Headers"]["Content-type"] is None:
inResponseDict["Headers"]["Content-type"]= mimetypes.guess_type(inURLItem["ResponseFilePath"])[0]
#If function is set
if "ResponseDefRequestGlobal" in inURLItem:
lDef = inURLItem["ResponseDefRequestGlobal"]
lDefSignature = inspect.signature(lDef)
if len(lDefSignature.parameters) == 2:
inURLItem["ResponseDefRequestGlobal"](inRequest, inGlobalDict)
elif len(lDefSignature.parameters) == 1:
inURLItem["ResponseDefRequestGlobal"](inRequest)
else:
inURLItem["ResponseDefRequestGlobal"]()
if "ResponseFolderPath" in inURLItem:
#lRequestPath = inRequest.path
lRequestPath = urllib.parse.unquote(inRequest.path)
if inURLItem["URL"][-1]!="/": inURLItem["URL"]+= "/" # Fix for settings
lFilePathSecondPart = lRequestPath.replace(inURLItem["URL"],"")
lFilePathSecondPart = lFilePathSecondPart.split("?")[0]
lFilePath = CrossOS.PathStr(os.path.join(inURLItem["ResponseFolderPath"],lFilePathSecondPart))
#print(f"File full path {lFilePath}")
#Check if file exist
if os.path.exists(lFilePath) and os.path.isfile(lFilePath):
# Check cache
if inURLItem.get("UseCacheBool",False) == True:
if lFilePath in gCacheDict:
# Write content as utf-8 data
inResponseDict["Body"] = gCacheDict[lFilePath]
else:
lFileObject = open(lFilePath, "rb")
# Write content as utf-8 data
gCacheDict[lFilePath] = lFileObject.read()
inResponseDict["Body"] = gCacheDict[lFilePath]
# Закрыть файловый объект
lFileObject.close()
else:
lFileObject = open(lFilePath, "rb")
# Write content as utf-8 data
inResponseDict["Body"] = lFileObject.read()
# Закрыть файловый объект
lFileObject.close()
# detect MIME type if none
if inResponseDict["Headers"]["Content-type"] is None:
inResponseDict["Headers"]["Content-type"]= mimetypes.guess_type(lFilePath)[0]
else:
inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
# If No content-type
if inResponseDict["Headers"]["Content-type"] is None:
inResponseDict["Headers"]["Content-type"]= "application/octet-stream"
##############################################
# UAC Check
if inOnlyFlagUACBool == True and inURLItem.get("UACBool",None) in [None, True]:
return False
if inURLItem["Method"].upper() == inMethod.upper():
# check Match type variant: BeginWith
if inURLItem["MatchType"].upper() == "BEGINWITH":
lURLPath = urllib.parse.unquote(self.path)
lURLPath = lURLPath.upper()
if lURLPath.startswith(inURLItem["URL"].upper()):
URLItemDo(inURLItem, self, __Orchestrator__.GSettingsGet())
return True
# check Match type variant: Contains
elif inURLItem["MatchType"].upper() == "CONTAINS":
lURLPath = urllib.parse.unquote(self.path)
lURLPath = lURLPath.upper()
if lURLPath.contains(inURLItem["URL"].upper()):
URLItemDo(inURLItem, self, __Orchestrator__.GSettingsGet())
return True
# check Match type variant: Equal
elif inURLItem["MatchType"].upper() == "EQUAL":
if inURLItem["URL"].upper() == urllib.parse.unquote(self.path).upper():
URLItemDo(inURLItem, self, __Orchestrator__.GSettingsGet())
return True
# check Match type variant: EqualNoParam
elif inURLItem["MatchType"].upper() == "EQUALNOPARAM":
if inURLItem["URL"].upper() == urllib.parse.unquote(self.path).upper().split("?")[0]:
URLItemDo(inURLItem, self, __Orchestrator__.GSettingsGet())
return True
# check Match type variant: EqualCase
elif inURLItem["MatchType"].upper() == "EQUALCASE":
if inURLItem["URL"] == urllib.parse.unquote(self.path):
URLItemDo(inURLItem, self, __Orchestrator__.GSettingsGet())
return True
return False
#ResponseContentTypeFile
def SendResponseContentTypeFile(self, inContentType, inFilePath):
inResponseDict = self.OpenRPAResponseDict
self.mResponse.status_code = 200
# Send headers
self.mResponse.headers["Content-type"]=inContentType
#Check if var exist
if hasattr(self, "OpenRPASetCookie"):
self.mResponse.set_cookie(key='AuthToken',value=self.OpenRPA['AuthToken'])
lFileObject = open(inFilePath, "rb")
# Write content as utf-8 data
lFileBytes = lFileObject.read()
#Закрыть файловый объект
lFileObject.close()
return lFileBytes
# ResponseContentTypeFile
def ResponseDictSend(self):
inResponseDict = self.OpenRPAResponseDict
self.mResponse.status_code = inResponseDict["StatusCode"]
# Send headers
for lItemKey, lItemValue in inResponseDict["Headers"].items():
self.mResponse.headers[lItemKey]=lItemValue
# Send headers: Set-Cookie
for lItemKey, lItemValue in inResponseDict["SetCookies"].items():
self.mResponse.set_cookie(key=lItemKey,value=lItemValue)
self.send_header("Set-Cookie", f"{lItemKey}={lItemValue}")
return inResponseDict["Body"]
def do_GET(self, inBodyStr):
try:
try:
self.OpenRPA["DefUserRoleAccessAsk"]=self.UserRoleAccessAsk # Alias for def
self.OpenRPA["DefUserRoleHierarchyGet"]=self.UserRoleHierarchyGet # Alias for def
except Exception as e:
pass
# Prepare result dict
lResponseDict = {"Headers": {}, "SetCookies": {}, "Body": b"", "StatusCode": None, "BodyIsText":True}
self.OpenRPAResponseDict = lResponseDict
#Check the user access (if flag, UAC)
####################################
lFlagUserAccess = True
#If need user authentication
if __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
if self.OpenRPA["AuthToken"] != None:
lFlagUserAccess = UserAccessCheckBefore("GET", self)
######################################
if lFlagUserAccess:
if CrossOS.IS_WINDOWS_BOOL: lOrchestratorFolder = "\\".join(__file__.split("\\")[:-1])
if CrossOS.IS_LINUX_BOOL: lOrchestratorFolder = "/".join(__file__.split("/")[:-1])
############################
#New server engine (url from global dict (URLList))
############################
for lURLItem in __Orchestrator__.GSettingsGet()["ServerDict"]["URLList"]:
#Check if all condition are applied
lFlagURLIsApplied=False
lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "GET")
if lFlagURLIsApplied:
return self.ResponseDictSend()
else:
raise HTTPException(status_code=403, detail="here is the details", headers={})
except Exception as e:
lL = __Orchestrator__.OrchestratorLoggerGet()
if lL: lL.exception(f"Сервер (do_GET): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")
# POST
def do_POST(self, inBodyStr):
try:
lL = __Orchestrator__.OrchestratorLoggerGet()
try:
self.OpenRPA["DefUserRoleAccessAsk"]=self.UserRoleAccessAsk # Alias for def
self.OpenRPA["DefUserRoleHierarchyGet"]=self.UserRoleHierarchyGet # Alias for def
except Exception as e:
pass
# Prepare result dict
#pdb.set_trace()
lResponseDict = {"Headers": {}, "SetCookies": {}, "Body": b"", "StatusCode": None, "BodyIsText":True}
self.OpenRPAResponseDict = lResponseDict
#Check the user access (if flag)
####################################
lFlagUserAccess = True
#If need user authentication
if __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
if self.OpenRPA["AuthToken"] != None:
lFlagUserAccess = UserAccessCheckBefore("POST", self)
######################################
if lFlagUserAccess:
lOrchestratorFolder = "\\".join(__file__.split("\\")[:-1])
############################
#New server engine (url from global dict (URLList))
############################
for lURLItem in __Orchestrator__.GSettingsGet()["ServerDict"]["URLList"]:
#Check if all condition are applied
lFlagURLIsApplied=False
lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "POST")
if lFlagURLIsApplied:
return self.ResponseDictSend()
else:
raise HTTPException(status_code=403, detail="here is the details", headers={})
except Exception as e:
lL = __Orchestrator__.OrchestratorLoggerGet()
if lL: lL.exception(f"Сервер, обратная совместимость (do_POST): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")

@ -1,6 +1,7 @@
import json, os
import copy
from . import __Orchestrator__
from .Server import app,IdentifyAuthorize # FAST API SERVER
#ControlPanelDict
from pyOpenRPA.Tools import CrossOS
if CrossOS.IS_WINDOWS_BOOL: #CrossOS
@ -20,6 +21,16 @@ from ..Tools import Usage
from . import BackwardCompatibility # Support old up to 1.2.0 defs
from . import Processor
from . import SettingsTemplate
from fastapi import FastAPI, Form, Request, HTTPException, Depends, Header, Response, Body
from fastapi.responses import PlainTextResponse, HTMLResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
import io
from starlette.responses import StreamingResponse
from typing import Union
from fastapi.responses import JSONResponse
# # # # # # # # # # # #
# v 1.2.0 Functionallity
@ -118,14 +129,8 @@ def HiddenAgentDictGenerate(inRequest, inGSettings):
# Client: mGlobal.pyOpenRPA.ServerDataHashStr
# Client: mGlobal.pyOpenRPA.ServerDataDict
def pyOpenRPA_ServerData(inRequest,inGSettings):
# Extract the hash value from request
lValueStr = None
if inRequest.headers.get('Content-Length') is not None:
lInputByteArrayLength = int(inRequest.headers.get('Content-Length'))
lInputByteArray = inRequest.rfile.read(lInputByteArrayLength)
# Превращение массива байт в объект
lValueStr = (lInputByteArray.decode('utf8'))
lValueStr = inRequest.body
# Generate ServerDataDict
lFlagDoGenerateBool = True
while lFlagDoGenerateBool:
@ -168,12 +173,7 @@ def pyOpenRPA_ServerJSInit(inRequest,inGSettings):
# Client: mGlobal.pyOpenRPA.ServerLogList
def pyOpenRPA_ServerLog(inRequest,inGSDict):
# Extract the hash value from request
lValueStr = None
if inRequest.headers.get('Content-Length') is not None:
lInputByteArrayLength = int(inRequest.headers.get('Content-Length'))
lInputByteArray = inRequest.rfile.read(lInputByteArrayLength)
# Превращение массива байт в объект
lValueStr = (lInputByteArray.decode('utf8'))
lValueStr = inRequest.body
# Generate ServerDataDict
lFlagDoGenerateBool = True
while lFlagDoGenerateBool:
@ -193,59 +193,60 @@ def pyOpenRPA_ServerLog(inRequest,inGSDict):
inResponseDict["Body"] = bytes(message, "utf8")
return lResult
def pyOpenRPA_Screenshot(inRequest,inGlobalDict):
# Get thread list /orpa/threads
@app.get(path="/orpa/client/screenshot-get",response_class=PlainTextResponse,tags=["Client"])
def pyOpenRPA_Screenshot():
# Get Screenshot
def SaveScreenshot(inFilePath):
# grab fullscreen
# Save the entire virtual screen as a PNsG
lScreenshot = getScreenAsImage()
lScreenshot.save('Screenshot.png', format='png')
# lScreenshot = ScreenshotSecondScreen.grab_screen()
# save image file
# lScreenshot.save('screenshot.png')
lScreenshot.save('screenshot.png', format='png')
# Сохранить файл на диск
if CrossOS.IS_WINDOWS_BOOL:
SaveScreenshot("Screenshot.png")
lFileObject = open("Screenshot.png", "rb")
SaveScreenshot("screenshot.png")
lFileObject = open("screenshot.png", "rb")
# Write content as utf-8 data
inRequest.OpenRPAResponseDict["Body"] = lFileObject.read()
lImage = lFileObject.read()
# Закрыть файловый объект
lFileObject.close()
else:
pyscreeze._screenshot_linux(imageFilename='Screenshot.png')
lFileObject = open("Screenshot.png", "rb")
pyscreeze._screenshot_linux(imageFilename='screenshot.png')
lFileObject = open("screenshot.png", "rb")
# Write content as utf-8 data
inRequest.OpenRPAResponseDict["Body"] = lFileObject.read()
lImage = lFileObject.read()
# Закрыть файловый объект
lFileObject.close()
return StreamingResponse(io.BytesIO(lImage), media_type="image/png")
# Add activity item or activity list to the processor queue
# Body is Activity item or Activity List
def pyOpenRPA_Processor(inRequest, inGSettings):
lL = inGSettings["Logger"]
# body inauthtoken JSON
@app.post(path="/orpa/api/processor-queue-add",response_class=JSONResponse,tags=["API"])
def pyOpenRPA_Processor(inRequest:Request, inAuthTokenStr:str = Depends(IdentifyAuthorize), inBodyStr:str = Body("")):
inGSettings = __Orchestrator__.GSettingsGet()
lL = __Orchestrator__.OrchestratorLoggerGet()
# Recieve the data
lValueStr = None
if inRequest.headers.get('Content-Length') is not None:
lInputByteArrayLength = int(inRequest.headers.get('Content-Length'))
lInputByteArray = inRequest.rfile.read(lInputByteArrayLength)
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
lValueStr = inBodyStr
# Превращение массива байт в объект
lInput = json.loads(lValueStr)
lResult=[]
# If list - operator plus
if type(lInput) is list:
# Logging info about processor activity if not SuperToken ()
if not __Orchestrator__.WebUserIsSuperToken(inRequest=inRequest, inGSettings=inGSettings):
if not __Orchestrator__.WebUserIsSuperToken(inAuthTokenStr=inAuthTokenStr):
lActivityTypeListStr = ""
try:
for lActivityItem in lInput:
lActivityTypeListStr += f"{lActivityItem['Def']}; "
except Exception as e:
lActivityTypeListStr = "Ошибка чтения типа активности"
lWebAuditMessageStr = __Orchestrator__.WebAuditMessageCreate(inRequest=inRequest,inOperationCodeStr=lActivityTypeListStr, inMessageStr="pyOpenRPA_Processor")
lHostStr = __Orchestrator__.WebRequestHostGet(inRequest=inRequest)
lWebAuditMessageStr = __Orchestrator__.WebAuditMessageCreate(inAuthTokenStr=inAuthTokenStr, inHostStr = lHostStr,inOperationCodeStr=lActivityTypeListStr, inMessageStr="pyOpenRPA_Processor")
if lL: lL.info(lWebAuditMessageStr)
# Separate into 2 lists - sync and async
lSyncActvityList = []
lAsyncActivityList = []
for lActivityItem in lInput:
lResult.append(__Orchestrator__.ProcessorActivityItemAppend(inActivityItemDict=lActivityItem))
if lInput.get("ThreadBool", False) == False:
lSyncActvityList.append(lActivityItem)
else:
@ -259,14 +260,16 @@ def pyOpenRPA_Processor(inRequest, inGSettings):
lThread = threading.Thread(target=Processor.ActivityListExecute, kwargs=lActivityItemArgsDict)
lThread.start()
else:
lResult=__Orchestrator__.ProcessorActivityItemAppend(inActivityItemDict=lInput)
# Logging info about processor activity if not SuperToken ()
if not __Orchestrator__.WebUserIsSuperToken(inRequest=inRequest, inGSettings=inGSettings):
if not __Orchestrator__.WebUserIsSuperToken(inAuthTokenStr=inAuthTokenStr):
lActivityTypeListStr = ""
try:
lActivityTypeListStr = lInput['Def']
except Exception as e:
lActivityTypeListStr = "Ошибка чтения типа активности"
lWebAuditMessageStr = __Orchestrator__.WebAuditMessageCreate(inRequest=inRequest,inOperationCodeStr=lActivityTypeListStr, inMessageStr="pyOpenRPA_Processor")
lHostStr = __Orchestrator__.WebRequestHostGet(inRequest=inRequest)
lWebAuditMessageStr = __Orchestrator__.WebAuditMessageCreate(inAuthTokenStr=inAuthTokenStr, inHostStr = lHostStr, inOperationCodeStr=lActivityTypeListStr, inMessageStr="pyOpenRPA_Processor")
if lL: lL.info(lWebAuditMessageStr)
if lInput.get("ThreadBool",False) == False:
# Append in list
@ -275,61 +278,65 @@ def pyOpenRPA_Processor(inRequest, inGSettings):
lActivityItemArgsDict = {"inGSettings": inGSettings, "inActivityList": [lInput]}
lThread = threading.Thread(target=Processor.ActivityListExecute, kwargs=lActivityItemArgsDict)
lThread.start()
return lResult
# Execute activity list
def pyOpenRPA_ActivityListExecute(inRequest, inGSettings):
@app.post(path="/orpa/api/activity-list-execute",response_class=JSONResponse,tags=["API"])
def pyOpenRPA_ActivityListExecute(inRequest:Request, inAuthTokenStr:str = Depends(IdentifyAuthorize), inBodyStr:str = Body("")):
# Recieve the data
lL = inGSettings["Logger"]
lValueStr = None
if inRequest.headers.get('Content-Length') is not None:
lInputByteArrayLength = int(inRequest.headers.get('Content-Length'))
lInputByteArray = inRequest.rfile.read(lInputByteArrayLength)
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
inGSettings = __Orchestrator__.GSettingsGet()
lL = __Orchestrator__.OrchestratorLoggerGet()
lValueStr = inBodyStr
# Превращение массива байт в объект
lInput = json.loads(lValueStr)
# If list - operator plus
if type(lInput) is list:
# Logging info about processor activity if not SuperToken ()
if not __Orchestrator__.WebUserIsSuperToken(inRequest=inRequest, inGSettings=inGSettings):
if not __Orchestrator__.WebUserIsSuperToken(inAuthTokenStr=inAuthTokenStr):
lActivityTypeListStr = ""
try:
for lActivityItem in lInput:
lActivityTypeListStr += f"{lActivityItem['Def']}; "
except Exception as e:
lActivityTypeListStr = "Ошибка чтения типа активности"
lWebAuditMessageStr = __Orchestrator__.WebAuditMessageCreate(inRequest=inRequest,inOperationCodeStr=lActivityTypeListStr, inMessageStr="pyOpenRPA_ActivityListExecute")
lHostStr = __Orchestrator__.WebRequestHostGet(inRequest=inRequest)
lWebAuditMessageStr = __Orchestrator__.WebAuditMessageCreate(inAuthTokenStr=inAuthTokenStr, inHostStr = lHostStr,inOperationCodeStr=lActivityTypeListStr, inMessageStr="pyOpenRPA_ActivityListExecute")
if lL: lL.info(lWebAuditMessageStr)
# Execution
lResultList = Processor.ActivityListExecute(inGSettings = inGSettings, inActivityList = lInput)
inRequest.OpenRPAResponseDict["Body"] = bytes(json.dumps(lResultList), "utf8")
return lResultList
#inRequest.OpenRPAResponseDict["Body"] = bytes(json.dumps(lResultList), "utf8")
else:
# Logging info about processor activity if not SuperToken ()
if not __Orchestrator__.WebUserIsSuperToken(inRequest=inRequest, inGSettings=inGSettings):
if not __Orchestrator__.WebUserIsSuperToken(inAuthTokenStr=inAuthTokenStr):
lActivityTypeListStr = ""
try:
lActivityTypeListStr = lInput['Def']
except Exception as e:
lActivityTypeListStr = "Ошибка чтения типа активности"
lWebAuditMessageStr = __Orchestrator__.WebAuditMessageCreate(inRequest=inRequest,
lHostStr = __Orchestrator__.WebRequestHostGet(inRequest=inRequest)
lWebAuditMessageStr = __Orchestrator__.WebAuditMessageCreate(inAuthTokenStr=inAuthTokenStr, inHostStr = lHostStr,
inOperationCodeStr=lActivityTypeListStr,
inMessageStr="pyOpenRPA_ActivityListExecute")
if lL: lL.info(lWebAuditMessageStr)
# Execution
lResultList = Processor.ActivityListExecute(inGSettings = inGSettings, inActivityList = [lInput])
inRequest.OpenRPAResponseDict["Body"] = bytes(json.dumps(lResultList[0]), "utf8")
return lResultList
#inRequest.OpenRPAResponseDict["Body"] = bytes(json.dumps(lResultList[0]), "utf8")
# See docs in Agent (pyOpenRPA.Agent.O2A)
def pyOpenRPA_Agent_O2A(inRequest, inGSettings):
lL = inGSettings["Logger"] # Alias
@app.post(path="/orpa/agent/o2a",response_class=JSONResponse,tags=["Agent"])
def pyOpenRPA_Agent_O2A(inRequest:Request, inAuthTokenStr:str = Depends(IdentifyAuthorize), inBodyDict = Body({})):
inGSettings = __Orchestrator__.GSettingsGet()
lL = __Orchestrator__.OrchestratorLoggerGet()
lConnectionLifetimeSecFloat = inGSettings["ServerDict"]["AgentConnectionLifetimeSecFloat"] # 300.0 # 5 min * 60 sec 300.0
lActivityItemLifetimeLimitSecFloat = inGSettings["ServerDict"]["AgentActivityLifetimeSecFloat"]
lAgentLoopSleepSecFloat = inGSettings["ServerDict"]["AgentLoopSleepSecFloat"]
lTimeStartFloat = time.time()
# Recieve the data
lValueStr = None
if inRequest.headers.get('Content-Length') is not None:
lInputByteArrayLength = int(inRequest.headers.get('Content-Length'))
lInputByteArray = inRequest.rfile.read(lInputByteArrayLength)
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
#lValueStr = inBodyDict
# Превращение массива байт в объект
lInput = inBodyDict#json.loads(lValueStr)
# Check if item is created
lAgentDictItemKeyTurple = (lInput["HostNameUpperStr"],lInput["UserUpperStr"])
if lAgentDictItemKeyTurple not in inGSettings["AgentDict"]:
@ -380,12 +387,12 @@ def pyOpenRPA_Agent_O2A(inRequest, inGSettings):
for lItemDict in lReturnActivityItemList:
if "CreatedByDatetime" in lItemDict:
del lItemDict["CreatedByDatetime"]
inRequest.OpenRPAResponseDict["Body"] = bytes(json.dumps(lReturnActivityItemList), "utf8")
# Log full version if bytes size is less than limit . else short
lBodyLenInt = len(inRequest.OpenRPAResponseDict["Body"])
lBodyLenInt = len(lReturnActivityItemList)
lAgentLimitLogSizeBytesInt = inGSettings["ServerDict"]["AgentLimitLogSizeBytesInt"]
if lL: lL.debug(f"ActivityItem to Agent ({lInput['HostNameUpperStr']}, {lInput['UserUpperStr']}): Item count: {len(lReturnActivityItemList)}, bytes size: {lBodyLenInt}")
lDoLoopBool = False # CLose the connection
lThisAgentDict["ConnectionCountInt"] -= 1 # Connection go to be closed - decrement the connection count
return lReturnActivityItemList
else: # Nothing to send - sleep for the next iteration
time.sleep(lAgentLoopSleepSecFloat)
else: # no queue item - sleep for the next iteration
@ -394,42 +401,43 @@ def pyOpenRPA_Agent_O2A(inRequest, inGSettings):
if lL: lL.exception("pyOpenRPA_Agent_O2A Exception!")
lThisAgentDict["ConnectionCountInt"] -= 1 # Connection go to be closed - decrement the connection count
def pyOpenRPA_Debugging_HelperDefList(inRequest, inGSettings):
@app.get(path="/orpa/api/helper-def-list/{inTokenStr}",response_class=JSONResponse,tags=["API"])
def pyOpenRPA_Debugging_HelperDefList(inRequest:Request, inAuthTokenStr:str = Depends(IdentifyAuthorize), inBodyStr:str = Body("")):
# Parse query
lResultDict = {
"success": True,
"results": []
}
# Get the path
lPathSplitList = __Orchestrator__.WebRequestParsePath(inRequest=inRequest).split('/')
lPathSplitList = inRequest.url.path.split('/')
lQueryStr = None
if "HelperDefList" != lPathSplitList[-1] and "" != lPathSplitList[-1]: lQueryStr = lPathSplitList[-1]
if lQueryStr != "" and lQueryStr is not None:
lDefList = __Orchestrator__.ActivityItemHelperDefList(inDefQueryStr=lQueryStr)
for lDefStr in lDefList:
lResultDict["results"].append({"name": lDefStr, "value": lDefStr, "text": lDefStr})
__Orchestrator__.WebRequestResponseSend(inRequest=inRequest, inResponeStr=json.dumps(lResultDict))
return lResultDict
def pyOpenRPA_Debugging_HelperDefAutofill(inRequest, inGSettings):
@app.get(path="/orpa/api/helper-def-autofill/{inTokenStr}",response_class=JSONResponse,tags=["API"])
def pyOpenRPA_Debugging_HelperDefAutofill(inRequest:Request, inAuthTokenStr:str = Depends(IdentifyAuthorize), inBodyStr:str = Body("")):
# Parse query
# Get the path
lPathSplitList = __Orchestrator__.WebRequestParsePath(inRequest=inRequest).split('/')
lPathSplitList = inRequest.url.path.split('/')
lQueryStr = None
if "HelperDefAutofill" != lPathSplitList[-1] and "" != lPathSplitList[-1]: lQueryStr = lPathSplitList[-1]
lResultDict = __Orchestrator__.ActivityItemHelperDefAutofill(inDef = lQueryStr)
__Orchestrator__.WebRequestResponseSend(inRequest=inRequest, inResponeStr=json.dumps(lResultDict))
return lResultDict
# See docs in Agent (pyOpenRPA.Agent.A2O)
def pyOpenRPA_Agent_A2O(inRequest, inGSettings):
lL = inGSettings["Logger"]
@app.post(path="/orpa/agent/a2o",response_class=JSONResponse,tags=["Agent"])
def pyOpenRPA_Agent_A2O(inRequest:Request, inAuthTokenStr:str = Depends(IdentifyAuthorize), inBodyDict = Body({})):
inGSettings = __Orchestrator__.GSettingsGet()
lL = __Orchestrator__.OrchestratorLoggerGet()
# Recieve the data
lValueStr = None
if inRequest.headers.get('Content-Length') is not None:
lInputByteArrayLength = int(inRequest.headers.get('Content-Length'))
lInputByteArray = inRequest.rfile.read(lInputByteArrayLength)
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
lAgentDictItemKeyTurple = (lInput["HostNameUpperStr"], lInput["UserUpperStr"])
#lValueStr = inBodyStr
# Превращение массива байт в объект
lInput = inBodyDict#json.loads(lValueStr)
lAgentDictItemKeyTurple = (lInput["HostNameUpperStr"], lInput["UserUpperStr"])
if "LogList" in lInput:
for lLogItemStr in lInput["LogList"]:
inGSettings["Logger"].info(lLogItemStr)
@ -460,14 +468,14 @@ def pyOpenRPA_Agent_A2O(inRequest, inGSettings):
from pyOpenRPA.Utils.Render import Render
lFileStr = CrossOS.PathJoinList(CrossOS.PathSplitList(__file__)[:-2] + ["Resources","Web","orpa","orc.xhtml"])
gRender = Render(inTemplatePathStr=lFileStr,inTemplateRefreshBool=True)
from pyOpenRPA import __version__
def pyOpenRPA_Index():
# Пример использования
global gRender
lStr = gRender.Generate(inDataDict={"title":"ОРКЕСТРАТОР PYOPENRPA", "subtitle":"ПАНЕЛЬ УПРАВЛЕНИЯ"})
lStr = gRender.Generate(inDataDict={"title":"ОРКЕСТРАТОР PYOPENRPA", "subtitle":"ПАНЕЛЬ УПРАВЛЕНИЯ", "version":__version__})
__Orchestrator__.WebRequestResponseSend(inResponeStr=lStr,inContentTypeStr="text/html")
def SettingsUpdate(inGlobalConfiguration):
def SettingsUpdate():
import os
import pyOpenRPA.Orchestrator
gSettingsDict = __Orchestrator__.GSettingsGet()
@ -487,32 +495,36 @@ def SettingsUpdate(inGlobalConfiguration):
#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, "..\\Resources\\Web\\orpa\\metadata.json"), "ResponseContentType": "application/json"},
{"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, "..\\Resources"),"UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/3rdParty/Semantic-UI-CSS-master/semantic.min.css", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.css"), "ResponseContentType": "text/css", "UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/3rdParty/Semantic-UI-CSS-master/semantic.min.js", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.js"), "ResponseContentType": "application/javascript", "UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/3rdParty/jQuery/jquery-3.1.1.min.js", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\jQuery\\jquery-3.1.1.min.js"), "ResponseContentType": "application/javascript", "UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/3rdParty/Google/LatoItalic.css", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Google\\LatoItalic.css"), "ResponseContentType": "font/css", "UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/3rdParty/Semantic-UI-CSS-master/themes/default/assets/fonts/icons.woff2", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default\\assets\\fonts\\icons.woff2"), "ResponseContentType": "font/woff2", "UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/themes/default/", "MatchType": "BeginWith", "ResponseFolderPath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default"),"UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/favicon.ico", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "Web\\favicon.ico"), "ResponseContentType": "image/x-icon", "UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/3rdParty/Handlebars/handlebars-v4.1.2.js", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Handlebars\\handlebars-v4.1.2.js"), "ResponseContentType": "application/javascript", "UACBool":False, "UseCacheBool": True},
{"Method": "GET", "URL": "/Monitor/ControlPanelDictGet", "MatchType": "Equal", "ResponseDefRequestGlobal": BackwardCompatibility.v1_2_0_Monitor_ControlPanelDictGet_SessionCheckInit, "ResponseContentType": "application/json"},
{"Method": "GET", "URL": "/GetScreenshot", "MatchType": "BeginWith", "ResponseDefRequestGlobal": pyOpenRPA_Screenshot, "ResponseContentType": "image/png"},
{"Method": "GET", "URL": "/pyOpenRPA_logo.png", "MatchType": "Equal", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\pyOpenRPA_logo.png"), "ResponseContentType": "image/png", "UACBool":False, "UseCacheBool": True},
{"Method": "POST", "URL": "/Orchestrator/UserRoleHierarchyGet", "MatchType": "Equal","ResponseDefRequestGlobal": BackwardCompatibility.v1_2_0_UserRoleHierarchyGet, "ResponseContentType": "application/json"},
#{"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, "..\\Resources"),"UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/orpa/client/resources/", "MatchType": "BeginWith", "ResponseFolderPath": os.path.join(lOrchestratorFolder, "Web"),"UACBool":False, "UseCacheBool": True},
#{"Method":"GET", "URL": "/3rdParty/Semantic-UI-CSS-master/semantic.min.css", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.css"), "ResponseContentType": "text/css", "UACBool":False, "UseCacheBool": True},
#{"Method":"GET", "URL": "/3rdParty/Semantic-UI-CSS-master/semantic.min.js", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.js"), "ResponseContentType": "application/javascript", "UACBool":False, "UseCacheBool": True},
#{"Method":"GET", "URL": "/3rdParty/jQuery/jquery-3.1.1.min.js", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\jQuery\\jquery-3.1.1.min.js"), "ResponseContentType": "application/javascript", "UACBool":False, "UseCacheBool": True},
#{"Method":"GET", "URL": "/3rdParty/Google/LatoItalic.css", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Google\\LatoItalic.css"), "ResponseContentType": "font/css", "UACBool":False, "UseCacheBool": True},
#{"Method":"GET", "URL": "/3rdParty/Semantic-UI-CSS-master/themes/default/assets/fonts/icons.woff2", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default\\assets\\fonts\\icons.woff2"), "ResponseContentType": "font/woff2", "UACBool":False, "UseCacheBool": True},
#{"Method":"GET", "URL": "/themes/default/", "MatchType": "BeginWith", "ResponseFolderPath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default"),"UACBool":False, "UseCacheBool": True},
{"Method":"GET", "URL": "/favicon.ico", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\orpa\\favicon.ico"), "ResponseContentType": "image/x-icon", "UACBool":False, "UseCacheBool": True},
#{"Method":"GET", "URL": "/3rdParty/Handlebars/handlebars-v4.1.2.js", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\Handlebars\\handlebars-v4.1.2.js"), "ResponseContentType": "application/javascript", "UACBool":False, "UseCacheBool": True},
#{"Method": "GET", "URL": "/Monitor/ControlPanelDictGet", "MatchType": "Equal", "ResponseDefRequestGlobal": BackwardCompatibility.v1_2_0_Monitor_ControlPanelDictGet_SessionCheckInit, "ResponseContentType": "application/json"},
#{"Method": "GET", "URL": "/GetScreenshot", "MatchType": "BeginWith", "ResponseDefRequestGlobal": pyOpenRPA_Screenshot, "ResponseContentType": "image/png"},
#{"Method": "GET", "URL": "/pyOpenRPA_logo.png", "MatchType": "Equal", "ResponseFilePath": os.path.join(lOrchestratorFolder, "..\\Resources\\Web\\pyOpenRPA_logo.png"), "ResponseContentType": "image/png", "UACBool":False, "UseCacheBool": True},
{"Method": "POST", "URL": "/orpa/client/user-role-hierarchy-get", "MatchType": "Equal","ResponseDefRequestGlobal": BackwardCompatibility.v1_2_0_UserRoleHierarchyGet, "ResponseContentType": "application/json"},
# New way of the v.1.2.0 functionallity (all defs by the URL from /pyOpenRPA/...)
{"Method": "POST", "URL": "/pyOpenRPA/ServerData", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_ServerData, "ResponseContentType": "application/json"},
{"Method": "GET", "URL": "/pyOpenRPA/ServerJSInit", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_ServerJSInit, "ResponseContentType": "application/javascript"},
{"Method": "POST", "URL": "/pyOpenRPA/ServerLog", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_ServerLog, "ResponseContentType": "application/json"},
{"Method": "GET", "URL": "/pyOpenRPA/Screenshot", "MatchType": "BeginWith", "ResponseDefRequestGlobal": pyOpenRPA_Screenshot, "ResponseContentType": "image/png"},
{"Method": "POST", "URL": "/pyOpenRPA/ProcessorQueueAdd", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Processor, "ResponseContentType": "application/json"},
{"Method": "POST", "URL": "/pyOpenRPA/ActivityListExecute", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_ActivityListExecute, "ResponseContentType": "application/json"},
{"Method": "POST", "URL": "/pyOpenRPA/Agent/O2A", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Agent_O2A, "ResponseContentType": "application/json"},
{"Method": "POST", "URL": "/pyOpenRPA/Agent/A2O", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Agent_A2O, "ResponseContentType": "application/json"},
{"Method": "GET", "URL": "/pyOpenRPA/Debugging/HelperDefList/", "MatchType": "BeginWith","ResponseDefRequestGlobal": pyOpenRPA_Debugging_HelperDefList, "ResponseContentType": "application/json"},
{"Method": "GET", "URL": "/pyOpenRPA/Debugging/HelperDefAutofill/", "MatchType": "BeginWith","ResponseDefRequestGlobal": pyOpenRPA_Debugging_HelperDefAutofill, "ResponseContentType": "application/json"},
{"Method": "POST", "URL": "/orpa/client/server-data", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_ServerData, "ResponseContentType": "application/json"},
{"Method": "GET", "URL": "/orpa/client/server-js-init", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_ServerJSInit, "ResponseContentType": "application/javascript"},
{"Method": "POST", "URL": "/orpa/client/server-log", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_ServerLog, "ResponseContentType": "application/json"},
#{"Method": "GET", "URL": "/orpa/client/screenshot-get", "MatchType": "Equal", "ResponseDefRequestGlobal": pyOpenRPA_Screenshot, "ResponseContentType": "image/png"},
# API
#{"Method": "POST", "URL": "/orpa/api/processor-queue-add", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Processor, "ResponseContentType": "application/json"},
#{"Method": "POST", "URL": "/orpa/api/activity-list-execute", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_ActivityListExecute, "ResponseContentType": "application/json"},
#{"Method": "GET", "URL": "/orpa/api/helper-def-list/", "MatchType": "BeginWith","ResponseDefRequestGlobal": pyOpenRPA_Debugging_HelperDefList, "ResponseContentType": "application/json"},
#{"Method": "GET", "URL": "/orpa/api/helper-def-autofill/", "MatchType": "BeginWith","ResponseDefRequestGlobal": pyOpenRPA_Debugging_HelperDefAutofill, "ResponseContentType": "application/json"},
# AGENT
#{"Method": "POST", "URL": "/orpa/agent/o2a", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Agent_O2A, "ResponseContentType": "application/json"},
#{"Method": "POST", "URL": "/orpa/agent/a2o", "MatchType": "Equal","ResponseDefRequestGlobal": pyOpenRPA_Agent_A2O, "ResponseContentType": "application/json"}
]
Usage.Process(inComponentStr="Orchestrator")
inGlobalConfiguration["ServerDict"]["URLList"]=inGlobalConfiguration["ServerDict"]["URLList"]+lURLList
return inGlobalConfiguration
gSettingsDict["ServerDict"]["URLList"]=gSettingsDict["ServerDict"]["URLList"]+lURLList
return gSettingsDict

@ -1,992 +0,0 @@
var mGlobal={}
mGlobal.pyOpenRPA = {}
window.onload=function() {
//document.cookie = "SessionGUIDStr=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
//Render existing data
//mGlobal.Monitor.fControlPanelRefresh_TechnicalRender()
}
$(document).ready(function() {
document.cookie = "SessionGUIDStr=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
console.log("Cookie is deleted")
// fix main menu to page on passing
$('.main.menu').visibility({
type: 'fixed'
});
$('.overlay').visibility({
type: 'fixed',
offset: 80
});
// lazy load images
$('.image').visibility({
type: 'image',
transition: 'vertical flip in',
duration: 500
});
// show dropdown on hover
$('.main.menu .ui.dropdown').dropdown({
on: 'hover'
});
function clone(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
//For data storage key
mGlobal["DataStorage"] = {}
// Clear the session cookie
String.prototype.replaceAll = function(search, replace){
return this.split(search).join(replace);
}
mGlobal.GeneralGenerateHTMLCodeHandlebars=function(inInnerTemplateSelector,inData) {
lHTMLTemplate=$(inInnerTemplateSelector)[0].innerHTML
//console.log(lHTMLTemplate)
//Компиляция
var template = Handlebars.compile(lHTMLTemplate);
//Вставка данных
var lHTMLResult = template(inData);
return lHTMLResult
}
mGlobal.GeneralGenerateHTMLCode=function(inTemplateHTMLSelector,inItemDictionary,inKeywordPrefix="::",inKeywordPostfix="::") {
///Получить заготовку
lTemplateHTMLCode=$(inTemplateHTMLSelector)[0].outerHTML
///Определить ключь экранирования специальных ключевых слов
///Выполнить циклические замены, если там есть пожходящие ключи
lResultHTMLCode=lTemplateHTMLCode
for(var lKey in inItemDictionary) {
lHTMLKey=inKeywordPrefix+lKey+inKeywordPostfix;
lResultHTMLCode=lResultHTMLCode.replaceAll(lHTMLKey,inItemDictionary[lKey])
}
///Вернуть результат
return lResultHTMLCode
}
//////////////////////////
/////Info JS module
//////////////////////////
mGlobal.Info={};
mGlobal.Info.TableActivityLogScheduleListRefresh=function() {
}
//////////////////////////
/////Controller JS module
//////////////////////////
mGlobal.Controller={};
mGlobal.Controller.CMDRunText=function(inCMDText) {
///Подготовить конфигурацию
lData = [
{"Type":"CMDStart", "Command": inCMDText}
]
///Обнулить таблицу
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3){},
dataType: "text"
});
}
mGlobal.Controller.CMDRun=function() {
///Обнулить таблицу
lCMDCode=$(".openrpa-controller-cmd-run-input")[0].value
///Подготовить конфигурацию
lData = [
{
"Def":"OSCMD", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
"ArgList":[], // Args list
"ArgDict":{"inCMDStr":lCMDCode,"inRunAsyncBool":false}, // Args dictionary
"ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
"ArgLogger": "inLogger" // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
}
]
$.ajax({
type: "POST",
url: '/pyOpenRPA/ActivityListExecute',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///Отправить запрос на формирование таблицы
//lHTMLCode=console.log("CMDRun result: "+lResponseJSON[0].result)
},
dataType: "text"
});
}
mGlobal.Controller.CMDRunGUILogout=function() {
///Обнулить таблицу
lCMDCode="for /f \"skip=1 tokens=2\" %s in ('query user %USERNAME%') do (tscon \\dest:console)"
//lCMDCode = lCMDCode.replace(/\\n/g, "\\n")
// .replace(/\\'/g, "\\'")
// .replace(/\\"/g, '\\"')
// .replace(/\\&/g, "\\&")
// .replace(/\\r/g, "\\r")
// .replace(/\\t/g, "\\t")
// .replace(/\\b/g, "\\b")
// .replace(/\\f/g, "\\f")
// .replace('"', "\\\"");
///Подготовить конфигурацию
lData = [
{"Type":"CMDStart", "Command": lCMDCode }
]
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///Отправить запрос на формирование таблицы
//lHTMLCode=console.log("CMDRun result: "+lResponseJSON[0].result)
},
dataType: "text"
});
}
///Restart PC
mGlobal.Controller.PCRestart = function () {
mGlobal.Controller.OrchestratorSessionSave()
mGlobal.Controller.CMDRunText("shutdown -r")
}
///Orchestrator save session
mGlobal.Controller.OrchestratorSessionSave=function() {
///Подготовить конфигурацию
lData = [
{"Type":"OrchestratorSessionSave"}
]
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
},
dataType: "text"
});
}
///Перезагрузить Orchestrator
mGlobal.Controller.OrchestratorRestart=function() {
///Подготовить конфигурацию
lData = [
{"Type":"OrchestratorRestart"}
]
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
},
dataType: "text"
});
}
mGlobal.Controller.OrchestratorGITPullRestart = function() {
mGlobal.Controller.OrchestratorSessionSave() //Save current RDP list session
mGlobal.Controller.CMDRunText("timeout 3 & taskkill /f /im OpenRPA_Orchestrator.exe & timeout 2 & cd "+mGlobal.WorkingDirectoryPathStr+" & git reset --hard & git pull & pyOpenRPA.Orchestrator_x64_administrator_startup.cmd");
}
//////////////////////////
/////Monitor JS module
//////////////////////////
mGlobal.Monitor={};
mGlobal.Monitor.ScreenshotModal={};
mGlobal.Monitor.GenerateUniqueID=function(inPrefix="tempUID=") {
return inPrefix+Math.round(Math.random()*1000)+"-"+Math.round(Math.random()*10000)+"-"+Math.round(Math.random()*1000)
}
//inHostURI: http://localhost:8081
mGlobal.Monitor.ScreenshotModal.Show=function(inHostURI=" ") {
$('.ui.modal.daemon-screenshot').modal({'onHide':function (inElement) {mGlobal.Monitor.ScreenshotModal.Close();} }).modal('show');
//Функция обновления картинки
lScreenshotUpdate=function() {
lScreenshotSrc=inHostURI+"/GetScreenshot?"+mGlobal.Monitor.GenerateUniqueID()
$(".daemon-screenshot img").attr('src', lScreenshotSrc);
}
mGlobal.Monitor.ScreenshotModal.timerId=setInterval(lScreenshotUpdate,1500)
}
mGlobal.Monitor.ScreenshotModal.Close=function() {
clearInterval(mGlobal.Monitor.ScreenshotModal.timerId);
}
///Monitor
mGlobal.Monitor.DaemonList={}
mGlobal.Monitor.DaemonList.fRefreshTable=function() {
///Загрузка данных
$.ajax({
type: "GET",
url: 'Monitor/JSONDaemonListGet',
data: '',
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///Сформировать HTML код новой таблицы
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-hidden-monitor-table-general",lResponseJSON)
///Очистить дерево
//mGlobal.ElementTree.fClear();
///Прогрузить новую таблицу
$(".openrpa-monitor").html(lHTMLCode)
},
dataType: "text"
});
}
////////////////////////////////
///////Control panel
///////////////////////////////
///Refresh control panel
function sleep(ms) {
ms += new Date().getTime();
while (new Date() < ms){}
}
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
mGlobal.SessionGUIDStr = uuidv4() // Generate uuid4 of the session
//console.log(uuidv4());
mGlobal.RobotRDPActive = {}
mGlobal.Monitor.fControlPanelRefresh_TechnicalRender = function()
{
lResponseJSON = mGlobal.Monitor.mDatasetLast
if (lResponseJSON!= null) {
/// New version of control panels
for (var lKeyStr in lResponseJSON){
if (lKeyStr != "RenderRobotList") { /// Check if not "RenderRobotList"
lCPDict = lResponseJSON[lKeyStr]
/// Render HTML
if ("HTMLStr" in lCPDict) {
}
}
}
/// v1.2.0 Backward compatibility - support old control panels
if ("RenderRobotList" in lResponseJSON) {
///Escape onclick
/// RenderRobotList
lResponseJSON["RenderRobotList"].forEach(
function(lItem){
if ('FooterButtonX2List' in lItem) {
/// FooterButtonX2List
lItem["FooterButtonX2List"].forEach(
function(lItem2){
if ('OnClick' in lItem) {
lOnClickEscaped = lItem["OnClick"];
lOnClickEscaped = lOnClickEscaped.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
lItem["OnClick"] = lOnClickEscaped;
}
}
);
/// FooterButtonX1List
lItem["FooterButtonX1List"].forEach(
function(lItem2){
if ('OnClick' in lItem) {
lOnClickEscaped = lItem["OnClick"];
lOnClickEscaped = lOnClickEscaped.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
lItem["OnClick"] = lOnClickEscaped;
}
}
);
}
}
);
//////////////////////////////////////////////////////////
///Сформировать HTML код новой таблицы - контрольная панель
lHTMLCode+=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-hidden-control-panel",lResponseJSON)
//Присвоить ответ в mGlobal.Monitor.mResponseList
mGlobal.Monitor.mResponseList = lResponseJSON
///Set result in mGlobal.DataStorage
lResponseJSON["RenderRobotList"].forEach(
function(lItem){
if ('DataStorageKey' in lItem) {
mGlobal["DataStorage"][lItem['DataStorageKey']]=lItem
}
}
)
///Прогрузить новую таблицу
$(".openrpa-control-panel").html(lHTMLCode)
////////////////////////////////////////////////////
/// !RDP List ! Сформировать HTML код новой таблицы - список RDP
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-hidden-robotrdpactive-control-panel",lResponseJSON)
//Присвоить ответ в mGlobal.RobotRDPActive.mResponseList
mGlobal.RobotRDPActive.mResponseList = lResponseJSON
///Прогрузить новую таблицу
$(".openrpa-robotrdpactive-control-panel").html(lHTMLCode)
///Очистить дерево
//mGlobal.ElementTree.fClear();
////////////////////////////////////////////////////
/// !UserAgent List ! Сформировать HTML код новой таблицы - список RDP
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".pyOpenRPA-Agent-ListTemplate",lResponseJSON)
//Присвоить ответ в mGlobal.RobotRDPActive.mResponseList
mGlobal.RobotRDPActive.mResponseList = lResponseJSON
///Прогрузить новую таблицу
$(".pyOpenRPA-Agent-List").html(lHTMLCode)
///Очистить дерево
//mGlobal.ElementTree.fClear();
}
}
}
///v 1.2.0 pyOpenRPA
/// Execute ActivityItem
mGlobal.pyOpenRPA.ActivityItemExecute=function(inActivityItem) {
///EXAMPLE
// {
// "Def":"OSCMD", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
// "ArgList":[], // Args list
// "ArgDict":{"inCMDStr":lCMDCode,"inRunAsyncBool":false}, // Args dictionary
// "ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
// "ArgLogger": "inLogger" // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
// }
///Подготовить конфигурацию
lData = [inActivityItem]
$.ajax({
type: "POST",
url: '/pyOpenRPA/ActivityListExecute',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
console.log(lResponseJSON)
},
dataType: "text"
});
}
/// Execute ActivityList
mGlobal.pyOpenRPA.ActivityListExecute=function(inActivityList) {
///EXAMPLE
// [{
// "Def":"OSCMD", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
// "ArgList":[], // Args list
// "ArgDict":{"inCMDStr":lCMDCode,"inRunAsyncBool":false}, // Args dictionary
// "ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
// "ArgLogger": "inLogger" // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
// }]
///Подготовить конфигурацию
lData = inActivityList
$.ajax({
type: "POST",
url: '/pyOpenRPA/ActivityListExecute',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
console.log(lResponseJSON)
},
dataType: "text"
});
}
/// Add ActivityList in processor queue
mGlobal.pyOpenRPA.ProcessorQueueAdd=function(inActivityList) {
///EXAMPLE
// [{
// "Def":"OSCMD", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
// "ArgList":[], // Args list
// "ArgDict":{"inCMDStr":lCMDCode,"inRunAsyncBool":false}, // Args dictionary
// "ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
// "ArgLogger": "inLogger" // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
// }]
///Подготовить конфигурацию
lData = inActivityList
$.ajax({
type: "POST",
url: '/pyOpenRPA/ProcessorQueueAdd',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
console.log(lResponseJSON)
},
dataType: "text"
});
}
/// v1.2.0 pyOpenRPA ServerJSInit
mGlobal.pyOpenRPA.ServerJSInitDef=function() {
try {
$.ajax({
type: "GET",
headers: {},
url: 'pyOpenRPA/ServerJSInit',
data: mGlobal.pyOpenRPA.ServerDataHashStr,
async: false,
success: function(lJSText) {
try {
eval(lJSText)
}
catch(error) {
console.log(error)
}
},
dataType: "text",
error: function(jqXHR, textStatus, errorThrown ) {
console.log(textStatus)
}
});
}
catch(error) {
console.log(error)
}
}
/// v1.2.0 pyOpenRPA ServerData
mGlobal.pyOpenRPA.ServerDataDict = null
mGlobal.pyOpenRPA.ServerDataHashStr = ""
mGlobal.pyOpenRPA.ServerDataRefreshDef_TechnicalRender = function()
{
lResponseJSON = mGlobal.pyOpenRPA.ServerDataDict
if (lResponseJSON!= null) {
/// New version of control panels
lHTMLCode = '<div class="ui cards">'
for (var lKeyStr in lResponseJSON["CPDict"]){
lCPDict = lResponseJSON["CPDict"][lKeyStr]
/// Render HTML
if ("HTMLStr" in lCPDict) {
lHTMLCode+=lCPDict["HTMLStr"]
}
}
lHTMLCode += '</div>'
///Прогрузить новую таблицу
$(".openrpa-control-panel").html(lHTMLCode)
////////////////////////////////////////////////////
/// !RDP List ! Сформировать HTML код новой таблицы - список RDP
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-hidden-robotrdpactive-control-panel",lResponseJSON["RDPDict"])
//Присвоить ответ в mGlobal.RobotRDPActive.mResponseList
mGlobal.RobotRDPActive.mResponseList = lResponseJSON["RDPDict"]
///Прогрузить новую таблицу
$(".openrpa-robotrdpactive-control-panel").html(lHTMLCode)
///Очистить дерево
//mGlobal.ElementTree.fClear();
////////////////////////////////////////////////////
/// !UserAgent List ! Сформировать HTML код новой таблицы - список RDP
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".pyOpenRPA-Agent-ListTemplate",lResponseJSON["AgentDict"])
///Прогрузить новую таблицу
$(".pyOpenRPA-Agent-List").html(lHTMLCode)
///Очистить дерево
//mGlobal.ElementTree.fClear();
}
}
mGlobal.pyOpenRPA.ServerDataRefreshDef=function() {
try {
$.ajax({
type: "POST",
headers: {},
url: 'pyOpenRPA/ServerData',
data: mGlobal.pyOpenRPA.ServerDataHashStr,
success: function(lData,l2,l3) {
try {
var lResponseJSON=JSON.parse(lData)
mGlobal.VersionStr = lResponseJSON["ServerDataDict"]["UserDict"]["VersionStr"]
mGlobal.pyOpenRPA.ServerDataDict = lResponseJSON["ServerDataDict"]
mGlobal.pyOpenRPA.ServerDataHashStr = lResponseJSON["HashStr"]
mGlobal.pyOpenRPA.ServerDataRefreshDef_TechnicalRender()
mGlobal.UserRoleUpdate();
setTimeout(mGlobal.pyOpenRPA.ServerDataRefreshDef,600) // If LOGS are update every ms - set some limit in ms on the client side
//mGlobal.pyOpenRPA.ServerDataRefreshDef() // Go to the next call
}
catch(error) {
console.log(error)
setTimeout(mGlobal.pyOpenRPA.ServerDataRefreshDef,3000)
}
//mGlobal.pyOpenRPA.ServerDataRefreshDef() // recursive
},
dataType: "text",
error: function(jqXHR, textStatus, errorThrown ) {
setTimeout(mGlobal.pyOpenRPA.ServerDataRefreshDef,3000)
//sleep(3000)
//mGlobal.pyOpenRPA.ServerDataRefreshDef() // recursive
}
});
}
catch(error) {
setTimeout(mGlobal.pyOpenRPA.ServerDataRefreshDef,3000)
//sleep(3000)
//mGlobal.pyOpenRPA.ServerDataRefreshDef() // recursive
}
}
/////////////////////////////////////////////////////////////
/// v1.2.0 pyOpenRPA ServerLogs
mGlobal.pyOpenRPA.ServerLogList = null
mGlobal.pyOpenRPA.ServerLogListHashStr = ""
mGlobal.pyOpenRPA.ServerLogListDoRenderBool = true
///Turn OFF rendering
mGlobal.pyOpenRPA.ServerLogListDoRenderFalse = function() {
///Set unfreeze button
$("a.mGlobal-pyOpenRPA-ServerLogListDoRender").html("Unfreeze textarea")
$("a.mGlobal-pyOpenRPA-ServerLogListDoRender").attr("onclick","mGlobal.pyOpenRPA.ServerLogListDoRenderTrue()")
$("textarea.mGlobal-pyOpenRPA-ServerLogList").css("background-color","#b9e2e8")
mGlobal.pyOpenRPA.ServerLogListDoRenderBool = false
}
///Turn ON rendering
mGlobal.pyOpenRPA.ServerLogListDoRenderTrue = function() {
mGlobal.pyOpenRPA.ServerLogListDoRenderBool = true
///Render last data
mGlobal.pyOpenRPA.ServerLogListRefreshDef_TechnicalRender()
///Set unfreeze button
$("a.mGlobal-pyOpenRPA-ServerLogListDoRender").html("Freeze textarea")
$("a.mGlobal-pyOpenRPA-ServerLogListDoRender").attr("onclick","mGlobal.pyOpenRPA.ServerLogListDoRenderFalse()")
$("textarea.mGlobal-pyOpenRPA-ServerLogList").css("background-color","")
}
mGlobal.pyOpenRPA.ServerLogListScrollBottomDef = function() {
var lTA = $("textarea.mGlobal-pyOpenRPA-ServerLogList")[0];
lTA.scrollTop = lTA.scrollHeight;
}
mGlobal.pyOpenRPA.ServerLogListRefreshDef_TechnicalRender = function()
{
lResponseJSON = mGlobal.pyOpenRPA.ServerLogList
if (lResponseJSON!= null && mGlobal.pyOpenRPA.ServerLogListDoRenderBool==true) {
lText = lResponseJSON.join("\n") /// Code for the processing the text
$("textarea.mGlobal-pyOpenRPA-ServerLogList")[0].value= lText ///Прогрузить новую таблицу
mGlobal.pyOpenRPA.ServerLogListScrollBottomDef() //Scroll to the bottom
}
}
mGlobal.pyOpenRPA.ServerLogListRefreshDef=function() {
try {
$.ajax({
type: "POST",
headers: {},
url: 'pyOpenRPA/ServerLog',
data: mGlobal.pyOpenRPA.ServerLogListHashStr,
success: function(lData,l2,l3) {
try {
var lResponseJSON=JSON.parse(lData)
mGlobal.pyOpenRPA.ServerLogList = lResponseJSON["ServerLogList"]
mGlobal.pyOpenRPA.ServerLogListHashStr = lResponseJSON["HashStr"]
mGlobal.pyOpenRPA.ServerLogListRefreshDef_TechnicalRender()
}
catch(error) {
}
setTimeout(mGlobal.pyOpenRPA.ServerLogListRefreshDef,600) // If LOGS are update every ms - set some limit in ms on the client side
//mGlobal.pyOpenRPA.ServerLogListRefreshDef() // recursive
},
dataType: "text",
error: function(jqXHR, textStatus, errorThrown ) {
setTimeout(mGlobal.pyOpenRPA.ServerLogListRefreshDef,3000)
//sleep(3000)
//mGlobal.pyOpenRPA.ServerLogListRefreshDef() // recursive
}
});
}
catch(error) {
setTimeout(mGlobal.pyOpenRPA.ServerLogListRefreshDef,3000)
//sleep(3000)
//mGlobal.pyOpenRPA.ServerLogListRefreshDef() // recursive
}
}
/////////////////////////////////////////////////////////////
mGlobal.Monitor.mDatasetLast = null
///////////////////////////////
///Processor functions
///////////////////////////////
mGlobal.Processor = {}
mGlobal.Processor.ServerValueAppend = function(inKeyList,inValue) {
lData = [
{
"Type":"GlobalDictKeyListValueAppend",
"KeyList": inKeyList,
"Value": inValue
}
]
///Обнулить таблицу
$('.ui.modal.basic .content').html("");
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///TODO Show error if exist error
},
dataType: "text"
});
}
mGlobal.Processor.ServerValueSet = function(inKeyList,inValue) {
lData = [
{
"Type":"GlobalDictKeyListValueSet",
"KeyList": inKeyList,
"Value": inValue
}
]
///Обнулить таблицу
$('.ui.modal.basic .content').html("");
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///TODO Show error if exist error
},
dataType: "text"
});
}
mGlobal.Processor.ServerValueOperatorPlus = function(inKeyList,inValue) {
lData = [
{
"Type":"GlobalDictKeyListValueOperator+",
"KeyList": inKeyList,
"Value": inValue
}
]
///Обнулить таблицу
$('.ui.modal.basic .content').html("");
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///TODO Show error if exist error
},
dataType: "text"
});
}
mGlobal.Processor.Send = function(inData) {
lData = inData
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///TODO Show error if exist error
},
dataType: "text"
});
}
mGlobal.Server= {}
mGlobal.Server.JSONGet=function(inMethod, inURL, inDataJSON, inCallback)
{
$.ajax({
type: inMethod,
url: inURL,
data: JSON.stringify(inDataJSON),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
inCallback(lResponseJSON)
},
dataType: "text"
});
}
/////////////////
///Modal
///////////////////
mGlobal.Modal={}
/////////////////////////////////////////////////////
mGlobal.Modal.TableFilter={}
mGlobal.Modal.TableFilter.Show=function(inJSON) {
//{
// "Title":"",
// "Headers":["Header1","Header2"],
// "Rows": [["Cell1","Cell2"],["Cell2-1","Cell2-2"]],
// "FilterOnKeyUp": "<JS Code>" //Fill here in function
//}
//Set js handler to Search field
inJSON["FilterOnKeyUp"]="mGlobal.Modal.TableFilter.FilterUpdate(this.value);"
///Set value
mGlobal.Modal.TableFilter.mDataJSON = inJSON
//Render HTML
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-handlebar-template-table-filter",inJSON);
///Установить HTML код
$('.ui.modal.basic .content').html(lHTMLCode);
$('.ui.modal.basic').modal('show');
//DO widest modal for table with scroll x
$("div.ui.basic.modal.transition.visible.active.scrolling")[0].style["width"]="1300px"
$("div.ui.basic.modal.transition.visible.active.scrolling")[0].style["overflow"]="scroll"
}
//Service function
mGlobal.Modal.TableFilter.FilterUpdate=function(inFilterValue) {
//Get JSON, apply filter, clone data
lDataJSON = clone(mGlobal.Modal.TableFilter.mDataJSON)
delete lDataJSON["Rows"]
lDataJSON["Rows"]=[]
//Filter code [any occurence in the row is ok for push! ]
mGlobal.Modal.TableFilter.mDataJSON["Rows"].forEach(
function(inElement) {
lFlagElementAppend = false
inElement.forEach(
function(inElement2) {
if (String(inElement2).includes(inFilterValue)) {
lFlagElementAppend = true
}
}
)
if (lFlagElementAppend) {
lDataJSON["Rows"].push(inElement)
}
}
)
//Clear Filter Title property (fixed in html)
delete lDataJSON["FilterOnKeyUp"]
delete lDataJSON["Title"]
//Search the table element [replace only table html]
lElement = $('.ui.modals.active .content table.table')[0]
lElementParentElement = lElement.parentNode
lElement.parentNode.removeChild(lElement);
//Render HTML
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-handlebar-template-table-filter",lDataJSON);
///Установить HTML код
lElementParentElement.insertAdjacentHTML("beforeend",lHTMLCode);
}
/////////////////////////////////////////////////////////////
mGlobal.Modal.ListFilter={}
mGlobal.Modal.ListFilter.Show=function(inJSON) {
//{
// "Title":"",
// "List":[{"Header":"","Description":""}],
// "FilterOnKeyUp": "<JS Code>" //Fill here in function
//}
//Set js handler to Search field
inJSON["FilterOnKeyUp"]="mGlobal.Modal.ListFilter.FilterUpdate(this.value);"
///Set value
mGlobal.Modal.ListFilter.mDataJSON = inJSON
//Render HTML
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-handlebar-template-list-filter",inJSON);
///Установить HTML код
$('.ui.modal.basic .content').html(lHTMLCode);
$('.ui.modal.basic').modal('show');
}
//Service function
mGlobal.Modal.ListFilter.FilterUpdate=function(inFilterValue) {
//Get JSON, apply filter, clone data
lDataJSON = clone(mGlobal.Modal.ListFilter.mDataJSON)
delete lDataJSON["List"]
lDataJSON["List"]=[]
//Filter code [any occurence in the row is ok for push! ]
mGlobal.Modal.ListFilter.mDataJSON["List"].forEach(
function(inElement) {
lFlagElementAppend = false
if (String(inElement["Header"]).includes(inFilterValue)) {
lFlagElementAppend = true
}
if (String(inElement["Description"]).includes(inFilterValue)) {
lFlagElementAppend = true
}
if (lFlagElementAppend) {
lDataJSON["List"].push(inElement)
}
}
)
//Clear Filter Title property (fixed in html)
delete lDataJSON["FilterOnKeyUp"]
delete lDataJSON["Title"]
//Search the table element [replace only table html]
lElement = $('.ui.modals.active .content div.ui.inverted.segment')[0]
lElementParentElement = lElement.parentNode
lElement.parentNode.removeChild(lElement);
//Render HTML
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-handlebar-template-list-filter",lDataJSON);
///Установить HTML код
lElementParentElement.insertAdjacentHTML("beforeend",lHTMLCode);
}
mGlobal.UserRoleHierarchyDict = null // Put here the user role hierarchy
// UAC Ask
mGlobal.UserRoleAsk=function(inList) {
var lResult = true; // Init flag
var lRoleHierarchyDict = mGlobal.pyOpenRPA.ServerDataDict.UserDict.UACClientDict; // get the Hierarchy
// Try to get value from key list
var lKeyValue = lRoleHierarchyDict; // Init the base
var lListLength = inList.length;
for (var i = 0; i<lListLength; i++) {
var lItem = inList[i]; // get the item
if (typeof lKeyValue == "object") {
if (lItem in lKeyValue) { // Has key
lKeyValue = lKeyValue[lItem]; // Get the value and go to the next loop iteration
} else { // Else branch - true or false
if (Object.keys(lKeyValue).length > 0) { // false - if Dict has some elements
lResult = false; // Set the False Flag
} else {
lResult = true; // Set the true flag
}
break; // Stop the loop
}
} else { // Has element with no detalization - return true
lResult = true; // Set the flag
break; // Close the loop
}
}
return lResult; // Return the result
}
// Check user roles and update the Orchestrator UI
mGlobal.UserRoleUpdate=function() {
var lUACAsk = mGlobal.UserRoleAsk // Alias
//CPKeyDict
if (lUACAsk(["pyOpenRPADict","CPKeyDict"])) { $(".UACClient-pyOpenRPADict-CPKeyDict").show(); }
//RDPKeyDict
if (lUACAsk(["pyOpenRPADict","RDPKeyDict"])) { $(".UACClient-pyOpenRPADict-RDPKeyDict").show(); }
//AgentKeyDict
if (lUACAsk(["pyOpenRPADict","AgentKeyDict"])) { $(".UACClient-pyOpenRPADict-AgentKeyDict").show(); }
// AdminDict
if (lUACAsk(["pyOpenRPADict","AdminDict","LogViewerBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-LogViewerBool").show(); }
if (lUACAsk(["pyOpenRPADict","AdminDict","CMDInputBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-CMDInputBool").show(); }
if (lUACAsk(["pyOpenRPADict","AdminDict","ScreenshotViewerBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-ScreenshotViewerBool").show(); }
if (lUACAsk(["pyOpenRPADict","AdminDict","RestartOrchestratorBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-RestartOrchestratorBool").show(); }
if (lUACAsk(["pyOpenRPADict","AdminDict","RestartOrchestratorGITPullBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-RestartOrchestratorGITPullBool").show(); }
if (lUACAsk(["pyOpenRPADict","AdminDict","RestartPCBool"])) { $(".UACClient-pyOpenRPADict-AdminDict-RestartPCBool").show(); }
if (lUACAsk(["pyOpenRPADict","AdminDict","Debugging"])) { $(".UACClient-pyOpenRPADict-AdminDict-Debugging").show(); }
}
/// v1.2.0 pyOpenRPA Init defs
mGlobal.pyOpenRPA.ServerDataRefreshDef(); // Init the refresh data def from server side
mGlobal.pyOpenRPA.ServerLogListRefreshDef(); // Init the refresh data def from the log window
mGlobal.pyOpenRPA.ServerLogListDoRenderTrue(); // Init button to freeze/unfreeze textare with logs
mGlobal.pyOpenRPA.ServerJSInitDef(); // Recieve JS from server (if exist) and then call anothe url ServerData
//$('.ui.dropdown').dropdown();
////////////////////////////////////////////
// 1.2.7 Debugging
/// Execute ActivityItem
// Debugging onchange def autofill init
var lDropdownOnChange = function(inEvent){
//lValueStr = inEvent.target.value
lValueStr = inEvent
$.ajax({
type: "GET",
url: '/pyOpenRPA/Debugging/HelperDefAutofill/'+lValueStr,
data: null,
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
console.log("HelperDefAutofill:")
console.log(lResponseJSON)
//ArgDict merge
var lArgDictTargetDict = lResponseJSON["ArgDict"]
var lArgDictStr = $(".mGlobal-pyOpenRPA-Debugging-ArgDict")[0].value
if (lArgDictStr !="" && lArgDictStr !=null) {
lArgDictLastDict = JSON.parse(lArgDictStr)
lArgDictTargetDict = mGlobal.pyOpenRPA.DebuggingAutofillMerge(lArgDictTargetDict, lArgDictLastDict)
}
$(".mGlobal-pyOpenRPA-Debugging-ArgList")[0].value = JSON.stringify(lResponseJSON["ArgList"])
$(".mGlobal-pyOpenRPA-Debugging-ArgDict")[0].value = JSON.stringify(lArgDictTargetDict)
$(".mGlobal-pyOpenRPA-Debugging-ArgGSettingsStr")[0].value = JSON.stringify(lResponseJSON["ArgGSettingsStr"])
$(".mGlobal-pyOpenRPA-Debugging-ArgLoggerStr")[0].value = JSON.stringify(lResponseJSON["ArgLoggerStr"])
},
dataType: "text"
});
}
//$('.ui.dropdown.mGlobal-pyOpenRPA-Debugging-Def-Dropdown')[0].onchange=lDropdownOnChange
mGlobal.pyOpenRPA.DebuggingExecute=function() {
///EXAMPLE
// {
// "Def":"OSCMD", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
// "ArgList":[], // Args list
// "ArgDict":{"inCMDStr":lCMDCode,"inRunAsyncBool":false}, // Args dictionary
// "ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
// "ArgLogger": "inLogger" // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
// }
///Подготовить конфигурацию
lArgListStr = $(".mGlobal-pyOpenRPA-Debugging-ArgList")[0].value
lArgDictStr = $(".mGlobal-pyOpenRPA-Debugging-ArgDict")[0].value
lArgGSettingsStr = $(".mGlobal-pyOpenRPA-Debugging-ArgGSettingsStr")[0].value
lArgLoggerStr = $(".mGlobal-pyOpenRPA-Debugging-ArgLoggerStr")[0].value
lActivityItem = {
"Def":$(".mGlobal-pyOpenRPA-Debugging-Def")[0].value, // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
"ArgList":(lArgListStr == "" ? [] : JSON.parse(lArgListStr)), // Args list
"ArgDict":(lArgDictStr == "" ? {} : JSON.parse(lArgDictStr)), // Args dictionary
"ArgGSettingsStr": (lArgGSettingsStr == "" ? null : lArgGSettingsStr), // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
"ArgLoggerStr": (lArgLoggerStr == "" ? null : lArgLoggerStr) // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
}
lData = [lActivityItem]
$.ajax({
type: "POST",
url: '/pyOpenRPA/ActivityListExecute',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
console.log(lResponseJSON)
$(".mGlobal-pyOpenRPA-Debugging-Output")[0].value = JSON.stringify(lResponseJSON[0])
},
dataType: "text"
});
}
mGlobal.pyOpenRPA.DebuggingAutofillMerge=function(inTargetDict, inLastDict) {
// Merge 2 dict (get values from Last dict if key exists in new dict
for (const [lKeyStr, lValue] of Object.entries(inTargetDict)) {
//Check if key exists in LastDict
if (lKeyStr in inLastDict) {
inTargetDict[lKeyStr] = inLastDict[lKeyStr]
}
}
return inTargetDict
}
// 1.2.7 Debugging toolbox init
$('.ui.dropdown.mGlobal-pyOpenRPA-Debugging-Def-Dropdown')
.dropdown({
apiSettings: {
// this url parses query server side and returns filtered results
url: '/pyOpenRPA/Debugging/HelperDefList/{query}'
},
onChange: lDropdownOnChange
})
;
});

@ -1,499 +0,0 @@
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="utf-8">
<title>pyOpenRPA Orchestrator</title>
<link rel="stylesheet" type="text/css" href="3rdParty/Semantic-UI-CSS-master/semantic.min.css">
<script
src="3rdParty/jQuery/jquery-3.1.1.min.js"
crossorigin="anonymous"></script>
<script src="3rdParty/Semantic-UI-CSS-master/semantic.min.js"></script>
<script src="3rdParty/Handlebars/handlebars-v4.1.2.js"></script>
<script src = "Index.js"></script>
<style type="text/css">
body {
background-color: #FFFFFF;
}
.main.container {
margin-top: 2em;
}
.overlay {
float: left;
margin: 0em 3em 1em 0em;
}
.overlay .menu {
position: relative;
left: 0;
transition: left 0.5s ease;
}
.main.menu.fixed {
background-color: #FFFFFF;
border: 1px solid #DDD;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.2);
}
.overlay.fixed .menu {
left: 800px;
}
.text.container .left.floated.image {
margin: 2em 2em 2em -4em;
}
.text.container .right.floated.image {
margin: 2em -4em 2em 2em;
}
.ui.footer.segment {
margin: 5em 0em 0em;
padding: 5em 0em;
}
.ui.search.dropdown>input.search {
width:100%;
font-family:monospace;
font-weight: bold;
}
.ui.search.dropdown>.text {
width:100%;
font-family:monospace;
font-weight: bold;
}
</style>
</head>
<body>
<div class="ui internally celled grid">
<div class="row black">
<div class="sixteen wide column" style="display: flex;">
<img src="pyOpenRPA_logo.png" width="70px;" height="70px"></img>
&nbsp;&nbsp;&nbsp;
<h1 class="ui header inverted" style="cursor: pointer" onclick="window.open('https://gitlab.com/UnicodeLabs/OpenRPA','_blank');">pyOpenRPA</h1>
<h5 style="cursor: pointer" onclick="window.open('https://www.facebook.com/RU.IT4Business','_blank');">by Ivan Maslov</h5>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
<h1 class="ui header inverted">ORCHESTRATOR WEB GUI</h1>
</div>
</div>
<div class="row">
<div class="sixteen wide column openrpa-control-panel-general UACClient-pyOpenRPADict-CPKeyDict" style="display:none;" >
<h4 class="ui horizontal divider header">
<i class="clipboard list icon"></i>
Dashboard (Robot control panel)
</h4>
<div class="openrpa-control-panel"></div>
<script class="openrpa-hidden-control-panel" style="display:none" type="text/x-handlebars-template">
<div class="ui cards">
{{#RenderRobotList}}
<div class="card">
<div class="content">
<div class="right floated mini ui ">
{{{HeaderRightText}}}
</div>
<div class="header">
{{{HeaderLeftText}}}
</div>
<div class="meta">
{{{SubheaderText}}}
</div>
<div class="description">
<ul style="padding-inline-start:16px;margin:0px">
{{#BodyKeyValueList}}
<li>{{{Key}}}: {{{Value}}}</li>
{{/BodyKeyValueList}}
</ul>
</div>
</div>
<div class="extra content">
{{{FooterText}}}
</div>
<div class="extra content">
<div class="ui two buttons">
{{#FooterButtonX2List}}
<div class="ui basic {{Color}} button" onclick="{{OnClick}}">{{{Text}}}</div>
{{/FooterButtonX2List}}
</div>
<div class="ui horizontal divider">Add. controls</div>
<div class="ui one buttons">
{{#FooterButtonX1List}}
<div class="ui basic {{Color}} button" onclick="{{OnClick}}">{{{Text}}}</div>
{{/FooterButtonX1List}}
</div>
</div>
</div>
{{/RenderRobotList}}
</div>
</script>
</div>
</div>
<div class="row">
<script class="openrpa-hidden-monitor-table-general" style="display:none" type="text/x-handlebars-template">
<table class="ui celled table">
<thead>
<tr>
<th>Machine name</th>
<th>Machihe host</th>
<th>Status</th>
<th>Actions,length: {{childs.length}}</th>
</tr>
</thead>
<tbody>
{{#ListenURLList}}
<tr><td>{{Description}}</td><td>{{URL}}</td><td class="negative">None</td></tr>
{{/ListenURLList}}
</tbody>
</table>
</script>
<script class="openrpa-handlebar-template-table-filter" style="display:none" type="text/x-handlebars-template">
{{#if Title}}
<h1>{{{Title}}}</h1>
{{/if}}
{{#if FilterOnKeyUp}}
<div class="ui icon input search" style="width:500px;">
<input type="text" onkeyup="{{#if FilterOnKeyUp}}{{{FilterOnKeyUp}}}{{/if}}" placeholder="Search...">
<i class="inverted circular search link icon"></i>
</div>
{{/if}}
<table class="ui celled table selectable inverted">
<thead>
<tr>
{{#Columns}}
<th>{{{this}}}</th>
{{/Columns}}
</tr>
</thead>
<tbody>
{{#Rows}}
<tr>
{{#this}}
<td>
{{{this}}}
</td>
{{/this}}
</tr>
{{/Rows}}
</tbody>
</table>
</script>
<script class="openrpa-handlebar-template-list-filter" style="display:none" type="text/x-handlebars-template">
{{#if Title}}
<h1>{{{Title}}}</h1>
{{/if}}
{{#if FilterOnKeyUp}}
<div class="ui icon input search" style="width:500px;">
<input type="text" onkeyup="{{#if FilterOnKeyUp}}{{{FilterOnKeyUp}}}{{/if}}" placeholder="Search...">
<i class="inverted circular search link icon"></i>
</div>
{{/if}}
<div class="ui inverted segment">
<div class="ui inverted relaxed divided list">
{{#List}}
<div class="item">
<i class="map marker icon"></i>
<div class="content">
<a class="header">{{{Header}}}</a>
<div class="description">{{{Description}}}</div>
</div>
</div>
{{/List}}
</div>
</div>
</script>
<div class="eight wide column openrpa-robotrdpactive-control-panel-general UACClient-pyOpenRPADict-RDPKeyDict" style="display:none;">
<h2 class="ui header openrpa-rdpactive-title">
<i class="desktop icon"></i>
<div class="content">
RDP active list
</div>
</h2>
<div class="openrpa-robotrdpactive-control-panel"></div>
<script class="openrpa-hidden-robotrdpactive-control-panel" style="display:none" type="text/x-handlebars-template">
<div class="ui inverted segment" style="background: #368279">
<div class="ui inverted relaxed divided list">
{{#HandlebarsList}}
<div class="item">
<div class="right floated content">
<div class="ui button" onclick="mGlobal.Processor.ServerValueAppend(['RobotRDPActive','ActivityList'],{'DefNameStr': 'RDPSessionReconnect', 'ArgList': [], 'ArgDict': {'inRDPSessionKeyStr': '{{{SessionKeyStr}}}'} })" >Reconnect</div>
</div>
<div class="right floated content">
{{#if IsIgnoredBool}}
<div class="ui button red" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','RDPList','{{{SessionKeyStr}}}','SessionIsIgnoredBool'],false);">Ignore</div>
{{else}}
<div class="ui button" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','RDPList','{{{SessionKeyStr}}}','SessionIsIgnoredBool'],true);">Ignore</div>
{{/if}}
</div>
<div class="right floated content">
{{#if IsFullScreenBool}}
<div class="ui button green" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','FullScreenRDPSessionKeyStr'],null);">Full screen</div>
{{else}}
<div class="ui button" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','FullScreenRDPSessionKeyStr'],'{{{SessionKeyStr}}}');">Full screen</div>
{{/if}}
</div>
<div class="content">
<div class="header">Session key: {{{SessionKeyStr}}}</div>
{{{SessionHexStr}}}
</div>
</div>
{{/HandlebarsList}}
</div>
</div>
</script>
</div>
<div class="eight wide column UACClient-pyOpenRPADict-AgentKeyDict" style="display:none">
<h2 class="ui header " style="">
<i class="bug icon"></i>
<div class="content">
Agent active list
</div>
</h2>
<div class="pyOpenRPA-Agent-List"></div>
<script class="pyOpenRPA-Agent-ListTemplate" style="display:none" type="text/x-handlebars-template">
<div class="ui inverted segment" style="background: #368279">
<div class="ui inverted relaxed divided list">
{{#HandlebarsList}}
<div class="item">
<div class="right floated content">
{{#if IsListenBool}}
<i class="circle icon green"></i>
Online
{{else}}
<i class="circle icon red"></i>
Offline
{{/if}}
</div>
<div class="content">
<div class="header">Hostname: {{{HostnameUpperStr}}}, User: {{{UserUpperStr}}}</div>
</div>
</div>
{{/HandlebarsList}}
</div>
</div>
</script>
</div>
</div>
<div class="row openrpa-monitor">
</div>
<div class="row">
<div class="sixteen wide column" style="">
<h2 class="ui header">
<i class="settings icon"></i>
<div class="content">
Administration
</div>
</h2>
</div>
</div>
<div class="row">
<div class="sixteen wide column" style="">
<h4 class="ui horizontal divider header" >
<i class="clipboard list icon"></i>
Logs
</h4>
<textarea class="mGlobal-pyOpenRPA-ServerLogList UACClient-pyOpenRPADict-AdminDict-LogViewerBool" readonly="readonly" style="width:100%; display:none; resize: none; font-family:monospace; font-weight: bold;" id="textarea_id" rows="20">
</textarea>
<a class="mGlobal-pyOpenRPA-ServerLogListDoRender" onclick="" style="cursor: pointer;">Freeze textarea</a>
<div class="ui fluid action input UACClient-pyOpenRPADict-AdminDict-CMDInputBool" style="display:none;">
<input class="openrpa-controller-cmd-run-input" type="text" placeholder="CMD Code...">
<div class="ui button" onclick="mGlobal.Controller.CMDRun();">Run!</div>
<div class="ui button" onclick="mGlobal.Controller.CMDRunGUILogout();">GUI Logout</div>
</div>
</div>
</div>
<div class="row UACClient-pyOpenRPADict-AdminDict-Debugging" style= "display:none;">
<div class="twelve wide column">
<h4 class="ui horizontal divider header" >
<i class="bug icon"></i>
Debugging - Send
</h4>
<div class="ui labeled input">
<div class="ui label">Def</div>
</div>
<div class="ui fluid search selection dropdown mGlobal-pyOpenRPA-Debugging-Def-Dropdown" style="margin-bottom:10px;">
<input class="mGlobal-pyOpenRPA-Debugging-Def" type="hidden" name="country" style="width:100%; font-family:monospace; font-weight: bold;">
<i class="dropdown icon"></i>
<div class="default text">Def</div>
<div class="menu">
<div class="item" data-value="eh">pyOpenRPA... sys.. os.. </div>
</div>
</div>
<div class="ui fluid labeled input" style="margin-bottom:10px;">
<div class="ui label">
ArgList
</div>
<input type="text" placeholder="[1,2,3]" class="mGlobal-pyOpenRPA-Debugging-ArgList" style="width:100%; font-family:monospace; font-weight: bold;">
</div>
<div class="ui fluid labeled input" style="margin-bottom:10px;">
<div class="ui label">
ArgDict
</div>
<input type="text" placeholder="{&quot;Key1&quot;:&quot;Value1&quot;}" class="mGlobal-pyOpenRPA-Debugging-ArgDict" style="width:100%; font-family:monospace; font-weight: bold;">
</div>
<div class="ui fluid labeled input" style="margin-bottom:10px;">
<div class="ui label">
ArgGSettingsStr
</div>
<input type="text" placeholder="inGSettings" class="mGlobal-pyOpenRPA-Debugging-ArgGSettingsStr" style="width:100%; font-family:monospace; font-weight: bold;">
</div>
<div class="ui fluid labeled input" style="margin-bottom:10px;">
<div class="ui label">
ArgLoggerStr
</div>
<input type="text" placeholder="inLogger" class="mGlobal-pyOpenRPA-Debugging-ArgLoggerStr" style="width:100%; font-family:monospace; font-weight: bold;">
</div>
<div class="ui fluid button" onclick="mGlobal.pyOpenRPA.DebuggingExecute();">Execute</div>
</div>
<div class="four wide column">
<h4 class="ui horizontal divider header" >
<i class="bug icon"></i>
Debugging - Output
</h4>
<p><textarea class="mGlobal-pyOpenRPA-Debugging-Output" readonly="readonly" style="width:100%; font-family:monospace; font-weight: bold;" rows="16" cols="60"></textarea></p>
</div>
</div>
<h4 class="ui horizontal divider header">
<i class="clipboard list icon"></i>
Controls
</h4>
<div class="four ui buttons">
<div class="ui animated button openrpa-control-lookmachinescreenshot green UACClient-pyOpenRPADict-AdminDict-ScreenshotViewerBool" onclick="mGlobal.Monitor.ScreenshotModal.Show();" style="display: none; margin-top: 5px;">
<div class="visible content">Show live screenshots</div>
<div class="hidden content">
<i class="right arrow icon"></i>
</div>
</div>
<div class="ui animated button openrpa-control-restartorchestrator orange UACClient-pyOpenRPADict-AdminDict-RestartOrchestratorBool" onclick="mGlobal.Controller.OrchestratorRestart();" style="display: none; margin-top: 5px;">
<div class="visible content">Restart orchestrator</div>
<div class="hidden content">
<i class="right arrow icon"></i>
</div>
</div>
<div class="ui animated button openrpa-control-gitrestartorchestrator teal UACClient-pyOpenRPADict-AdminDict-RestartOrchestratorGITPullBool" onclick="mGlobal.Controller.OrchestratorGITPullRestart();" style="display: none; margin-top: 5px;">
<div class="visible content">Git pull, restart orchestrator</div>
<div class="hidden content">
<i class="right arrow icon"></i>
</div>
</div>
<div class="ui animated button openrpa-control-restartpc red UACClient-pyOpenRPADict-AdminDict-RestartPCBool" onclick="mGlobal.Controller.PCRestart();" style="display: none; margin-top: 5px;">
<div class="visible content">Restart PC</div>
<div class="hidden content">
<i class="right arrow icon"></i>
</div>
</div>
</div>
<div class="row black">
</div>
</div>
<div class="ui inverted vertical footer segment">
<div class="ui center aligned container">
<div class="ui stackable inverted divided grid">
<div class="three wide column">
<h4 class="ui inverted header">Pywinauto</h4>
<div class="ui inverted link list">
<a href="https://pywinauto.readthedocs.io/en/latest/code/pywinauto.findwindows.html" class="item" target="_blank">Specification manual</a>
<a href="https://pywinauto.readthedocs.io/en/latest/code/code.html#main-user-modules" class="item" target="_blank">Classes manual</a>
<a href="https://pywinauto.readthedocs.io/en/latest/code/code.html#main-user-modules" class="item" target="_blank">How to use manual</a>
<a href="https://pywinauto.readthedocs.io/en/latest/code/pywinauto.base_wrapper.html" class="item" target="_blank">Base wrapper manual</a>
</div>
</div>
<div class="three wide column">
<h4 class="ui inverted header">Semantic UI</h4>
<div class="ui inverted link list">
<a href="https://semantic-ui.com/usage/theming.html" class="item" target="_blank">Color manual</a>
<a href="https://semantic-ui.com/elements/input.html" class="item" target="_blank">Input manual</a>
<a href="https://semantic-ui.com/elements/icon.html" class="item" target="_blank">Icon list</a>
<a href="https://semantic-ui.com/elements/button.html" class="item" target="_blank">Button manual</a>
</div>
</div>
<div class="three wide column">
<h4 class="ui inverted header">GitLab</h4>
<div class="ui inverted link list">
<a href="https://gitlab.com/UnicodeLabs/OpenRPA" class="item" target="_blank">pyOpenRPA repository</a>
<a href="https://www.facebook.com/RU.IT4Business" class="item" target="_blank">Ivan Maslov</a>
<a href="#" class="item">Link -</a>
<a href="#" class="item">Link -</a>
</div>
</div>
<div class="seven wide column">
<h4 class="ui inverted header">pyOpenRPA</h4>
<p>Open source Robotic Process Automation software by the pyOpenRPA LLC (Created by Ivan Maslov in Russia). Licensed under LICENSE.PDF or https://pyopenrpa.ru/license/oferta.pdf #IT4Business</p>
</div>
</div>
<div class="ui inverted section divider"></div>
<div class="ui horizontal inverted small divided link list">
<a class="item" href="#">Site Map</a>
<a class="item" href="#">Contact Us</a>
<a class="item" href="#">Terms and Conditions</a>
<a class="item" href="#">Privacy Policy</a>
</div>
</div>
</div>
<div class="ui basic modal">
<div class="ui icon header">
</div>
<div class="content">
<p>Here is the message text!</p>
</div>
<div class="actions">
<div class="ui red basic cancel inverted button">
<i class="remove icon"></i>
No
</div>
<div class="ui green ok inverted button">
<i class="checkmark icon"></i>
Yes
</div>
</div>
</div>
<div class="ui daemon-screenshot modal">
<div class="ui icon header">
</div>
<div class="content">
<img src="GetScreenshot" class="ui fluid image">
</div>
<div class="actions">
<div class="ui green ok inverted button" onclick="mGlobal.Monitor.ScreenshotModal.Close()">
<i class="checkmark icon"></i>
Close
</div>
</div>
</div>
<div class="ui modal openrpa-code-list-gui-import-modal">
<i class="close icon"></i>
<div class="header">
Code list import
</div>
<div class="content">
<div class="description">
<div class="ui header">Insert your JSON specification here.</div>
<p><textarea style="width:100%" rows="6" cols="60"></textarea></p>
</div>
</div>
<div class="actions">
<div class="ui black deny button">
Cancel
</div>
<div class="ui positive right labeled icon button" onclick="mGlobal.CodeList.fActionSpecificationImportFromJSON();">
Parse
<i class="checkmark icon"></i>
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

@ -16,6 +16,7 @@ from . import BackwardCompatibility # Backward compatibility from v1.1.13
from . import Core
from . import Managers
from ..Tools import License
from ..Utils import Dictionary
if CrossOS.IS_WINDOWS_BOOL: from subprocess import CREATE_NEW_CONSOLE
@ -41,6 +42,8 @@ import math
import glob # search the files
import urllib
#Единый глобальный словарь (За основу взять из Settings.py)
gSettingsDict = None
@ -108,7 +111,7 @@ def AgentActivityItemReturnExists(inGUIDStr, inGSettings = None):
return inGUIDStr in inGSettings["AgentActivityReturnDict"]
def AgentActivityItemReturnGet(inGUIDStr, inCheckIntervalSecFloat = 0.5, inGSettings=None):
def AgentActivityItemReturnGet(inGUIDStr, inCheckIntervalSecFloat = 0.5, inGSettings=None, inTimeoutSecFloat=None):
"""L+,W+: Ожидает появления результата по активности (ActivityItem). Возвращает результат выполнения активности.
!ВНИМАНИЕ! Замораживает поток, пока не будет получен результат.
@ -120,15 +123,21 @@ def AgentActivityItemReturnGet(inGUIDStr, inCheckIntervalSecFloat = 0.5, inGSett
:return: Результат выполнения активности. !ВНИМАНИЕ! Возвращаются только то результаты, которые могут быть интерпретированы в JSON формате.
"""
inGSettings = GSettingsGet(inGSettings=inGSettings) # Set the global settings
lTimeStampSecFloat = time.time()
#Check if Orchestrator has been initialized - else raise exception
if Core.IsOrchestratorInitialized(inGSettings=inGSettings) == True:
# Wait while result will not come here
while not AgentActivityItemReturnExists(inGSettings=inGSettings, inGUIDStr=inGUIDStr):
time.sleep(inCheckIntervalSecFloat)
lLoopBool = True
while lLoopBool:
if not AgentActivityItemReturnExists(inGSettings=inGSettings, inGUIDStr=inGUIDStr):
time.sleep(inCheckIntervalSecFloat)
else: lLoopBool=False
if (inTimeoutSecFloat is not None) and (time.time() - lTimeStampSecFloat) >= inTimeoutSecFloat:
raise Exception(f"Orchestrator.AgentActivityItemReturnGet !ВНИМАНИЕ! ПРЕВЫШЕНО ВРЕМЯ ОЖИДАНИЯ ОТВЕТА")
# Return the result
return inGSettings["AgentActivityReturnDict"][inGUIDStr]["Return"]
else:
raise Exception(f"__Orchestrator__.AgentActivityItemReturnGet !ATTENTION! Use this function only after Orchestrator initialization! Before orchestrator init exception will be raised.")
raise Exception(f"Orchestrator.AgentActivityItemReturnGet !ВНИМАНИЕ! ФУНКЦИЯ МОЖЕТ БЫТЬ ЗАПУЩЕНА ТОЛЬКО ПОСЛЕ ИНИЦИАЛИЗАЦИИ!")
def AgentOSCMD(inHostNameStr, inUserStr, inCMDStr, inRunAsyncBool=True, inSendOutputToOrchestratorLogsBool=True, inCMDEncodingStr="cp1251", inGSettings=None, inCaptureBool=True):
"""L+,W+: Отправка команды командной строки на сессию, где работает pyOpenRPA.Agent. Результат выполнения команды можно выводить в лог оркестратора.
@ -533,6 +542,7 @@ def OSCMD(inCMDStr, inRunAsyncBool=True, inLogger = None):
# New call
if inRunAsyncBool:
lThread = threading.Thread(target=_CMDRunAndListenLogs, kwargs={"inCMDStr":inCMDStr, "inLogger":inLogger})
lThread.setName("OSCMD_ACTIVITY")
lThread.start()
lResultStr="Список ActivityList был запущен в асинхронном режиме - захватить содержимое невозможно"
else:
@ -592,6 +602,7 @@ def OrchestratorThreadStart(inDef, *inArgList, **inArgDict):
:return: threading.Thread экземпляр
"""
lDefThread = threading.Thread(target=inDef,args=inArgList,kwargs=inArgDict)
lDefThread.setName(f"ORCHESTRATOR_THREAD_{str(inDef).upper()}")
lDefThread.start()
return lDefThread
@ -638,7 +649,7 @@ def OrchestratorRerunAsAdmin():
else:
print(f"Уже запущено с правами администратора!")
def OrchestratorPySearchInit(inGlobPatternStr, inDefStr = None, inDefArgNameGSettingsStr = None, inAsyncInitBool = False):
def OrchestratorPySearchInit(inGlobPatternStr, inDefStr = None, inDefArgNameGSettingsStr = None, inAsyncInitBool = False, inPackageLevelInt = 0):
"""L+,W+: Выполнить поиск и инициализацию пользовательских .py файлов в Оркестраторе (например панелей управления роботов)
Добавляет инициализированный модуль в пространство sys.modules как imported (имя модуля = имя файла без расширения).
@ -668,20 +679,32 @@ def OrchestratorPySearchInit(inGlobPatternStr, inDefStr = None, inDefArgNameGSet
:param inDefStr: ОПЦИОНАЛЬНО Строковое наименование функции. Преимущественно для обратной совместимости
:param inDefArgNameGSettingsStr: ОПЦИОНАЛЬНО Наименование аргумента, в который требуется передать GSettings (если необходимо)
:param inAsyncInitBool: ОПЦИОНАЛЬНО True - Инициализация py модулей в отдельных параллельных потоках - псевдопараллельное выполнение. False - последовательная инициализация
:param inPackageLevelInt: ОПЦИОНАЛЬНО Уровень вложенности модуля в пакет. По умолчанию 0 (не является модулем пакета)
:return: Сведения об инициализированных модулях в структуре: { "ModuleNameStr":{"PyPathStr": "", "Module": ...}, ...}
"""
inGlobPatternStr = CrossOS.PathStr(inGlobPatternStr)
# # # # # # # #
def __execute__(inResultDict, inPyPathItemStr, inDefStr = None, inDefArgNameGSettingsStr = None):
def __execute__(inResultDict, inPyPathItemStr, inDefStr = None, inDefArgNameGSettingsStr = None, inPackageLevelInt=0):
try:
lPyPathItemStr = inPyPathItemStr
CrossOS.PathSplitList(inPathStr=lPyPathItemStr)[-1*inPackageLevelInt-1:-1]
lModuleNameStr = os.path.basename(lPyPathItemStr)[0:-3]
lTechSpecification = importlib.util.spec_from_file_location(lModuleNameStr, lPyPathItemStr)
lTechModuleFromSpec = importlib.util.module_from_spec(lTechSpecification)
sys.modules[lModuleNameStr] = lTechModuleFromSpec # Add initialized module in sys - python will not init this module enought
lTechSpecificationModuleLoader = lTechSpecification.loader.exec_module(lTechModuleFromSpec)
if inPackageLevelInt==0:
lTechSpecification = importlib.util.spec_from_file_location(lModuleNameStr, lPyPathItemStr)
lTechModuleFromSpec = importlib.util.module_from_spec(lTechSpecification)
sys.modules[lModuleNameStr] = lTechModuleFromSpec # Add initialized module in sys - python will not init this module enought
lTechSpecificationModuleLoader = lTechSpecification.loader.exec_module(lTechModuleFromSpec)
else:
lPrePackagePathStr=CrossOS.PathStr("\\".join(CrossOS.PathSplitList(inPathStr=lPyPathItemStr)[:-1-inPackageLevelInt]))
lPackageNameStr=".".join(CrossOS.PathSplitList(inPathStr=lPyPathItemStr)[-inPackageLevelInt-1:-1])
lModuleNameStr=os.path.basename(lPyPathItemStr)[0:-3]
sys.path.insert(0,lPrePackagePathStr)
lTechSpecification = importlib.import_module(f".{lModuleNameStr}", lPackageNameStr)
lTechModuleFromSpec=sys.modules[f"{lPackageNameStr}.{lModuleNameStr}"]
lModuleNameStr= f"{lPackageNameStr}.{lModuleNameStr}"
lItemDict = {"ModuleNameStr": lModuleNameStr, "PyPathStr": lPyPathItemStr, "Module": lTechModuleFromSpec}
if lL: lL.info(f"Модуль .py {lModuleNameStr} был успешно инициализирован")
if lL: lL.info(f"Модуль .py {lModuleNameStr} был успешно инициализирован. Путь к файлу: {lPyPathItemStr}")
inResultDict[lModuleNameStr]=lItemDict
# Backward compatibility to call def with gsettings when init
if inDefStr is not None and inDefStr != "":
@ -691,7 +714,7 @@ def OrchestratorPySearchInit(inGlobPatternStr, inDefStr = None, inDefArgNameGSet
lArgDict = {inDefArgNameGSettingsStr:GSettingsGet()}
lDef(**lArgDict)
except Exception as e:
if lL: lL.exception(f"Exception when init the .py file {os.path.abspath(lPyPathItemStr)}")
if lL: lL.exception(f"Ошибка при инициализации .py файла: {os.path.abspath(lPyPathItemStr)}")
# # # # # # # #
lResultDict = {}
@ -703,11 +726,12 @@ def OrchestratorPySearchInit(inGlobPatternStr, inDefStr = None, inDefArgNameGSet
# ASYNC EXECUTION
lThreadInit = threading.Thread(target=__execute__,kwargs={
"inResultDict":lResultDict, "inPyPathItemStr": lPyPathItemStr,
"inDefStr": inDefStr, "inDefArgNameGSettingsStr": inDefArgNameGSettingsStr}, daemon=True)
"inDefStr": inDefStr, "inDefArgNameGSettingsStr": inDefArgNameGSettingsStr, "inPackageLevelInt":inPackageLevelInt}, daemon=True)
lThreadInit.setName("PY_SEARCH_MODULE_INIT")
lThreadInit.start()
else:
# SYNC EXECUTION
__execute__(inResultDict=lResultDict, inPyPathItemStr=lPyPathItemStr, inDefStr = inDefStr, inDefArgNameGSettingsStr = inDefArgNameGSettingsStr)
__execute__(inResultDict=lResultDict, inPyPathItemStr=lPyPathItemStr, inDefStr = inDefStr, inDefArgNameGSettingsStr = inDefArgNameGSettingsStr, inPackageLevelInt=inPackageLevelInt)
return lResultDict
def OrchestratorSessionSave(inGSettings=None):
@ -785,7 +809,7 @@ def OrchestratorSessionRestore(inGSettings=None):
inGSettings["StorageDict"] = {}
with open('_SessionLast_StorageDict.pickle', 'rb') as lFile:
lStorageDictDumpDict = pickle.load(lFile)
Server.__ComplexDictMerge2to1Overwrite__(in1Dict=inGSettings["StorageDict"],
Dictionary.MergeNoException(in1Dict=inGSettings["StorageDict"],
in2Dict=lStorageDictDumpDict) # Merge dict 2 into dict 1
if lL: lL.warning(f"Словарь StorageDict был восстановлен из прошлой сессии оркестратора")
os.remove("_SessionLast_StorageDict.pickle") # remove the temp file
@ -797,13 +821,13 @@ def OrchestratorSessionRestore(inGSettings=None):
inGSettings["ManagersProcessDict"] = {}
with open('_SessionLast_GSettings.pickle', 'rb') as lFile:
lStorageDictDumpDict = pickle.load(lFile)
Server.__ComplexDictMerge2to1Overwrite__(in1Dict=inGSettings,
Dictionary.MergeNoException(in1Dict=inGSettings,
in2Dict=lStorageDictDumpDict) # Merge dict 2 into dict 1
if lL: lL.warning(f"Словарь GSettings был восстановлен из прошлой сессии оркестратора")
os.remove("_SessionLast_GSettings.pickle") # remove the temp file
def UACKeyListCheck(inRequest, inRoleKeyList) -> bool:
"""L+,W+: Проверить права доступа для пользователя запроса по списку ключей до права.
"""L+,W+: [ПРЕКРАЩЕНИЕ ПОДДЕРЖКИ В 1.3.2, см. WebUserUACCheck] Проверить права доступа для пользователя запроса по списку ключей до права.
.. code-block:: python
@ -818,8 +842,8 @@ def UACKeyListCheck(inRequest, inRoleKeyList) -> bool:
return inRequest.UACClientCheck(inRoleKeyList=inRoleKeyList)
def UACUserDictGet(inRequest) -> dict:
"""L+,W+: Вернуть UAC (User Access Control) словарь доступов для пользователя, который отправил запрос. Пустой словарь - супердоступ (доступ ко всему)
"""L+,W+: [ПРЕКРАЩЕНИЕ ПОДДЕРЖКИ В 1.3.2, см. WebUserUACHierarchyGet] Вернуть UAC (User Access Control) словарь доступов для пользователя, который отправил запрос. Пустой словарь - супердоступ (доступ ко всему)
:param inRequest: Экземпляр request (from http.server import BaseHTTPRequestHandler)
:return: Словарь доступов пользователя. Пустой словарь - супердоступ (доступ ко всему)
"""
@ -855,7 +879,7 @@ def UACUpdate(inADLoginStr, inADStr="", inADIsDefaultBool=True, inURLList=None,
# Check RoleHierarchyAllowedDict in gSettings for the old role hierarchy - include in result.
if lUserTurple in inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"] and "RoleHierarchyAllowedDict" in inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"][lUserTurple]:
lRoleHierarchyAllowedOLDDict = inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"][lUserTurple]["RoleHierarchyAllowedDict"]
Server.__ComplexDictMerge2to1__(in1Dict=inRoleHierarchyAllowedDict, in2Dict=lRoleHierarchyAllowedOLDDict) # Merge dict 2 into dict 1
Dictionary.Merge(in1Dict=inRoleHierarchyAllowedDict, in2Dict=lRoleHierarchyAllowedOLDDict) # Merge dict 2 into dict 1
# Create Access item
lRuleDomainUserDict = {
@ -916,6 +940,7 @@ def WebURLConnectDef(inMethodStr, inURLStr, inMatchTypeStr, inDef, inContentType
"UACBool": inUACBool
}
inGSettings["ServerDict"]["URLList"].append(lURLItemDict)
Server.BCURLUpdate()
def WebURLConnectFolder(inMethodStr, inURLStr, inMatchTypeStr, inFolderPathStr, inGSettings = None, inUACBool = None, inUseCacheBool= False):
@ -946,6 +971,7 @@ def WebURLConnectFolder(inMethodStr, inURLStr, inMatchTypeStr, inFolderPathStr,
"UseCacheBool": inUseCacheBool
}
inGSettings["ServerDict"]["URLList"].append(lURLItemDict)
Server.BCURLUpdate()
def WebURLConnectFile(inMethodStr, inURLStr, inMatchTypeStr, inFilePathStr, inContentTypeStr=None, inGSettings = None, inUACBool = None, inUseCacheBool = False):
@ -974,12 +1000,13 @@ def WebURLConnectFile(inMethodStr, inURLStr, inMatchTypeStr, inFilePathStr, inCo
"UseCacheBool": inUseCacheBool
}
inGSettings["ServerDict"]["URLList"].append(lURLItemDict)
Server.BCURLUpdate()
def WebListenCreate(inServerKeyStr="Default", inAddressStr="", inPortInt=80, inCertFilePEMPathStr=None, inKeyFilePathStr=None, inGSettings = None):
def WebListenCreate(inServerKeyStr="Default", inAddressStr="0.0.0.0", inPortInt=1024, inCertFilePEMPathStr=None, inKeyFilePathStr=None, inGSettings = None):
"""L+,W+: Настроить веб-сервер Оркестратора.
:param inAddressStr: IP адрес для прослушивания. Если "", то прослушивать запросы со всех сетевых карт. Если "127.0.0.1", то слушать запросы только с той ОС, на которой работает Оркестратор
:param inPortInt: Номер порта для прослушивания. Если HTTP - 80; Если HTTPS - 443. По умолчанию 80. Допускается установка других портов
:param inAddressStr: IP адрес для прослушивания. Если "0.0.0.0", то прослушивать запросы со всех сетевых карт. Если "127.0.0.1", то слушать запросы только с той ОС, на которой работает Оркестратор
:param inPortInt: Номер порта для прослушивания. Если HTTP - 80; Если HTTPS - 443. По умолчанию 1024 (Связано с тем, что в линукс можно устанавливать порты выше 1000). Допускается установка других портов
:param inCertFilePEMPathStr: Путь файлу сертификата, сгенерированного в .pem (base64) формате. Обязателен при использовании защищенного HTTPS/SSL соединения.
:param inKeyFilePathStr: Путь к файлу закрытого ключа в base64 формате
:param inGSettings: Глобальный словарь настроек Оркестратора (синглтон)
@ -1015,8 +1042,18 @@ def WebCPUpdate(inCPKeyStr, inHTMLRenderDef=None, inJSONGeneratorDef=None, inJSI
if inJSInitGeneratorDef is not None: lCPManager.mBackwardCompatibilityJSDef = inJSInitGeneratorDef
def WebAuditMessageCreate(inRequest=None, inOperationCodeStr="-", inMessageStr="-"):
"""L+,W+: Создание сообщения ИТ аудита с такими сведениями как (Домен, IP, логин и тд.). Данная функция особенно актуальна в том случае, если требуется реализовать дополнительные меры контроля ИТ системы.
def WebRequestHostGet(inRequest) -> str:
"""L+,W+: Получить наименование хоста, с которого поступил запрос
:param inRequest: Экземпляр fastapi.Request, по умолчанию None
:type inRequest: fastapi.Request, опционально
:return: Наименование хоста
:rtype: str
"""
return inRequest.client.host
def WebAuditMessageCreate(inAuthTokenStr:str = None, inHostStr:str=None, inOperationCodeStr:str="-", inMessageStr:str="-"):
"""L+,W+: [ИЗМЕНЕНИЕ В 1.3.1] Создание сообщения ИТ аудита с такими сведениями как (Домен, IP, логин и тд.). Данная функция особенно актуальна в том случае, если требуется реализовать дополнительные меры контроля ИТ системы.
.. code-block:: python
@ -1031,47 +1068,44 @@ def WebAuditMessageCreate(inRequest=None, inOperationCodeStr="-", inMessageStr="
# Логгирование сообщения
lLogger.info(lWebAuditMessageStr)
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
:param inAuthTokenStr: Токен авторизации пользователя / бота, по умолчанию None (не установлен)
:type inAuthTokenStr: str, опционально
:param inHostStr: IP адрес хоста пользователя / бота, по умолчанию None (не установлен)
:type inHostStr: str, опционально
:param inOperationCodeStr: Код операции, который принят в компании в соответствии с регламентными процедурами
:param inMessageStr: Дополнительное сообщение, которое необходимо отправить в сообщение об ИТ аудите
:return: Формат сообщения: "WebAudit :: DOMAIN\\USER@101.121.123.12 :: operation code :: message"
"""
try:
if inRequest is None: inRequest = WebRequestGet()
lClientIPStr = inRequest.client_address[0]
lUserDict = WebUserInfoGet(inRequest=inRequest)
lDomainUpperStr = lUserDict["DomainUpperStr"]
lUserLoginStr = lUserDict["UserNameUpperStr"]
lResultStr = f"ВебАудит :: {lDomainUpperStr}\\\\{lUserLoginStr}@{lClientIPStr} :: {inOperationCodeStr} :: {inMessageStr}"
lDomainUpperStr = WebUserDomainGet(inAuthTokenStr=inAuthTokenStr).upper()
lUserLoginStr = WebUserLoginGet(inAuthTokenStr=inAuthTokenStr).upper()
lResultStr = f"ВебАудит :: {lDomainUpperStr}\\\\{lUserLoginStr}@{inHostStr} :: {inOperationCodeStr} :: {inMessageStr}"
except Exception as e:
print(str(e)) # Has no logger - must be dead alg branch
lResultStr = inMessageStr
return lResultStr
def WebRequestParseBodyBytes(inRequest=None):
"""L+,W+: Извлечь данные в байт виде из тела (body) HTTP запроса.
"""L+,W+: [ПРЕКРАЩЕНИЕ ПОДДЕРЖКИ В 1.3.1, см. FASTAPI] Извлечь данные в байт виде из тела (body) HTTP запроса.
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
:return: Строка байт b'' или None (если тело запроса было пустым)
"""
if inRequest is None: inRequest = WebRequestGet()
lBodyBytes=None
if inRequest.headers.get('Content-Length') is not None:
lInputByteArrayLength = int(inRequest.headers.get('Content-Length'))
lBodyBytes = inRequest.rfile.read(lInputByteArrayLength)
return lBodyBytes
return inRequest.body.encode("utf8")
def WebRequestParseBodyStr(inRequest=None):
"""L+,W+: Извлечь данные в виде строки из тела (body) HTTP запроса.
"""L+,W+: [ПРЕКРАЩЕНИЕ ПОДДЕРЖКИ В 1.3.1, см. FASTAPI] Извлечь данные в виде строки из тела (body) HTTP запроса.
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
:return: Текстовая строка '' или None (если тело запроса было пустым)
"""
if inRequest is None: inRequest = WebRequestGet()
return WebRequestParseBodyBytes(inRequest=inRequest).decode('utf-8')
return inRequest.body
def WebRequestParseBodyJSON(inRequest=None):
"""L+,W+: Извлечь из тела (body) запроса HTTP JSON данные и преобразовать в Dict / List структуры языка Python.
"""L+,W+: [ПРЕКРАЩЕНИЕ ПОДДЕРЖКИ В 1.3.1, см. FASTAPI] Извлечь из тела (body) запроса HTTP JSON данные и преобразовать в Dict / List структуры языка Python.
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
:return: Словарь (dict), список (list) или None (если тело запроса было пустым)
@ -1080,7 +1114,7 @@ def WebRequestParseBodyJSON(inRequest=None):
return json.loads(WebRequestParseBodyStr(inRequest=inRequest))
def WebRequestParsePath(inRequest=None):
"""L+,W+: Извлечь декодированный URL путь из HTTP запроса пользователя в формате строки.
"""L+,W+: [ПРЕКРАЩЕНИЕ ПОДДЕРЖКИ В 1.3.1, см. FASTAPI] Извлечь декодированный URL путь из HTTP запроса пользователя в формате строки.
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
:return: str, пример: /pyOpenRPA/Debugging/DefHelper
@ -1089,7 +1123,7 @@ def WebRequestParsePath(inRequest=None):
return urllib.parse.unquote(inRequest.path)
def WebRequestParseFile(inRequest=None):
"""L+,W+: Извлечь файл (наименование + содержимое в виде строки байт b'') из HTTP запроса пользователя.
"""L+,W+: [ПРЕКРАЩЕНИЕ ПОДДЕРЖКИ В 1.3.1, см. FASTAPI] Извлечь файл (наименование + содержимое в виде строки байт b'') из HTTP запроса пользователя.
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
:return: Кортеж формата (FileNameStr, FileBodyBytes) or (None, None), если файл не был обнаружен
@ -1115,7 +1149,7 @@ def WebRequestParseFile(inRequest=None):
return lResultTurple
def WebRequestResponseSend(inResponeStr, inRequest=None, inContentTypeStr: str = None, inHeadersDict: dict = None):
"""L+,W+: Установить ответ на HTTP запрос пользователя.
"""L+,W+: [ПРЕКРАЩЕНИЕ ПОДДЕРЖКИ В 1.3.1, см. FASTAPI] Установить ответ на HTTP запрос пользователя.
:param inResponeStr: Тело ответа (строка)
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
@ -1131,46 +1165,104 @@ def WebRequestResponseSend(inResponeStr, inRequest=None, inContentTypeStr: str =
def WebRequestGet():
"""L+,W+: Вернуть экземпляр HTTP запроса, если функция вызвана в потоке, который был порожден для отработки HTTP запроса пользователя.
"""L+,W+: [ПРЕКРАЩЕНИЕ ПОДДЕРЖКИ В 1.3.2] Вернуть экземпляр HTTP запроса, если функция вызвана в потоке, который был порожден для отработки HTTP запроса пользователя.
"""
lCurrentThread = threading.current_thread()
if hasattr(lCurrentThread, "request"):
return lCurrentThread.request
def WebUserLoginGet(inAuthTokenStr: str=None) -> str:
"""L+,W+: Получить логин авторизованного пользователя. Если авторизация не производилась - вернуть None
:param inAuthTokenStr: Токен авторизации пользователя / бота, по умолчанию None (не установлен)
:type inAuthTokenStr: str, опционально
:return: Логин пользователя
:rtype: str
"""
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)
def WebUserDomainGet(inAuthTokenStr: str=None) -> str:
"""L+,W+: Получить домен авторизованного пользователя. Если авторизация не производилась - вернуть None
:param inAuthTokenStr: Токен авторизации пользователя / бота, по умолчанию None (не установлен)
:type inAuthTokenStr: str, опционально
:return: Домен пользователя
:rtype: str
"""
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(inRequest=None):
"""L+,W+: Информация о пользователе, который отправил HTTP запрос.
"""L+,W+: [ПРЕКРАЩЕНИЕ ПОДДЕРЖКИ В 1.3.2, см. WebUserLoginGet, WebUserDomainGet] Информация о пользователе, который отправил HTTP запрос.
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
:return: Сведения в формате {"DomainUpperStr": "PYOPENRPA", "UserNameUpperStr": "IVAN.MASLOV"}
"""
if inRequest is None: inRequest = WebRequestGet()
lDomainUpperStr = inRequest.OpenRPA["Domain"].upper()
lUserUpperStr = inRequest.OpenRPA["User"].upper()
return {"DomainUpperStr": lDomainUpperStr, "UserNameUpperStr": lUserUpperStr}
try:
lDomainUpperStr = inRequest.OpenRPA["Domain"].upper()
lUserUpperStr = inRequest.OpenRPA["User"].upper()
return {"DomainUpperStr": lDomainUpperStr, "UserNameUpperStr": lUserUpperStr}
except Exception as e:
return {"DomainUpperStr": None, "UserNameUpperStr": None}
def WebUserIsSuperToken(inRequest = None, inGSettings = None):
"""L+,W+: Проверить, авторизован ли HTTP запрос с помощью супер токена (токен, который не истекает).
def WebUserIsSuperToken(inAuthTokenStr: str=None):
"""L+,W+: [ИЗМЕНЕНИЕ В 1.3.1] Проверить, авторизован ли HTTP запрос с помощью супер токена (токен, который не истекает).
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
:param inGSettings: Глобальный словарь настроек Оркестратора (синглтон)
:return: True - является супертокеном; False - не является супертокеном
:param inAuthTokenStr: Токен авторизации пользователя / бота, по умолчанию None (не установлен)
:type inAuthTokenStr: str, опционально
:return: True - является супертокеном; False - не является супертокеном; None - авторизация не производилась
"""
if inRequest is None: inRequest = WebRequestGet()
inGSettings = GSettingsGet(inGSettings=inGSettings) # Set the global settings
if inAuthTokenStr is None: return None
inGSettings = GSettingsGet() # Get the global settings
lIsSuperTokenBool = False
# Get Flag is supertoken (True|False)
lIsSuperTokenBool = inGSettings.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(inRequest.OpenRPA["AuthToken"], {}).get("FlagDoNotExpire", False)
lIsSuperTokenBool = inGSettings.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(inAuthTokenStr, {}).get("FlagDoNotExpire", False)
return lIsSuperTokenBool
def WebUserUACHierarchyGet(inRequest = None):
"""L+,W+: Вернуть словарь доступа UAC в отношении пользователя, который выполнил HTTP запрос inRequest
def WebUserUACHierarchyGet(inAuthTokenStr: str=None) -> dict:
"""L+,W+: [ИЗМЕНЕНИЕ В 1.3.1] Вернуть словарь доступа UAC в отношении пользователя, который выполнил HTTP запрос inRequest
:param inRequest: Экземпляр HTTP request. Опционален, если сообщение фиксируется из под потока, который был инициирован запросом пользователя
:param inAuthTokenStr: Токен авторизации пользователя / бота, по умолчанию None (не установлен)
:type inAuthTokenStr: str, опционально
:return: UAC словарь доступа или {}, что означает полный доступ
"""
if inRequest is None: inRequest = WebRequestGet()
return inRequest.UserRoleHierarchyGet()
lDomainUpperStr = WebUserDomainGet(inAuthTokenStr=inAuthTokenStr).upper()
lUserUpperStr = WebUserLoginGet(inAuthTokenStr=inAuthTokenStr).upper()
if lUserUpperStr is None: return {}
else: return GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("RuleDomainUserDict", {}).get((lDomainUpperStr, lUserUpperStr), {}).get("RoleHierarchyAllowedDict", {})
def WebUserUACCheck(inAuthTokenStr:str=None, inKeyList:list=None) -> bool:
"""L+,W+: Проверить UAC доступ списка ключей для пользователя
:param inAuthTokenStr: Токен авторизации пользователя / бота, по умолчанию None (не установлен)
:type inAuthTokenStr: str, опционально
:return: True - доступ имеется, False - доступа нет
:rtype: bool
"""
if inAuthTokenStr is None: return True # Если авторизации не происходило - супердоступ
lResult = True # Init flag
lRoleHierarchyDict = WebUserUACHierarchyGet(inAuthTokenStr=inAuthTokenStr) # get the Hierarchy
# Try to get value from key list
lKeyValue = lRoleHierarchyDict # Init the base
for lItem in inKeyList:
if type(lKeyValue) is dict:
if lItem in lKeyValue: # Has key
lKeyValue = lKeyValue[lItem] # Get the value and go to the next loop iteration
else: # Else branch - true or false
if len(lKeyValue)>0: # False - if Dict has some elements
lResult = False # Set the False Flag
else:
lResult = True # Set the True flag
break # Stop the loop
else: # Has element with no detalization - return True
lResult = True # Set the flag
break # Close the loop
return lResult # Return the result
## GSettings defs
@ -1193,7 +1285,7 @@ def GSettingsGet(inGSettings=None):
global GSettings # identify the global variable
# Merge dictionaries if some new dictionary has come
if inGSettings is not None and GSettings is not inGSettings:
GSettings = Server.__ComplexDictMerge2to1Overwrite__(in1Dict = inGSettings, in2Dict = GSettings)
GSettings = Dictionary.MergeNoException(in1Dict = inGSettings, in2Dict = GSettings)
return GSettings # Return the result
def GSettingsKeyListValueSet(inValue, inKeyList=None, inGSettings = None):
@ -1962,6 +2054,7 @@ def ProcessDefIntervalCall(inDef, inIntervalSecFloat, inIntervalAsyncBool=False,
lThread2 = threading.Thread(target=inDef,
args=inDefArgList,
kwargs=inDefArgDict)
lThread2.setName("INTERVAL_CALL_DEF")
lThread2.start()
except Exception as e:
if inLogger: inLogger.exception(
@ -1976,6 +2069,7 @@ def ProcessDefIntervalCall(inDef, inIntervalSecFloat, inIntervalAsyncBool=False,
"inIntervalAsyncBool": inIntervalAsyncBool, "inDefArgList": inDefArgList,
"inDefArgDict": inDefArgDict, "inLogger": inLogger,
"inDefArgGSettingsNameStr":inDefArgGSettingsNameStr , "inDefArgLoggerNameStr":inDefArgLoggerNameStr})
lThread.setName("INTERVAL_CALL_EXECUTOR")
lThread.start()
else:
__Execute__(inGSettings=inGSettings, inDef=inDef, inIntervalSecFloat=inIntervalSecFloat, inIntervalAsyncBool=inIntervalAsyncBool,
@ -2710,8 +2804,6 @@ def Orchestrator(inGSettings=None, inDumpRestoreBool = True, inRunAsAdministrato
Processor.gSettingsDict = gSettingsDict
Timer.gSettingsDict = gSettingsDict
Timer.Processor.gSettingsDict = gSettingsDict
Server.gSettingsDict = gSettingsDict
Server.ProcessorOld.gSettingsDict = gSettingsDict # Backward compatibility
#Backward compatibility - restore in Orc def if old def
if inDumpRestoreBool == True:
@ -2732,7 +2824,7 @@ def Orchestrator(inGSettings=None, inDumpRestoreBool = True, inRunAsAdministrato
# Run SettingUpdate function in submodule
getattr(lTechModuleFromSpec, lSubmoduleFunctionName)(gSettingsDict)
except Exception as e:
if lL: lL.exception(f"Error when init .py file in orchestrator '{lModuleFilePathItem}'. Exception is below:")
if lL: lL.exception(f"Ошибка при инициализации .py файлов в оркестраторе '{lModuleFilePathItem}'")
# Turn on backward compatibility
BackwardCompatibility.Update(inGSettings= gSettingsDict)
@ -2752,18 +2844,10 @@ def Orchestrator(inGSettings=None, inDumpRestoreBool = True, inRunAsAdministrato
#Инициализация настроечных параметров
gSettingsDict["ServerDict"]["WorkingDirectoryPathStr"] = os.getcwd() # Set working directory in g settings
#Инициализация сервера (инициализация всех интерфейсов)
lListenDict = gSettingsDict.get("ServerDict",{}).get("ListenDict",{})
for lItemKeyStr in lListenDict:
lItemDict = lListenDict[lItemKeyStr]
lThreadServer = Server.RobotDaemonServer(lItemKeyStr, gSettingsDict)
lThreadServer.start()
gSettingsDict["ServerDict"]["ServerThread"] = lThreadServer
lItemDict["ServerInstance"] = lThreadServer
# Init the RobotScreenActive in another thread
lRobotScreenActiveThread = threading.Thread(target= Monitor.CheckScreen)
lRobotScreenActiveThread.daemon = True # Run the thread in daemon mode.
lRobotScreenActiveThread.setName("SCREEN_ACTIVE")
lRobotScreenActiveThread.start() # Start the thread execution.
if lL: lL.info("Модуль активного рабочего стола инициализирован") #Logging
@ -2771,12 +2855,14 @@ def Orchestrator(inGSettings=None, inDumpRestoreBool = True, inRunAsAdministrato
lRobotRDPThreadControlDict = {"ThreadExecuteBool":True} # inThreadControlDict = {"ThreadExecuteBool":True}
lRobotRDPActiveThread = threading.Thread(target= RobotRDPActive.RobotRDPActive, kwargs={"inGSettings":gSettingsDict, "inThreadControlDict":lRobotRDPThreadControlDict})
lRobotRDPActiveThread.daemon = True # Run the thread in daemon mode.
lRobotRDPActiveThread.setName("RDP_CONNECT")
lRobotRDPActiveThread.start() # Start the thread execution.
if lL: lL.info("Модуль подключения по РДП инициализированн") #Logging
# Init autocleaner in another thread
lAutocleanerThread = threading.Thread(target= GSettingsAutocleaner, kwargs={"inGSettings":gSettingsDict})
lAutocleanerThread.daemon = True # Run the thread in daemon mode.
lAutocleanerThread.setName("AUTOCLEANER")
lAutocleanerThread.start() # Start the thread execution.
if lL: lL.info("Модуль автоочистки инициализирован") #Logging
@ -2791,24 +2877,28 @@ def Orchestrator(inGSettings=None, inDumpRestoreBool = True, inRunAsAdministrato
# Processor thread
lProcessorThread = threading.Thread(target= Processor.ProcessorRunSync, kwargs={"inGSettings":gSettingsDict, "inRobotRDPThreadControlDict":lRobotRDPThreadControlDict})
lProcessorThread.daemon = True # Run the thread in daemon mode.
lProcessorThread.setName("PROCESSOR")
lProcessorThread.start() # Start the thread execution.
if lL: lL.info("Модуль процессора инициализирован") #Logging
# Processor monitor thread
lProcessorMonitorThread = threading.Thread(target= Processor.ProcessorMonitorRunSync, kwargs={"inGSettings":gSettingsDict})
lProcessorMonitorThread.daemon = True # Run the thread in daemon mode.
lProcessorMonitorThread.setName("PROCESSOR_MONITOR")
lProcessorMonitorThread.start() # Start the thread execution.
if lL: lL.info("Модуль контроля процессора инициализирован") #Logging
# Scheduler loop
lSchedulerThread = threading.Thread(target= __deprecated_orchestrator_loop__)
lSchedulerThread.daemon = True # Run the thread in daemon mode.
lSchedulerThread.setName("SCHEDULER_OLD")
lSchedulerThread.start() # Start the thread execution.
if lL: lL.info("Модуль расписания (старая версия) инициализирован") #Logging
# Schedule (new) loop
lScheduleThread = threading.Thread(target= __schedule_loop__)
lScheduleThread.daemon = True # Run the thread in daemon mode.
lScheduleThread.setName("SCHEDULER_NEW")
lScheduleThread.start() # Start the thread execution.
if lL: lL.info("Модуль расписания (новая версия) инициализирован") #Logging
@ -2817,11 +2907,22 @@ def Orchestrator(inGSettings=None, inDumpRestoreBool = True, inRunAsAdministrato
lProcess = inGSettings["ManagersProcessDict"][lProcessKeyTuple]
lProcess.StatusCheckIntervalRestore()
lThread = threading.Thread(target= lProcess.StatusRestore)
lThread.setName("MANAGER_PROCESS_RESTORE")
lThread.start()
# Init debug thread (run if "init_dubug" file exists)
Debugger.LiveDebugCheckThread(inGSettings=GSettingsGet())
#Инициализация сервера (инициализация всех интерфейсов)
Server.InitFastAPI()
lListenDict = gSettingsDict.get("ServerDict",{}).get("ListenDict",{})
for lItemKeyStr in lListenDict:
lItemDict = lListenDict[lItemKeyStr]
lItemDict["ServerInstance"]=Server.app
Server.InitUvicorn(inHostStr=lItemDict["AddressStr"], inPortInt=lItemDict["PortInt"], inSSLCertPathStr=lItemDict["CertFilePEMPathStr"], inSSLKeyPathStr=lItemDict["KeyFilePathStr"], inSSLPasswordStr=None)
def __schedule_loop__():
while True:
schedule.run_pending()
@ -2889,6 +2990,7 @@ def __deprecated_orchestrator_loop__():
lThread = threading.Thread(target=Processor.ActivityListExecute,
kwargs={"inGSettings": inGSettings,
"inActivityList": lItem["ActivityList"]})
lThread.setName("SCHEDULER_OLD_ACTIVITY")
lThread.start()
lIterationLastDateTime = datetime.datetime.now() # Set the new datetime for the new processor activity
except Exception as e:

@ -8,7 +8,7 @@
* http://opensource.org/licenses/MIT
*
*/
@import url(/3rdParty/Google/LatoItalic.css);
@import url(/orpa/resources/Web/Google/LatoItalic.css);
/*@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic&subset=latin);*//*!
* # Semantic UI 2.4.0 - Reset
* http://github.com/semantic-org/semantic-ui/

@ -14,6 +14,7 @@
<div class="four wide column">
<h4 class="ui inverted header">Интересный факт</h4>
<p><a href="https://pyopenrpa.ru" target="_blank">Портал pyOpenRPA</a> сделан на оркестраторе pyOpenRPA за 7 дней - присоединяйся к сильной стороне!</p>
<h4 class="ui inverted header">pyOpenRPA - роботы помогут!</h4>
</div>
<div class="eight wide column">
<h4 class="ui inverted header">Контактная информация</h4>
@ -30,6 +31,8 @@
</div>
<div class="ui inverted section divider"></div>
<div class="ui horizontal inverted small divided link list">
<a class="item" href="/orpa/fastapi/docs" target="_blank">Документация API (SWAGGER)</a>
<a class="item" href="/orpa/fastapi/redoc" target="_blank">Документация API (REDOC)</a>
<a class="item" href="https://semantic-ui.com/elements/input.html" target="_blank">Справка Semantic UI</a>
<a class="item" href="https://pywinauto.readthedocs.io/en/latest/code/code.html#main-user-modules" target="_blank">Справка Pywinauto</a>
<a class="item" href="https://gitlab.com/UnicodeLabs/OpenRPA" target="_blank">Репозиторий pyOpenRPA</a>
@ -39,11 +42,11 @@
</div>
<script src="/orpa/Resources/Web/orpa/styleset/visibility.js"></script>
<script src="/orpa/Resources/Web/orpa/styleset/sidebar.js"></script>
<script src="/orpa/Resources/Web/orpa/styleset/transition.js"></script>
<script src="/orpa/Resources/Web/orpa/styleset/docs.js"></script>
<script src="/orpa/Resources/Web/orpa/styleset/easing.min.js"></script>
<script src="/orpa/Resources/Web/orpa/styleset/highlight.min.js"></script>
<script src="/orpa/Resources/Web/orpa/styleset/less.min.js"></script>
<script src="/orpa/Resources/Web/orpa/styleset/home.js"></script>
<script src="/orpa/resources/Web/orpa/styleset/visibility.js"></script>
<script src="/orpa/resources/Web/orpa/styleset/sidebar.js"></script>
<script src="/orpa/resources/Web/orpa/styleset/transition.js"></script>
<script src="/orpa/resources/Web/orpa/styleset/docs.js"></script>
<script src="/orpa/resources/Web/orpa/styleset/easing.min.js"></script>
<script src="/orpa/resources/Web/orpa/styleset/highlight.min.js"></script>
<script src="/orpa/resources/Web/orpa/styleset/less.min.js"></script>
<script src="/orpa/resources/Web/orpa/styleset/home.js"></script>

@ -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">
@ -82,12 +82,13 @@
<div class="ui middle aligned stackable grid container">
<div class="row">
<div class="four wide column">
<img src="/orpa/Resources/Web/orpa/styleset/pyOpenRPA_logo.png" width="140px;" height="140px"></img>
<img src="/orpa/resources/Web/orpa/logo.png" width="140px;" height="140px"></img>
</div>
<div class="twelve wide column">
<h1 class="ui inverted header" style="margin-top:0px; margin-bottom:0px;">
<span class="library" style="text-transform: uppercase; letter-spacing: 5px; margin-top:0px; margin-bottom:5px; font-weight: bold;">{{title}}</span>
</h1>
<div class="floating ui teal label">{{version}}</div>
</h1>
<h2 style="text-transform: uppercase; letter-spacing: 5px; margin-top:0px; margin-bottom:5px;">
{{subtitle}}

@ -101,22 +101,6 @@ $(document).ready(function() {
/////Controller JS module
//////////////////////////
mGlobal.Controller={};
mGlobal.Controller.CMDRunText=function(inCMDText) {
///Подготовить конфигурацию
lData = [
{"Type":"CMDStart", "Command": inCMDText}
]
///Обнулить таблицу
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3){},
dataType: "text"
});
}
mGlobal.Controller.CMDRun=function() {
///Обнулить таблицу
lCMDCode=$(".openrpa-controller-cmd-run-input")[0].value
@ -132,37 +116,7 @@ $(document).ready(function() {
]
$.ajax({
type: "POST",
url: '/pyOpenRPA/ActivityListExecute',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///Отправить запрос на формирование таблицы
//lHTMLCode=console.log("CMDRun result: "+lResponseJSON[0].result)
},
dataType: "text"
});
}
mGlobal.Controller.CMDRunGUILogout=function() {
///Обнулить таблицу
lCMDCode="for /f \"skip=1 tokens=2\" %s in ('query user %USERNAME%') do (tscon \\dest:console)"
//lCMDCode = lCMDCode.replace(/\\n/g, "\\n")
// .replace(/\\'/g, "\\'")
// .replace(/\\"/g, '\\"')
// .replace(/\\&/g, "\\&")
// .replace(/\\r/g, "\\r")
// .replace(/\\t/g, "\\t")
// .replace(/\\b/g, "\\b")
// .replace(/\\f/g, "\\f")
// .replace('"', "\\\"");
///Подготовить конфигурацию
lData = [
{"Type":"CMDStart", "Command": lCMDCode }
]
$.ajax({
type: "POST",
url: 'Utils/Processor',
url: '/orpa/api/activity-list-execute',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
@ -174,51 +128,33 @@ $(document).ready(function() {
dataType: "text"
});
}
///Restart PC
mGlobal.Controller.PCRestart = function () {
mGlobal.Controller.OrchestratorSessionSave()
mGlobal.Controller.CMDRunText("shutdown -r")
}
///Orchestrator save session
mGlobal.Controller.OrchestratorSessionSave=function() {
///Подготовить конфигурацию
lData = [
{"Type":"OrchestratorSessionSave"}
]
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
},
dataType: "text"
});
mGlobal.Controller.OrchestratorGITPullRestart = function() {
mGlobal.Controller.OrchestratorSessionSave() //Save current RDP list session
mGlobal.Controller.CMDRunText("timeout 3 & taskkill /f /im OpenRPA_Orchestrator.exe & timeout 2 & cd "+mGlobal.WorkingDirectoryPathStr+" & git reset --hard & git pull & pyOpenRPA.Orchestrator_x64_administrator_startup.cmd");
}
///Перезагрузить Orchestrator
mGlobal.Controller.OrchestratorRestart=function() {
///Подготовить конфигурацию
lData = [
{"Type":"OrchestratorRestart"}
{
"Def":"OrchestratorRestart", // def link or def alias (look gSettings["Processor"]["AliasDefDict"])
"ArgList":[], // Args list
"ArgDict":{}, // Args dictionary
"ArgGSettings": null, // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
"ArgLogger": null // Name of GSettings attribute: str (ArgDict) or index (for ArgList)
}
]
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
},
dataType: "text"
type: "POST",
url: '/orpa/api/activity-list-execute',
data: JSON.stringify(lData)
});
}
mGlobal.Controller.OrchestratorGITPullRestart = function() {
mGlobal.Controller.OrchestratorSessionSave() //Save current RDP list session
mGlobal.Controller.CMDRunText("timeout 3 & taskkill /f /im OpenRPA_Orchestrator.exe & timeout 2 & cd "+mGlobal.WorkingDirectoryPathStr+" & git reset --hard & git pull & pyOpenRPA.Orchestrator_x64_administrator_startup.cmd");
}
//////////////////////////
/////Monitor JS module
//////////////////////////
@ -228,12 +164,12 @@ $(document).ready(function() {
return inPrefix+Math.round(Math.random()*1000)+"-"+Math.round(Math.random()*10000)+"-"+Math.round(Math.random()*1000)
}
//inHostURI: http://localhost:8081
mGlobal.Monitor.ScreenshotModal.Show=function(inHostURI=" ") {
mGlobal.Monitor.ScreenshotModal.Show=function(inHostURI="") {
$('.ui.modal.daemon-screenshot').modal({'onHide':function (inElement) {mGlobal.Monitor.ScreenshotModal.Close();} }).modal('show');
//Функция обновления картинки
lScreenshotUpdate=function() {
lScreenshotSrc=inHostURI+"/GetScreenshot?"+mGlobal.Monitor.GenerateUniqueID()
lScreenshotSrc=inHostURI+"/orpa/client/screenshot-get?"+mGlobal.Monitor.GenerateUniqueID()
$(".daemon-screenshot img").attr('src', lScreenshotSrc);
}
@ -244,26 +180,7 @@ $(document).ready(function() {
}
///Monitor
mGlobal.Monitor.DaemonList={}
mGlobal.Monitor.DaemonList.fRefreshTable=function() {
///Загрузка данных
$.ajax({
type: "GET",
url: 'Monitor/JSONDaemonListGet',
data: '',
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///Сформировать HTML код новой таблицы
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-hidden-monitor-table-general",lResponseJSON)
///Очистить дерево
//mGlobal.ElementTree.fClear();
///Прогрузить новую таблицу
$(".openrpa-monitor").html(lHTMLCode)
},
dataType: "text"
});
}
////////////////////////////////
///////Control panel
///////////////////////////////
@ -380,7 +297,7 @@ $(document).ready(function() {
lData = [inActivityItem]
$.ajax({
type: "POST",
url: '/pyOpenRPA/ActivityListExecute',
url: '/orpa/api/activity-list-execute',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
@ -405,7 +322,7 @@ $(document).ready(function() {
lData = inActivityList
$.ajax({
type: "POST",
url: '/pyOpenRPA/ActivityListExecute',
url: '/orpa/api/activity-list-execute',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
@ -430,7 +347,7 @@ $(document).ready(function() {
lData = inActivityList
$.ajax({
type: "POST",
url: '/pyOpenRPA/ProcessorQueueAdd',
url: '/orpa/api/processor-queue-add',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
@ -448,7 +365,7 @@ $(document).ready(function() {
$.ajax({
type: "GET",
headers: {},
url: 'pyOpenRPA/ServerJSInit',
url: '/orpa/client/server-js-init',
data: mGlobal.pyOpenRPA.ServerDataHashStr,
async: false,
success: function(lJSText) {
@ -512,7 +429,7 @@ $(document).ready(function() {
$.ajax({
type: "POST",
headers: {},
url: 'pyOpenRPA/ServerData',
url: '/orpa/client/server-data',
data: mGlobal.pyOpenRPA.ServerDataHashStr,
success: function(lData,l2,l3) {
try {
@ -587,7 +504,7 @@ $(document).ready(function() {
$.ajax({
type: "POST",
headers: {},
url: 'pyOpenRPA/ServerLog',
url: '/orpa/client/server-log',
data: mGlobal.pyOpenRPA.ServerLogListHashStr,
success: function(lData,l2,l3) {
try {
@ -624,90 +541,7 @@ $(document).ready(function() {
///Processor functions
///////////////////////////////
mGlobal.Processor = {}
mGlobal.Processor.ServerValueAppend = function(inKeyList,inValue) {
lData = [
{
"Type":"GlobalDictKeyListValueAppend",
"KeyList": inKeyList,
"Value": inValue
}
]
///Обнулить таблицу
$('.ui.modal.basic .content').html("");
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///TODO Show error if exist error
},
dataType: "text"
});
}
mGlobal.Processor.ServerValueSet = function(inKeyList,inValue) {
lData = [
{
"Type":"GlobalDictKeyListValueSet",
"KeyList": inKeyList,
"Value": inValue
}
]
///Обнулить таблицу
$('.ui.modal.basic .content').html("");
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///TODO Show error if exist error
},
dataType: "text"
});
}
mGlobal.Processor.ServerValueOperatorPlus = function(inKeyList,inValue) {
lData = [
{
"Type":"GlobalDictKeyListValueOperator+",
"KeyList": inKeyList,
"Value": inValue
}
]
///Обнулить таблицу
$('.ui.modal.basic .content').html("");
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///TODO Show error if exist error
},
dataType: "text"
});
}
mGlobal.Processor.Send = function(inData) {
lData = inData
$.ajax({
type: "POST",
url: 'Utils/Processor',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
{
var lResponseJSON=JSON.parse(lData)
///TODO Show error if exist error
},
dataType: "text"
});
}
mGlobal.Server= {}
mGlobal.Server.JSONGet=function(inMethod, inURL, inDataJSON, inCallback)
{
@ -904,7 +738,7 @@ $(document).ready(function() {
lValueStr = inEvent
$.ajax({
type: "GET",
url: '/pyOpenRPA/Debugging/HelperDefAutofill/'+lValueStr,
url: '/orpa/api/helper-def-autofill/'+lValueStr,
data: null,
success:
function(lData,l2,l3)
@ -957,7 +791,7 @@ $(document).ready(function() {
lData = [lActivityItem]
$.ajax({
type: "POST",
url: '/pyOpenRPA/ActivityListExecute',
url: '/orpa/api/activity-list-execute',
data: JSON.stringify(lData),
success:
function(lData,l2,l3)
@ -984,9 +818,15 @@ $(document).ready(function() {
.dropdown({
apiSettings: {
// this url parses query server side and returns filtered results
url: '/pyOpenRPA/Debugging/HelperDefList/{query}'
url: '/orpa/api/helper-def-list/{query}'
},
onChange: lDropdownOnChange
})
;
mGlobal.pyOpenRPA.RDPListTest= function() {
lResponseJSON={"HandlebarsList":[{"SessionKeyStr":"test"},{"SessionKeyStr":"test2"}]};
/// !RDP List ! Сформировать HTML код новой таблицы - список RDP
lHTMLCode=mGlobal.GeneralGenerateHTMLCodeHandlebars(".openrpa-hidden-robotrdpactive-control-panel",lResponseJSON)
$(".openrpa-robotrdpactive-control-panel").html(lHTMLCode)
}
});

@ -22,16 +22,16 @@
<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">
<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"
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>
<script src = "/orpa/Resources/Web/orpa/orc.js"></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>
<script src = "/orpa/resources/Web/orpa/orc.js"></script>
<style type="text/css">
body {
@ -69,13 +69,12 @@
}
.ui.search.dropdown>input.search {
width:100%;
font-family:monospace;
font-family:'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
font-weight: bold;
}
.ui.search.dropdown>.text {
width:100%;
font-family:monospace;
font-weight: bold;
font-family:'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
}
</style>
</head>
@ -234,27 +233,28 @@
<div class="ui inverted relaxed divided list">
{{#HandlebarsList}}
<div class="item">
<div class="content">
<div class="header">Session key: {{{SessionKeyStr}}}</div>
{{{SessionHexStr}}}
</div>
<div class="right floated content">
<div class="ui button" onclick="mGlobal.Processor.ServerValueAppend(['RobotRDPActive','ActivityList'],{'DefNameStr': 'RDPSessionReconnect', 'ArgList': [], 'ArgDict': {'inRDPSessionKeyStr': '{{{SessionKeyStr}}}'} })" >Переподключить</div>
<div style="margin-top:10px;" class="ui button" onclick="mGlobal.Processor.ServerValueAppend(['RobotRDPActive','ActivityList'],{'DefNameStr': 'RDPSessionReconnect', 'ArgList': [], 'ArgDict': {'inRDPSessionKeyStr': '{{{SessionKeyStr}}}'} })" >Переподключить</div>
</div>
<div class="right floated content">
{{#if IsIgnoredBool}}
<div class="ui button red" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','RDPList','{{{SessionKeyStr}}}','SessionIsIgnoredBool'],false);">Игнорировать</div>
<div style="margin-top:10px;" class="ui button red" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','RDPList','{{{SessionKeyStr}}}','SessionIsIgnoredBool'],false);">Игнорировать</div>
{{else}}
<div class="ui button" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','RDPList','{{{SessionKeyStr}}}','SessionIsIgnoredBool'],true);">Игнорировать</div>
<div style="margin-top:10px;" class="ui button" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','RDPList','{{{SessionKeyStr}}}','SessionIsIgnoredBool'],true);">Игнорировать</div>
{{/if}}
</div>
<div class="right floated content">
{{#if IsFullScreenBool}}
<div class="ui button green" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','FullScreenRDPSessionKeyStr'],null);">Полный экран</div>
<div style="margin-top:10px;" class="ui button green" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','FullScreenRDPSessionKeyStr'],null);">Полный экран</div>
{{else}}
<div class="ui button" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','FullScreenRDPSessionKeyStr'],'{{{SessionKeyStr}}}');">Полный экран</div>
<div style="margin-top:10px;" class="ui button" onclick="mGlobal.Processor.ServerValueSet(['RobotRDPActive','FullScreenRDPSessionKeyStr'],'{{{SessionKeyStr}}}');">Полный экран</div>
{{/if}}
</div>
<div class="content">
<div class="header">Session key: {{{SessionKeyStr}}}</div>
{{{SessionHexStr}}}
</div>
</div>
{{/HandlebarsList}}
</div>
@ -423,13 +423,13 @@
<i class="right arrow icon"></i>
</div>
</div>
<div class="ui animated button openrpa-control-gitrestartorchestrator teal UACClient-pyOpenRPADict-AdminDict-RestartOrchestratorGITPullBool" onclick="mGlobal.Controller.OrchestratorGITPullRestart();" style="display: none; margin-top: 5px;">
<div class="ui animated button openrpa-control-gitrestartorchestrator teal UACClient-pyOpenRPADict-AdminDict-RestartOrchestratorGITPullBool disabled" onclick="mGlobal.Controller.OrchestratorGITPullRestart();" style="display: none; margin-top: 5px;">
<div class="visible content">Обновить из GIT</div>
<div class="hidden content">
<i class="right arrow icon"></i>
</div>
</div>
<div class="ui animated button openrpa-control-restartpc red UACClient-pyOpenRPADict-AdminDict-RestartPCBool" onclick="mGlobal.Controller.PCRestart();" style="display: none; margin-top: 5px;">
<div class="ui animated button openrpa-control-restartpc red UACClient-pyOpenRPADict-AdminDict-RestartPCBool disabled" onclick="mGlobal.Controller.PCRestart();" style="display: none; margin-top: 5px;">
<div class="visible content">Перезагрузить ОС</div>
<div class="hidden content">
<i class="right arrow icon"></i>
@ -462,7 +462,7 @@
<div class="ui icon header">
</div>
<div class="content">
<img src="GetScreenshot" class="ui fluid image">
<img src="/orpa/client/screenshot-get" class="ui fluid image">
</div>
<div class="actions">
<div class="ui green ok inverted button" onclick="mGlobal.Monitor.ScreenshotModal.Close()">

@ -2,12 +2,15 @@
<html lang="en" >
<head>
<meta charset="utf-8">
<title>pyOpenRPA - studio</title>
<link rel="stylesheet" type="text/css" href="3rdParty/Semantic-UI-CSS-master/semantic.min.css">
<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">
<script
src="3rdParty/jQuery/jquery-3.1.1.min.js"
src="/3rdParty/jQuery/jquery-3.1.1.min.js"
crossorigin="anonymous"></script>
<script src="3rdParty/Semantic-UI-CSS-master/semantic.min.js"></script>
<script src="/3rdParty/Semantic-UI-CSS-master/semantic.min.js"></script>
<script>
// Production steps of ECMA-262, Edition 6, 22.1.2.1
if (!Array.from) {
@ -267,7 +270,7 @@
mGlobal.ElementTree.fRender = function(inElementsTreeDataArray,inBackendString)
{
var lHTMLList=
'<b style="font-size:10px;" >Backend: '+inBackendString+'</b>\
'<b style="font-size:10px;" >Протокол: '+inBackendString+'</b>\
<div class="ui list" style="height:'+mGlobal.Settings.mUIOTreeHeight+'px;overflow:scroll; margin-top:0px;">';
///Циклический обход списка
for (var i = 0; i< inElementsTreeDataArray.length;i++) {
@ -327,9 +330,9 @@
<div class="content">\
<div class="header" '+lIconSelectOnClick+'>'+inItem.title+'</div>\
<div class="description" '+lIconSelectOnClick+'>process_id:'+inItem.process_id+'; handle:'+inItem.handle+'; class_name: '+inItem.class_name+'; RECT:L'+inItem.rectangle.left+' T'+inItem.rectangle.top+' R'+inItem.rectangle.right+' B'+inItem.rectangle.bottom+'</div>\
<a class="ui tag label teal mini" '+lSubItemActionOnRightClick+'>Highlight</a>\
<a class="ui tag label teal mini" '+lSubItemActionOnClick+'>Expand</a>\
<a class="ui tag label teal mini" '+lIconUpOnClick+'>Mouse search</a>\
<a class="ui tag label teal mini" '+lSubItemActionOnRightClick+'>Подсветить UIO</a>\
<a class="ui tag label teal mini" '+lSubItemActionOnClick+'>Развернуть</a>\
<a class="ui tag label teal mini" '+lIconUpOnClick+'>Поиск мыши</a>\
'+lSubListHTML+'\
</div>\
</div>'
@ -370,9 +373,9 @@
<div class="content">\
<div class="header" '+lIconSelectOnClick+'>'+lTitle+'</div>\
<div class="description" '+lIconSelectOnClick+'>process_id:'+lResponseJSON.Result[i].process_id+'; handle:'+lSubItemHandleId+'; class_name: '+lResponseJSON.Result[i].class_name+'; RECT:L'+lResponseJSON.Result[i].rectangle.left+' T'+lResponseJSON.Result[i].rectangle.top+' R'+lResponseJSON.Result[i].rectangle.right+' B'+lResponseJSON.Result[i].rectangle.bottom+'</div>\
<a class="ui tag label teal mini" '+lSubItemActionOnRightClick+'>Highlight</a>\
<a class="ui tag label teal mini" '+lSubItemActionOnClick+'>Expand</a>\
<a class="ui tag label teal mini" '+lIconUpOnClick+'>Mouse search</a>\
<a class="ui tag label teal mini" '+lSubItemActionOnRightClick+'>Подсветить UIO</a>\
<a class="ui tag label teal mini" '+lSubItemActionOnClick+'>Развернуть</a>\
<a class="ui tag label teal mini" '+lIconUpOnClick+'>Поиск мыши</a>\
</div>\
</div>'
//Добавить информацию об элементе в словарь JS
@ -395,7 +398,7 @@
mGlobal.TreeObjectInfoLoad =function (inElementId) {
//Подгрузка массива спецификаций
lSpecificationArray = mGlobal.GUIElement[inElementId].GUISelectorFull
var lHTMLList='<div class="ui relaxed divided list" style="height:'+mGlobal.Settings.mUIOTreeHeight+'px;overflow:scroll;">'
var lHTMLList='<div class="ui relaxed divided list" style="height:'+mGlobal.Settings.mUIOTreeHeight+'px;overflow:scroll;margin-top: 22px;">'
var lSpecificationArrayNew=[]
for (i=0;i<lSpecificationArray.length;i++) {
lSpecificationArrayNew.push(lSpecificationArray[i])
@ -448,7 +451,7 @@
success:
function(lData,l2,l3)
{
var lHTMLList='<div class="ui relaxed divided list" style="height:'+mGlobal.Settings.mUIOTreeHeight+'px;overflow:scroll;">'
var lHTMLList='<div class="ui relaxed divided list" style="height:'+mGlobal.Settings.mUIOTreeHeight+'px;overflow:scroll; margin-top: 22px;">'
var lResponseJSON = JSON.parse(lData)
///Ошибка
if (lResponseJSON["ErrorFlag"]) {
@ -854,171 +857,91 @@
</style>
</head>
<body>
<div class="ui internally celled grid">
<div class="row black">
<div class="sixteen wide column" style="display: flex;">
<img src="pyOpenRPA_logo.png" width="70px;" height="70px"></img>
&nbsp;&nbsp;&nbsp;
<h1 class="ui header inverted" style="cursor: pointer" onclick="window.open('https://gitlab.com/UnicodeLabs/OpenRPA','_blank');">pyOpenRPA</h1>
<h5 style="cursor: pointer" onclick="window.open('https://www.facebook.com/RU.IT4Business','_blank');">by Ivan Maslov</h5>
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;
<h1 class="ui header inverted">Studio Web GUI</h1>
{% include 'header.xhtml' %}
</div>
</div>
<div class="row">
<div class="six wide column" >
<div class="ui input">
<input class="openrpa-value-backend" type="text" placeholder="Backend win32 | uia" value="win32">
</div>
<button class="ui labeled icon button red" onclick="mGlobal.Actions.fRestartStudioServer();">
<i class="icon redo alternate"></i>
Restart Studio
</button>
<b style="font-size:10px;color: #b1b1b1;" >Backend: win32 or uia</b>
<div class="ui aligned stackable grid container">
<h2 class="ui header" style="margin-top: 60px;">
<i class="search icon"></i>
<div class="content">
Шаг 1. Обнаружить UIO
</div>
</div>
</h2>
<div class="row">
<div class="six wide column rpa-object-tree" >
</div>
<div class="six wide column rpa-hierarchy" >
<p>Select element in the tree to see hierarchy list</p>
<p>Выбрать UI элемент слева в дереве </p>
</div>
<div class="four wide column rpa-property-list" >
</div>
</div>
<div class="row black">
<div class="row">
<div class="three wide column">
<div class="ui input">
<input class="openrpa-value-backend" type="text" placeholder="Протокол: win32 | uia" value="uia">
</div>
<b style="font-size:10px;color: #b1b1b1;" >Протокол: <span onclick="$('.openrpa-value-backend')[0].value='win32'" style="color:blue; cursor: pointer;">win32</span> или <span onclick="$('.openrpa-value-backend')[0].value='uia'" style="color:blue; cursor: pointer;">uia</span></b>
</div>
<div class="three wide column">
<button class="ui button grey labeled icon mini" onclick="mGlobal.GUIRefreshTree()">
<i class="up refresh icon"></i>
Refresh tree
Обновить дерево
</button>
</div>
<div class="three wide column">
</div>
<div class="ten wide column">
</div>
</div>
<h2 class="ui header">
<i class="edit icon"></i>
<div class="content">
Шаг 2. Редактировать UIO селектор
</div>
</h2>
<div class="row">
<div class="eight wide column">
<div class="ui tiny header" style="margin-bottom:0px;">Edit GUI selector</div>
<b style="font-size:10px;color: #797979;" >[{"depth_start": &ltint from 1&gt, "depth_end": &ltint from 1+&gt, "index"|"ctrl_index": &ltint from 0&gt, "title": &ltstr&gt, "title_re": &ltstr re pattern&gt, "rich_text": &ltstr&gt, "rich_text_re": &ltstr re pattern&gt, "class_name": &ltstr&gt, "class_name_re": &ltstr re pattern&gt, "friendly_class_name": &ltstr&gt, "f"riendly_class_name_re": &ltstr re pattern&gt, "control_type": &ltstr&gt, "control_type_re": &ltstr re pattern&gt, "is_enabled": &ltbool&gt, "is_visible": &ltbool&gt}]</b>
<div class="ui tiny header" style="margin-bottom:0px;">Редактировать UIO селектор</div>
<b style="font-size:1em;color: #797979;" >[{"depth_start": 1, "depth_end": 1, "index"|"ctrl_index": 0, "title": "", "title_re": ".*", "rich_text": "", "rich_text_re": ".*", "class_name": "", "class_name_re": ".*", "friendly_class_name": "", "friendly_class_name_re": ".*", "control_type": "", "control_type_re": ".*", "is_enabled": true, "is_visible": true}]</b>
<textarea style="width:100%; font-size:12pt" class="rpa-gui-selector" rows="6" cols="60"></textarea>
<button class="large ui blue button rpa-action-highlight" onclick="mGlobal.ElementHighlightNewGUISelectorString($('.rpa-gui-selector')[0].value);">Highlight element</button>
<button class="large ui grey button rpa-action-validate" onclick="mGlobal.ElementValidateGUISelectorString($('.rpa-gui-selector')[0].value);">Validate element</button>
<div class="ui tiny header">Validate Result</div>
<button class="large ui blue button rpa-action-highlight" onclick="mGlobal.ElementHighlightNewGUISelectorString($('.rpa-gui-selector')[0].value);">Подсветить UIO</button>
<button class="large ui grey button rpa-action-validate" onclick="mGlobal.ElementValidateGUISelectorString($('.rpa-gui-selector')[0].value);">Поиск UIO</button>
<div class="ui tiny header">Список подходящих UIO</div>
<div class="content openrpa-validate-result divided list" style="overflow:scroll; height: 100px;"></div>
</div>
<div class="eight wide column">
<div class="ui tiny header">Select action</div>
<div class="ui tiny header">Выбрать действие</div>
<button class="ui button blue labeled icon" onclick="mGlobal.GUIActionLoadList();">
<i class="right arrow icon"></i>
Load actions
Загрузить
</button>
<div class="ui search selection dropdown gui-action" style="width:410px;">
<div class="ui search selection dropdown gui-action" style="width:350px;">
<input type="hidden" name="country">
<i class="dropdown icon"></i>
<div class="default text">Select action</div>
<div class="default text">Выбрать действие</div>
<div class="menu">
</div>
</div>
<div class="ui tiny header">Set arguments</div>
<div class="ui tiny header">Установить аргументы</div>
<div class="ui input" style="width:500px;">
<input type="text" placeholder="[GUAActionArg1, GUAActionArg2...]" class="rpa-argument-list">
<input type="text" placeholder="[Аргумент 1, Аргумент 2...]" class="rpa-argument-list">
</div>
<p></p>
<button class="large ui green button" onclick="mGlobal.GUIActionRun();">Set focus + Run action</button>
<button class="large ui grey button" onclick="mGlobal.GUIActionAddToList();">+ Code list</button>
<div class="ui tiny header">Result</div>
<button class="large ui green button" onclick="mGlobal.GUIActionRun();">Установить фокус и выполнить действие</button>
<div class="ui tiny header">Результат</div>
<div class="content gui-result"></div>
</div>
</div>
<div class="row black">
<div class="sixteen wide column">
</div>
</div>
<div class="row">
<div class="eight wide column">
<div class="ui tiny header">Other activity</div>
<a class="ui tag label teal mini" onclick="$('.openrpa-other-activity-module-name-input')[0].value='time'; $('.openrpa-other-activity-activity-name-input')[0].value='sleep'; $('.openrpa-other-activity-argument-list-input')[0].value='[3]'; $('.openrpa-other-activity-argument-dict-input')[0].value=''; $('.gui-code-result').html('');">Sleep 3s</a>
<a class="ui tag label teal mini" onclick="$('.openrpa-other-activity-module-name-input')[0].value='Window'; $('.openrpa-other-activity-activity-name-input')[0].value='DialogYesNo'; $('.openrpa-other-activity-argument-list-input')[0].value='[&quot;Type title here&quot;,&quot;Type body message here&quot;]'; $('.openrpa-other-activity-argument-dict-input')[0].value=''; $('.gui-code-result').html('');">Show dialog YesNo</a>
<div class="ui tiny header">Module name</div>
<div class="ui input" style="width:300px;">
<input type="text" placeholder="time|keyboard|..." class="openrpa-other-activity-module-name-input">
</div>
<div class="ui tiny header">Activity name</div>
<div class="ui input" style="width:300px;">
<input type="text" placeholder="sleep|..." class="openrpa-other-activity-activity-name-input">
</div>
<div class="ui tiny header">Argument list</div>
<div class="ui input" style="width:300px;">
<input type="text" placeholder="[3]|[...]" class="openrpa-other-activity-argument-list-input">
</div>
<div class="ui tiny header">Argument dict</div>
<div class="ui input" style="width:300px;">
<input type="text" placeholder="{&quot;title&quot;:&quot;OpenRPA&quot;}|{...}" class="openrpa-other-activity-argument-dict-input">
</div>
<button class="large ui green button" onclick="mGlobal.OtherActivityRun();">Run activity</button>
<button class="large ui grey button" onclick="mGlobal.OtherActivityCodeListAppend();">+ Code list</button>
<div class="content gui-code-list-run-result"></div>
</div>
<div class="eight wide column">
<div class="ui tiny header">Code list</div>
<div class="content gui-code-result"></div>
<button class="large ui green button" onclick="mGlobal.GUICodeListRun();">Run code list</button>
<button class="large ui pink button" onclick="$('.ui.modal.openrpa-code-list-gui-import-modal textarea')[0].value=''; $('.ui.modal.openrpa-code-list-gui-import-modal').modal('show');">Import from JSON</button>
<button class="large ui blue button openrpa-save-to-clipboard" onclick="mGlobal.ClipboardSet(JSON.stringify(mGlobal.ActionSpecificationList)); alert('Saved to clipboard!')">Export JSON to clipboard</button>
</div>
</div>
</div>
<div class="ui inverted vertical footer segment">
<div class="ui center aligned container">
<div class="ui stackable inverted divided grid">
<div class="three wide column">
<h4 class="ui inverted header">Pywinauto</h4>
<div class="ui inverted link list">
<a href="https://pywinauto.readthedocs.io/en/latest/code/pywinauto.findwindows.html" class="item" target="_blank">Specification manual</a>
<a href="https://pywinauto.readthedocs.io/en/latest/code/code.html#main-user-modules" class="item" target="_blank">Classes manual</a>
<a href="https://pywinauto.readthedocs.io/en/latest/code/code.html#main-user-modules" class="item" target="_blank">How to use manual</a>
<a href="https://pywinauto.readthedocs.io/en/latest/code/pywinauto.base_wrapper.html" class="item" target="_blank">Base wrapper manual</a>
</div>
</div>
<div class="three wide column">
<h4 class="ui inverted header">Semantic UI</h4>
<div class="ui inverted link list">
<a href="https://semantic-ui.com/usage/theming.html" class="item" target="_blank">Color manual</a>
<a href="https://semantic-ui.com/elements/input.html" class="item" target="_blank">Input manual</a>
<a href="https://semantic-ui.com/elements/icon.html" class="item" target="_blank">Icon list</a>
<a href="https://semantic-ui.com/elements/button.html" class="item" target="_blank">Button manual</a>
</div>
</div>
<div class="three wide column">
<h4 class="ui inverted header">GitLab</h4>
<div class="ui inverted link list">
<a href="https://gitlab.com/UnicodeLabs/OpenRPA" class="item" target="_blank">OpenRPA repository</a>
<a href="https://pyopenrpa.ru/" class="item" target="_blank">pyOpenRPA portal</a>
<a href="https://pyopenrpa.ru/wiki/guide/index" class="item" target="_blank">pyOpenRPA wiki</a>
<a href="#" class="item">Link -</a>
</div>
</div>
<div class="seven wide column">
<h4 class="ui inverted header">pyOpenRPA</h4>
<p>Open source Robotic Process Automation software by the pyOpenRPA LLC (Created by Ivan Maslov in Russia). Licensed under LICENSE.PDF or https://pyopenrpa.ru/license/oferta.pdf #IT4Business</p>
</div>
</div>
<div class="ui inverted section divider"></div>
</div>
</div>
{% include 'footer.xhtml' %}
<div class="ui basic modal">
<div class="ui icon header">
</div>

@ -0,0 +1,407 @@
from pyOpenRPA.Tools import CrossOS
if CrossOS.IS_WINDOWS_BOOL: import pyaudio
if CrossOS.IS_WINDOWS_BOOL: from pydub import AudioSegment
import threading
import time
from pyOpenRPA.Utils import Text
import os
def DeviceMicrophoneIndex():
"""L-,W+: Выполнить поиск устройства, с помощью которого можно будет выполнить захват c микрофона.
"""
p = pyaudio.PyAudio()
lDeviceInfoDict = p.get_default_input_device_info()
lDefaultIndexInt = lDeviceInfoDict["index"]
return lDefaultIndexInt
def DeviceSystemSoundIndex():
"""L-,W+: Выполнить поиск устройства, с помощью которого можно будет выполнить захват аудио, которое поступает из приложений. Например: аудиоконференции Zoom, whatsapp, telegram и т.д.
"""
p = pyaudio.PyAudio()
inInputBool = True
inIsLoopbackBool = True
if inInputBool == True:
lDeviceInfoDict = p.get_default_output_device_info()
lDefaultIndexInt = lDeviceInfoDict["index"]
lDefaultNameStr = lDeviceInfoDict["name"]
lCatchIndexInt = None
lCatchDiffRatioFloat = 0.0
for lItemDict in DeviceListGet():
lCompareBool = False
if lItemDict["MaxOutputChannelsInt"]>0:
if inIsLoopbackBool==True and lItemDict["HostApiStr"]=="Windows WASAPI": lCompareBool = True
elif inIsLoopbackBool==False: lCompareBool = True
if lCompareBool == True:
lDiffRationFloat = Text.SimilarityNoCase(in1Str=lDefaultNameStr, in2Str=lItemDict["NameStr"])
if lDiffRationFloat> lCatchDiffRatioFloat:
lCatchDiffRatioFloat = lDiffRationFloat
lCatchIndexInt=lItemDict["IndexInt"]
else:
lDeviceInfoDict = p.get_default_output_device_info()
lDefaultIndexInt = lDeviceInfoDict["index"]
lDefaultNameStr = lDeviceInfoDict["name"]
lCatchIndexInt = None
lCatchDiffRatioFloat = 0.0
for lItemDict in DeviceListGet():
lCompareBool = False
if lItemDict["MaxInputChannelsInt"]>0:
if inIsLoopbackBool==True and lItemDict["HostApiStr"]=="Windows WASAPI": lCompareBool = True
elif inIsLoopbackBool==False: lCompareBool = True
if lCompareBool == True:
lDiffRationFloat = Text.SimilarityNoCase(in1Str=lDefaultNameStr, in2Str=lItemDict["NameStr"])
if lDiffRationFloat> lCatchDiffRatioFloat: lCatchIndexInt=lItemDict["IndexInt"]
return lCatchIndexInt
def DeviceListGet():
"""L-,W+: Вернуть список аудио устройст (входящих и исходящих, микрофонов и динамиков).
from pyOpenRPA.Robot import Audio
Audio.DeviceListGet()
:return: [{"IndexInt":1, "NameStr": "",
"HostApiInt": 0, "HostApiStr": "MME"|"Windows WASAPI"|"Windows WDM-KS",
"MaxInputChannelsInt": 0, "MaxOutputChannelsInt": 0,
"DefaultSampleRateFloat": 44100.0
},...]
:rtype: list
"""
l_result = []
p = pyaudio.PyAudio()
for i in range(0, p.get_device_count()):
l_info = p.get_device_info_by_index(i)
l_info_dict = {
"IndexInt":l_info["index"],
"NameStr": l_info["name"],
"MaxInputChannelsInt": l_info["maxInputChannels"],
"MaxOutputChannelsInt": l_info["maxOutputChannels"],
"HostApiInt": l_info["hostApi"],
"DefaultSampleRateFloat": l_info["defaultSampleRate"],
"HostApiStr": p.get_host_api_info_by_index(l_info["hostApi"])["name"] #"MME"|"Windows WASAPI"|"Windows WDM-KS"
}
l_result.append(l_info_dict)
return l_result
class Recorder:
mStatusStr = "0_READY" # "0_READY", "1_RECORDING"
mAudio = None
mCaptureThread = None
mStream = None
mDeviceInt = None
mChannelCountInt = None
mFramesInt = 512
mRecordedFramesList = []
mSampleRateInt = None
mSampleSizeInt = None
mCaptureBool = True
mFolderPathStr = None
mFileNameStr = None
mFileFormatStr = None
mFileAvailableChunkInt = None
mFileNameList=None
mFileInfoDict=None # {"file.mp3":{StartSecFloat:, EndSecFloat:, Extra:,PathStr, }}
mChunkSecFloat = None
mStartSecFloat = None
mStartChunkSecFloat = None
mDurationSecFloat = None
mThresholdInt = 500
mSilentLastCheckTimeFloat = None
mIsMicrophoneBool=None
mCallbackChunkDef = None
mCallbackChunkDefThreadList = []
mCallbackStopDef = None
mCallbackStopDefThreadList = []
def __init__(self, inDeviceInt=None):
"""L-,W+: Инициализация экземпляра класса записи звука
.. code-block:: python
from pyOpenRPA.Robot import Audio
lRec = Audio.Recorder()
:param inDeviceInt: , по умолчанию None (использование устройства, полученного от DeviceSystemSoundIndex())
:type inDeviceInt: int, опционально
"""
self.mDeviceInt = inDeviceInt
if inDeviceInt == None: inDeviceInt = DeviceSystemSoundIndex()
self.mDeviceInt = inDeviceInt
if DeviceListGet()[inDeviceInt]["MaxInputChannelsInt"]>0: self.mIsMicrophoneBool = True
else: self.mIsMicrophoneBool = False
def StatusGet(self):
"""L-,W+: Вернуть статус записи звука
.. code-block:: python
from pyOpenRPA.Robot import Audio
lRec = Audio.Recorder()
lRec.StatusGet()
:return: "0_READY" или "1_RECORDING"
:rtype: str
"""
return self.mStatusStr
def CaptureStart(self, inFolderPathStr="",inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 300.0, inCallbackChunkDef = None, inCallbackStopDef = None):
"""L-,W+: Начать запись звука
.. code-block:: python
def CallbackChunk(lRec, lFilenameStr):
pass # КОД ОБРАБОТКИ ПОСЛЕ СОХРАНЕНИЯ ЧАСТИ
from pyOpenRPA.Robot import Audio
lRec = Audio.Recorder()
lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0, inCallbackChunkDef=CallbackChunk)
lRec.CaptureStop()
:param inFolderPathStr: Путь к папке, в которую сохранять аудиофайлы захвата , по умолчанию ""
:type inFolderPathStr: str, опционально
:param inFileNameStr: Наименование файла без расширения, по умолчанию "out"
:type inFileNameStr: str, опционально
:param inFileFormatStr: Наименование формата, в который будет происходить сохранение ("mp3" или "wav" или "raw" или "aif"), по умолчанию "mp3"
:type inFileFormatStr: str, опционально
:param inDurationSecFloat: Длительность захвата аудио, по умолчанию None (пока не поступит команда CaptureStop() )
:type inDurationSecFloat: float, опционально
:param inChunkSecFloat: Максимальная длина части аудиофайла, по умолчанию 300.0
:type inChunkSecFloat: float, опционально
:param inCallbackChunkDef: Функция, которая будет инициирована в случае выполнения Chunk сохранения (сохранение части). Callback функция должна принимать 2 аргумента: экземпляр класса Recorder и наименование сохраненного файла. Внимание! Функция запускается асинхронно!
:type inCallbackChunkDef: def, опционально
:param inCallbackStopDef: Функция, которая будет инициирована в случае окончания записи. Callback функция должна принимать 2 аргумента: экземпляр класса Recorder и наименование сохраненного файла. Внимание! Функция запускается асинхронно!
:type inCallbackStopDef: def, опционально
"""
# CHECK AUX.mp3
self.mFileInfoDict = {}
self.mFileNameList=[]
#self.mRecordedFramesList=[]
self.mStatusStr = "1_RECORDING"
if inChunkSecFloat != None: self.mFileAvailableChunkInt = 0
self.mDurationSecFloat = inDurationSecFloat
self.mChunkSecFloat = inChunkSecFloat
self.mSilentLastCheckTimeFloat=time.time()
self.mFolderPathStr = inFolderPathStr
self.mFileNameStr = inFileNameStr
self.mFileFormatStr = inFileFormatStr
self.mAudio = pyaudio.PyAudio()
self.mSampleSizeInt = self.mAudio.get_sample_size(pyaudio.paInt16)
self.mCallbackChunkDef = inCallbackChunkDef
self.mCallbackStopDef = inCallbackStopDef
lDeviceInfoDict = self.mAudio.get_device_info_by_index(self.mDeviceInt)
#Open stream
self.mSampleRateInt = int(lDeviceInfoDict["defaultSampleRate"])
self.mChannelCountInt = lDeviceInfoDict["maxInputChannels"] if (lDeviceInfoDict["maxOutputChannels"] < lDeviceInfoDict["maxInputChannels"]) else lDeviceInfoDict["maxOutputChannels"]
self.mStream = self.mAudio.open(format = pyaudio.paInt16,
channels = self.mChannelCountInt,
rate = self.mSampleRateInt,
input = True,
frames_per_buffer = self.mFramesInt,
input_device_index = lDeviceInfoDict["index"],
as_loopback = not self.mIsMicrophoneBool)
self.mCaptureThread = threading.Thread(target=self.__Capture__, daemon = True)
self.mStartSecFloat = time.time()
self.mStartChunkSecFloat = self.mStartSecFloat
self.mCaptureThread.start()
def __Capture__(self):
while self.mCaptureBool==True:
self.mRecordedFramesList.append(self.mStream.read(self.mFramesInt))
self.__TriggerCheck__()
def CaptureWait(self, inWaitCallbackChunkBool = True, inWaitCallbackStopBool = True):
"""L-,W+: Ожидать окончания захвата аудио. Дополнительно настраивается ожидание окончания всех callback функций.
.. code-block:: python
from pyOpenRPA.Robot import Audio
lRec = Audio.Recorder()
lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = 10.0, inChunkSecFloat = 5.0)
lRec.CaptureWait()
:param inWaitCallbackChunkBool: True - ожидать выполнение всех асинхронных callback по сохранению части аудиофайла, по умолчанию True
:type inWaitCallbackChunkBool: bool, опционально
:param inWaitCallbackStopBool: True - ожидать выполнение всех асинхронных callback по завершению записи, по умолчанию True
:type inWaitCallbackStopBool: bool, опционально
"""
self.mCaptureThread.join()
if inWaitCallbackChunkBool==True:
for lItemThread in self.mCallbackChunkDefThreadList:
if lItemThread.is_alive(): lItemThread.join()
self.mCallbackChunkDefThreadList = []
if inWaitCallbackStopBool==True:
for lItemThread in self.mCallbackStopDefThreadList:
if lItemThread.is_alive(): lItemThread.join()
self.mCallbackStopDefThreadList = []
def CaptureStop(self, inWaitStream=True, inExtra=None):
"""L-,W+: Остановить захват аудио
.. code-block:: python
from pyOpenRPA.Robot import Audio
lRec = Audio.Recorder()
lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0)
lRec.CaptureStop()
:param inWaitStream: True - выполнить ожидание окончания потока захвата перед окончанием, по умолчанию True
:type inWaitStream: bool, опционально
:param inExtra: Дополнительный контент, необходимый для идентификации файла. В дальнейшем получить структуру можно с помощью функции FileInfoGet()['Extra'], по умолчанию None
:type inExtra: any, опционально
"""
self.mCaptureBool=False
self.mStream.stop_stream()
if inWaitStream == True: self.mCaptureThread.join()
self.mStream.close()
#Close module
self.mAudio.terminate()
lFileNameStr = self.CaptureChunk(inExtra=inExtra, inForceChunkBool=False)
self.mStatusStr = "0_READY"
if self.mCallbackStopDef != None:
lCallbackThread = threading.Thread(target=self.mCallbackStopDef,args=[self, lFileNameStr], daemon = True)
lCallbackThread.start()
self.mCallbackStopDefThreadList.append(lCallbackThread)
def CaptureChunk(self, inExtra=None, inForceChunkBool=True, inShiftSecFloat = 0.0):
"""L-,W+: Зафиксировать захват аудио в виде промежуточного файла вида: <имя файла>_00000.mp3
.. code-block:: python
from pyOpenRPA.Robot import Audio
lRec = Audio.Recorder()
lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0)
lRec.CaptureChunk()
:param inExtra: Дополнительный контент, необходимый для идентификации файла. В дальнейшем получить структуру можно с помощью функции FileInfoGet()['Extra'], по умолчанию None
:type inExtra: any, опционально
:param inForceChunkBool: True - вне зависимости от текущего режима перейти на режим сохранения по частям, по умолчанию True
:type inForceChunkBool: bool, опционально
:param inShiftSecFloat: Последние секунды, которые не записывать в промежуточный аудиофайл. Они будут началом следующего аудио отрывка, по умолчанию 0.0
:type inShiftSecFloat: float
:return: Наименование сохраненного аудиофайла
:rtype: str
"""
if inForceChunkBool==True and self.mFileAvailableChunkInt==None: self.mFileAvailableChunkInt=0
lFileNameStr = self.mFileNameStr
if self.mFileAvailableChunkInt!=None:
lFileNameStr+=f"_{self.mFileAvailableChunkInt:05}"
self.mFileAvailableChunkInt = self.mFileAvailableChunkInt + 1
lFileNameWExtStr = f"{lFileNameStr}.{self.mFileFormatStr}"
lFilePathStr = os.path.abspath(os.path.join(self.mFolderPathStr,lFileNameWExtStr))
lDataFrameBytes = b''.join(self.mRecordedFramesList)
if inShiftSecFloat != 0.0:
lFrameLenInt = int(self.mSampleSizeInt*inShiftSecFloat*self.mChannelCountInt*self.mSampleRateInt)
lFrameLenInt = int(lFrameLenInt // (self.mChannelCountInt * self.mSampleSizeInt)) * (self.mChannelCountInt * self.mSampleSizeInt)
if lFrameLenInt<len(lDataFrameBytes):
self.mRecordedFramesList=[lDataFrameBytes[-lFrameLenInt:]]
lDataFrameBytes = lDataFrameBytes[:-lFrameLenInt]
else:
lDataFrameBytes=b""
else:
self.mRecordedFramesList = []
# Advanced usage, if you have raw audio data:
sound = AudioSegment(
# raw audio data (bytes)
data=lDataFrameBytes,
# 2 byte (16 bit) samples
sample_width=self.mSampleSizeInt,
# 44.1 kHz frame rate
frame_rate=self.mSampleRateInt,
# stereo
channels=self.mChannelCountInt
)
lTimeSecFloat = time.time() - inShiftSecFloat
if not os.path.exists(os.path.abspath(self.mFolderPathStr)):
os.mkdir(self.mFolderPathStr)
sound.export(lFilePathStr, format=f"{self.mFileFormatStr}")
self.mFileNameList.append(lFileNameWExtStr)
self.mFileInfoDict[lFileNameWExtStr]= {
"StartSecFloat": self.mStartChunkSecFloat,
"EndSecFloat": lTimeSecFloat,
"Extra": inExtra,
"PathStr": lFilePathStr
}
if self.mCallbackChunkDef != None and self.mFileAvailableChunkInt!=None:
lCallbackThread = threading.Thread(target=self.mCallbackChunkDef,args=[self, lFileNameWExtStr], daemon = True)
lCallbackThread.start()
self.mCallbackChunkDefThreadList.append(lCallbackThread)
self.mStartChunkSecFloat = lTimeSecFloat
return lFileNameWExtStr
def FileInfoGet(self, inFileNameStr=None):
"""L-,W+: Вернуть информацию по аудиофайлу inFileNameStr. Если inFileNameStr == None -> Функция вернет информацию по последнему записанному файлу
.. code-block:: python
from pyOpenRPA.Robot import Audio
lRec = Audio.Recorder()
lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0)
lRec.CaptureChunk()
lFileInfoDict = lRec.FileInfoGet()
:param inFileNameStr: Наименование аудиофайла с указанием расширения, по умолчанию None (взять последний записанный файл)
:type inFileNameStr: str, опционально
:return: {StartSecFloat:, EndSecFloat:, Extra:, PathStr:, } или None
:rtype: dict
"""
if inFileNameStr == None: inFileNameStr = self.FileLastGet()
return self.mFileInfoDict.get(inFileNameStr, None)
def FileListGet(self):
"""L-,W+: Вернуть список сохраненных аудиофайлов (наименования)
.. code-block:: python
from pyOpenRPA.Robot import Audio
lRec = Audio.Recorder()
lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0)
lRec.CaptureChunk()
lRec.CaptureChunk()
lRec.FileListGet()
:return: ["out_00000.mp3", "out_00001.mp3", ...]
:rtype: list
"""
return self.mFileNameList
def FileLastGet(self):
"""L-,W+: Вернуть наименование последнего сохраненного аудиофайла
.. code-block:: python
from pyOpenRPA.Robot import Audio
lRec = Audio.Recorder()
lRec.CaptureStart(inFileNameStr = "out", inFileFormatStr = "mp3", inDurationSecFloat = None, inChunkSecFloat = 5.0)
lRec.CaptureChunk()
lRec.CaptureChunk()
lRec.FileListGet()
:return: ["out_00000.mp3", "out_00001.mp3", ...]
:rtype: list
"""
return self.mFileNameList[-1]
#def IsSilent(self, inLastSecFloat=None):
# "Returns 'True' if below the 'silent' threshold"
# self.mSilentLastCheckTimeFloat = time.time()
# if inLastSecFloat == None: inLastSecFloat = self.mChunkSilentSecFloat
# lFrameLenInt = int(self.mSampleSizeInt*inLastSecFloat)
# if lFrameLenInt<len(self.mRecordedFramesList): lData = self.mRecordedFramesList[-lFrameLenInt:]
# else: lData = self.mRecordedFramesList
# return max(lData) < self.mThresholdInt
def __TriggerCheck__(self):
"""Контроль записи / остановки аудио по следующим критериям:
- Общая длительность,
- Максимальная длительность части,
- Максимальная длит тишины (часть),
- Максимальная длительность тишины (остановка),
"""
# Проверка по длине записи (CHUNK)
if self.mChunkSecFloat != None and time.time() - self.mStartChunkSecFloat > self.mChunkSecFloat: self.CaptureChunk()
# Остановка записи по максимальной длине
if self.mDurationSecFloat != None and time.time() - self.mStartSecFloat > self.mDurationSecFloat: self.CaptureStop(inWaitStream=False)
# Проверка тишины
#if self.mChunkSilentSecFloat != None and time.time() - self.mSilentLastCheckTimeFloat and self.IsSilent(): self.mT.append("ТИШИНА!!")

@ -413,6 +413,8 @@ def Up(inKeyInt:int, inWaitAfterSecFloat:float=WAIT_AFTER_SEC_FLOAT) -> None:
ВНИМАНИЕ! ПРИ ПОПЫТКЕ ПЕЧАТИ ТЕКСТА БУДЕТ УЧИТЫВАТЬ ТЕКУЩУЮ РАСКЛАДКУ КЛАВИАТУРЫ. ДЛЯ ПЕЧАТИ ТЕКСТА ИСПОЛЬЗУЙ Write!
ВНИМАНИЕ! ФУНКЦИЯ МОЖЕТ ОТРАБОТАТЬ НЕКОРРЕКТНО В ТОМ СЛУЧАЕ, ЕСЛИ ДЕЙСТВИЕ ПРОИСХОДИТ СРАЗУ ПОСЛЕ НАЖАТИЯ КЛАВИШИ ENTER (ИСПОЛЬЗУЙТЕ SLEEP)
.. code-block:: python
# Keyboard: Взаимодействие с клавиатурой
@ -432,6 +434,8 @@ def Down(inKeyInt:int, inWaitAfterSecFloat:float=WAIT_AFTER_SEC_FLOAT) -> None:
ВНИМАНИЕ! ПРИ ПОПЫТКЕ ПЕЧАТИ ТЕКСТА БУДЕТ УЧИТЫВАТЬ ТЕКУЩУЮ РАСКЛАДКУ КЛАВИАТУРЫ. ДЛЯ ПЕЧАТИ ТЕКСТА ИСПОЛЬЗУЙ Write!
ВНИМАНИЕ! ФУНКЦИЯ МОЖЕТ ОТРАБОТАТЬ НЕКОРРЕКТНО В ТОМ СЛУЧАЕ, ЕСЛИ ДЕЙСТВИЕ ПРОИСХОДИТ СРАЗУ ПОСЛЕ НАЖАТИЯ КЛАВИШИ ENTER (ИСПОЛЬЗУЙТЕ SLEEP)
.. code-block:: python
# Keyboard: Взаимодействие с клавиатурой

@ -165,7 +165,7 @@ def BrowserClose():
"""
global gBrowser
if gBrowser is not None: gBrowser.quit()
if gBrowser is not None: gBrowser.close() # ранее был gBrowser.close(), но он трактуется браузером как принудительное завершение
def UIOSelectorList(inUIOSelectorStr, inUIO=None) -> list:
"""L+,W+: Получить список UIO объектов по UIO селектору.
@ -177,7 +177,7 @@ def UIOSelectorList(inUIOSelectorStr, inUIO=None) -> list:
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIOList = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)
lUIOList = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI объекта на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
@ -212,7 +212,7 @@ def UIOSelectorFirst(inUIOSelectorStr, inUIO=None) -> list:
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorFirst(inUIOSelectorStr = lUIOSelectorStr)
lUIO = UIWeb.UIOSelectorFirst(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI объекта на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
@ -237,7 +237,7 @@ def UIOTextGet(inUIO) -> str:
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
lTextStr = UIWeb.UIOTextGet(inUIO=lUIO)
UIWeb.BrowserClose()
@ -258,7 +258,7 @@ def UIOAttributeGet(inUIO, inAttributeStr) -> str:
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeGet(inUIO=lUIO, inAttributeStr = "href")
UIWeb.BrowserClose()
@ -281,7 +281,7 @@ def UIOAttributeStyleGet(inUIO, inAttributeStr) -> str:
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeStyleGet(inUIO=lUIO, inAttributeStr = "href")
UIWeb.BrowserClose()
@ -304,7 +304,7 @@ def UIOAttributeSet(inUIO, inAttributeStr, inValue):
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeSet(inUIO=lUIO, inAttributeStr = "href", inValue = "https://mail.ru")
UIWeb.BrowserClose()
@ -329,7 +329,7 @@ def UIOAttributeRemove(inUIO, inAttributeStr):
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeRemove(lUIO, "href")
UIWeb.BrowserClose()
@ -352,7 +352,7 @@ def UIOAttributeStyleSet(inUIO, inAttributeStr, inValue):
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeStyleSet(inUIO=lUIO, inAttributeStr = "color", inValue = "grey")
UIWeb.BrowserClose()
@ -377,7 +377,7 @@ def UIOAttributeStyleRemove(inUIO, inAttributeStr:str):
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIWeb.UIOAttributeStyleRemove(lUIO, "color")
UIWeb.BrowserClose()
@ -400,7 +400,7 @@ def UIOClick(inUIO):
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lUIO = UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
lUIO = UIWeb.UIOSelectorList(inUIOSelectorStr = lUIOSelectorStr)[0]
UIOClick(inUIO = lUIO)
UIWeb.BrowserClose()
@ -419,7 +419,7 @@ def UIOSelectorHighlight(inUIOSelectorStr: str, inIsFirst:bool=False, inDuration
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
UIOSelectorHighlight(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.UIOSelectorHighlight(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI элемента на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
@ -475,7 +475,7 @@ def UIOSelectorClick(inUIOSelectorStr: str):
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
UIOSelectorClick(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.UIOSelectorClick(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI элемента на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
@ -493,7 +493,7 @@ def UIOSelectorWaitAppear(inUIOSelectorStr:str, inWaitSecFloat:float=UIO_WAIT_SE
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
lAppearUIOList = UIOSelectorWaitAppear(inUIOSelectorStr = lUIOSelectorStr)
lAppearUIOList = UIWeb.UIOSelectorWaitAppear(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI элемента на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
@ -525,7 +525,7 @@ def UIOSelectorWaitDisappear(inUIOSelectorStr:str, inWaitSecFloat:float=UIO_WAIT
UIWeb.BrowserChromeStart()
UIWeb.PageOpen("https://mail.ru")
lUIOSelectorStr = "//*[@id=\"grid\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a"
UIOSelectorWaitDisappear(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.UIOSelectorWaitDisappear(inUIOSelectorStr = lUIOSelectorStr)
UIWeb.BrowserClose()
:param inUIOSelectorStr: XPATH или CSS селектор UI элемента на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
@ -557,7 +557,7 @@ def UIOSelectorDetect(inUIOSelectorStr:str) -> str:
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"
lResultStr = UIOSelectorDetect(inUIOSelectorStr = lUIOSelectorStr)
lResultStr = UIWeb.UIOSelectorDetect(inUIOSelectorStr = lUIOSelectorStr)
:param inUIOSelectorStr: XPATH или CSS селектор UI объекта на web странице. Подсказки по CSS: https://devhints.io/css Подсказки по XPath: https://devhints.io/xpath
:type inUIOSelectorStr: str

@ -30,6 +30,13 @@ RobotConnector.mGlobalDict = gSettingsDict
#Init the robot
RobotConnector.UIDesktop.Utils.ProcessBitness.SettingsInit(gSettingsDict["ProcessBitness"])
from pyOpenRPA.Utils.Render import Render
from pyOpenRPA.Tools import CrossOS # https://schedule.readthedocs.io/en/stable/examples.html
lFileStr = CrossOS.PathJoinList(CrossOS.PathSplitList(__file__)[:-2] + ["Resources","Web","orpa","std.xhtml"])
gRender = Render(inTemplatePathStr=lFileStr,inTemplateRefreshBool=True)
from pyOpenRPA import __version__
# HTTP Studio web server class
class testHTTPServer_RequestHandler(BaseHTTPRequestHandler):
#ResponseContentTypeFile
@ -49,7 +56,16 @@ class testHTTPServer_RequestHandler(BaseHTTPRequestHandler):
lStudioFolder = "\\".join(__file__.split("\\")[:-1])
#Мост между файлом и http запросом (новый формат)
if self.path == "/":
self.SendResponseContentTypeFile('text/html', os.path.join(lStudioFolder, "Web\\Index.xhtml"))
# Пример использования
global gRender
lStr = gRender.Generate(inDataDict={"title":"СТУДИЯ PYOPENRPA", "subtitle":"ПОИСК UIO СЕЛЕКТОРА", "version":__version__})
# Send response status code
self.send_response(200)
# Send headers
self.send_header('Content-type','text/html')
self.end_headers()
# Write content as utf-8 data
self.wfile.write(bytes(lStr, "utf8"))
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Semantic-UI-CSS-master/semantic.min.css':
self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.css"))
@ -67,10 +83,55 @@ class testHTTPServer_RequestHandler(BaseHTTPRequestHandler):
self.SendResponseContentTypeFile('font/woff2', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default\\assets\\fonts\\icons.woff2"))
#Мост между файлом и http запросом (новый формат)
if self.path == '/favicon.ico':
self.SendResponseContentTypeFile('image/x-icon', os.path.join(lStudioFolder, "Web\\favicon.ico"))
self.SendResponseContentTypeFile('image/x-icon', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\favicon.ico"))
#Мост между файлом и http запросом (новый формат)
if self.path == '/pyOpenRPA_logo.png' or self.path == '/orpa/resources/Web/orpa/logo.png':
self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\logo.png"))
#Мост между файлом и http запросом (новый формат)
if self.path == '/pyOpenRPA_logo.png':
self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\pyOpenRPA_logo.png"))
if self.path == '/pyOpenRPA_logo.png' or self.path == '/orpa/resources/Web/orpa/logo.png':
self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\logo.png"))
if self.path == '/orpa/resources/Web/orpa/styleset/home.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\home.css"))
if self.path == '/orpa/resources/Web/orpa/styleset/home.js': self.SendResponseContentTypeFile('application/javascript', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\home.js"))
if self.path == '/orpa/resources/Web/orpa/styleset/bg1.jpg': self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\bg1.jpg"))
if self.path == '/orpa/resources/Web/orpa/styleset/bg2.jpg': self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\bg2.jpg"))
if self.path == '/orpa/resources/Web/orpa/styleset/bg3.jpg': self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\bg3.jpg"))
if self.path == '/orpa/resources/Web/orpa/styleset/bg4.jpg': self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\bg4.jpg"))
if self.path == '/orpa/resources/Web/orpa/styleset/bg5.jpg': self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\bg5.jpg"))
if self.path == '/orpa/resources/Web/orpa/styleset/bg6.jpg': self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\bg6.jpg"))
if self.path == '/orpa/resources/Web/orpa/styleset/bg7.jpg': self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\bg7.jpg"))
if self.path == '/orpa/resources/Web/orpa/styleset/bg8.jpg': self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\bg8.jpg"))
if self.path == '/orpa/resources/Web/orpa/styleset/bg9.jpg': self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\bg9.jpg"))
if self.path == '/orpa/resources/Web/orpa/styleset/bg10.jpg': self.SendResponseContentTypeFile('image/png', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\bg10.jpg"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/site.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\site.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/reset.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\reset.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/container.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\container.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/grid.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\grid.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/header.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\header.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/image.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\image.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/divider.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\divider.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/menu.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\menu.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/dropdown.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\dropdown.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/segment.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\segment.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/button.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\button.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/list.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\list.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/sidebar.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\sidebar.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/icon.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\icon.css"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/components/transition.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\components\\transition.css"))
if self.path == '/orpa/resources/Web/orpa/styleset/sidebar.js': self.SendResponseContentTypeFile('application/javascript', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\sidebar.js"))
if self.path == '/orpa/resources/Web/orpa/styleset/transition.js': self.SendResponseContentTypeFile('application/javascript', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\transition.js"))
if self.path == '/orpa/resources/Web/orpa/styleset/docs.js': self.SendResponseContentTypeFile('application/javascript', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\docs.js"))
if self.path == '/orpa/resources/Web/orpa/styleset/visibility.js': self.SendResponseContentTypeFile('application/javascript', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\visibility.js"))
if self.path == '/orpa/resources/Web/orpa/styleset/highlight.min.js': self.SendResponseContentTypeFile('application/javascript', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\highlight.min.js"))
if self.path == '/orpa/resources/Web/orpa/styleset/less.min.js': self.SendResponseContentTypeFile('application/javascript', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\less.min.js"))
if self.path == '/orpa/resources/Web/orpa/styleset/easing.min.js': self.SendResponseContentTypeFile('application/javascript', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\styleset\\easing.min.js"))
if self.path == '/metadata.json': self.SendResponseContentTypeFile('application/json', os.path.join(lStudioFolder, "..\\Resources\\Web\\orpa\\\styleset\\metadata.json"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/themes/default/assets/fonts/icons.woff2': self.SendResponseContentTypeFile('font/woff2', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default\\assets\\fonts\\icons.woff2"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/themes/default/assets/fonts/icons.woff': self.SendResponseContentTypeFile('font/woff', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default\\assets\\fonts\\icons.woff"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/themes/default/assets/fonts/icons.ttf': self.SendResponseContentTypeFile('font/ttf', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default\\assets\\fonts\\icons.ttf"))
if self.path == '/orpa/resources/Web/Semantic-UI-CSS-master/semantic.min.css': self.SendResponseContentTypeFile('text/css', os.path.join(lStudioFolder, "..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.css"))
# POST
def do_POST(self):
#Restart studio

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

@ -20,4 +20,5 @@ def LiveDebugCheckThread(**inKWARGS):
global gKWARGS
gKWARGS = inKWARGS
lThread = threading.Thread(target=LiveDebugCheckLoop)
lThread.setName("DEBUG_LIVE")
lThread.start()

@ -0,0 +1,43 @@
# Tool to merge complex dictionaries
def Merge(in1Dict, in2Dict):
"""
Сливать словарь in2Dict в in1Dict. В случае конфликта вывести исключение
:param in1Dict: Исходный словарь. В него будет производится запись. Ссылка на него будет активна
:param in2Dict: Изменяющий словарь. Новые данные, которые будут скопированы в in1Dict
:return: Обновленный словарь in1Dict
"""
lPathList=None
if lPathList is None: lPathList = []
for lKeyStr in in2Dict:
if lKeyStr in in1Dict:
if isinstance(in1Dict[lKeyStr], dict) and isinstance(in2Dict[lKeyStr], dict):
Merge(in1Dict[lKeyStr], in2Dict[lKeyStr])
elif in1Dict[lKeyStr] == in2Dict[lKeyStr]:
pass # same leaf value
else:
raise Exception('Conflict at %s' % '.'.join(lPathList + [str(lKeyStr)]))
else:
in1Dict[lKeyStr] = in2Dict[lKeyStr]
return in1Dict
# Tool to merge complex dictionaries - no exceptions, just overwrite dict 2 in dict 1
def MergeNoException(in1Dict, in2Dict):
"""
Сливать словарь in2Dict в in1Dict. В случае конфликта перезаписать на значение из in2Dict
:param in1Dict: Исходный словарь. В него будет производится запись. Ссылка на него будет активна
:param in2Dict: Изменяющий словарь. Новые данные, которые будут скопированы в in1Dict
:return: Обновленный словарь in1Dict
"""
lPathList=None
if lPathList is None: lPathList = []
for lKeyStr in in2Dict:
if lKeyStr in in1Dict:
if isinstance(in1Dict[lKeyStr], dict) and isinstance(in2Dict[lKeyStr], dict):
MergeNoException(in1Dict[lKeyStr], in2Dict[lKeyStr])
else:
in1Dict[lKeyStr] = in2Dict[lKeyStr]
else:
in1Dict[lKeyStr] = in2Dict[lKeyStr]
return in1Dict

@ -0,0 +1,11 @@
import os
import shutil
def CheckFile(inDstPathStr, inTmplPathStr):
if os.path.exists(inDstPathStr) == False:
shutil.copy(inTmplPathStr, inDstPathStr)
def CheckFolder(inDstPathStr):
# проверка наличия всех файлов/каталогов
if not os.path.exists(os.path.abspath(inDstPathStr)):
os.mkdir(inDstPathStr)

@ -0,0 +1,17 @@
import requests
def RequestPrettyPrint(inRequest):
"""
At this point it is completely built and ready
to be fired; it is "prepared".
However pay attention at the formatting used in
this function because it is programmed to be pretty
printed and may differ from the actual request.
"""
prepared = inRequest.prepare()
print('{}\n{}\r\n{}\r\n\r\n{}'.format(
'-----------START-----------',
prepared.method + ' ' + prepared.url,
'\r\n'.join('{}: {}'.format(k, v) for k, v in prepared.headers.items()),
prepared.body,
))

@ -0,0 +1,7 @@
import difflib
def SimilarityNoCase(in1Str, in2Str):
normalized1 = in1Str.lower()
normalized2 = in2Str.lower()
matcher = difflib.SequenceMatcher(None, normalized1, normalized2)
return matcher.ratio()

@ -3,7 +3,7 @@ r"""
The pyOpenRPA package
"""
__version__ = 'v1.3.0'
__version__ = 'v1.3.1'
__all__ = []
__author__ = 'Ivan Maslov <ivan.maslov@pyopenrpa.ru>'
#from .Core import Robot

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 Alex Grönholm
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,102 @@
Metadata-Version: 2.1
Name: anyio
Version: 3.6.1
Summary: High level compatibility layer for multiple asynchronous event loop implementations
Author: Alex Grönholm
Author-email: alex.gronholm@nextday.fi
License: MIT
Project-URL: Documentation, https://anyio.readthedocs.io/en/latest/
Project-URL: Source code, https://github.com/agronholm/anyio
Project-URL: Issue tracker, https://github.com/agronholm/anyio/issues
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Framework :: AnyIO
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Requires-Python: >=3.6.2
License-File: LICENSE
Requires-Dist: idna (>=2.8)
Requires-Dist: sniffio (>=1.1)
Requires-Dist: contextvars ; python_version < "3.7"
Requires-Dist: dataclasses ; python_version < "3.7"
Requires-Dist: typing-extensions ; python_version < "3.8"
Provides-Extra: doc
Requires-Dist: packaging ; extra == 'doc'
Requires-Dist: sphinx-rtd-theme ; extra == 'doc'
Requires-Dist: sphinx-autodoc-typehints (>=1.2.0) ; extra == 'doc'
Provides-Extra: test
Requires-Dist: coverage[toml] (>=4.5) ; extra == 'test'
Requires-Dist: hypothesis (>=4.0) ; extra == 'test'
Requires-Dist: pytest (>=7.0) ; extra == 'test'
Requires-Dist: pytest-mock (>=3.6.1) ; extra == 'test'
Requires-Dist: trustme ; extra == 'test'
Requires-Dist: contextlib2 ; (python_version < "3.7") and extra == 'test'
Requires-Dist: uvloop (<0.15) ; (python_version < "3.7" and (platform_python_implementation == "CPython" and platform_system != "Windows")) and extra == 'test'
Requires-Dist: mock (>=4) ; (python_version < "3.8") and extra == 'test'
Requires-Dist: uvloop (>=0.15) ; (python_version >= "3.7" and (platform_python_implementation == "CPython" and platform_system != "Windows")) and extra == 'test'
Provides-Extra: trio
Requires-Dist: trio (>=0.16) ; extra == 'trio'
.. image:: https://github.com/agronholm/anyio/actions/workflows/test.yml/badge.svg
:target: https://github.com/agronholm/anyio/actions/workflows/test.yml
:alt: Build Status
.. image:: https://coveralls.io/repos/github/agronholm/anyio/badge.svg?branch=master
:target: https://coveralls.io/github/agronholm/anyio?branch=master
:alt: Code Coverage
.. image:: https://readthedocs.org/projects/anyio/badge/?version=latest
:target: https://anyio.readthedocs.io/en/latest/?badge=latest
:alt: Documentation
.. image:: https://badges.gitter.im/gitterHQ/gitter.svg
:target: https://gitter.im/python-trio/AnyIO
:alt: Gitter chat
AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio_ or
trio_. It implements trio-like `structured concurrency`_ (SC) on top of asyncio, and works in harmony
with the native SC of trio itself.
Applications and libraries written against AnyIO's API will run unmodified on either asyncio_ or
trio_. AnyIO can also be adopted into a library or application incrementally bit by bit, no full
refactoring necessary. It will blend in with native libraries of your chosen backend.
Documentation
-------------
View full documentation at: https://anyio.readthedocs.io/
Features
--------
AnyIO offers the following functionality:
* Task groups (nurseries_ in trio terminology)
* High level networking (TCP, UDP and UNIX sockets)
* `Happy eyeballs`_ algorithm for TCP connections (more robust than that of asyncio on Python
3.8)
* async/await style UDP sockets (unlike asyncio where you still have to use Transports and
Protocols)
* A versatile API for byte streams and object streams
* Inter-task synchronization and communication (locks, conditions, events, semaphores, object
streams)
* Worker threads
* Subprocesses
* Asynchronous file I/O (using worker threads)
* Signal handling
AnyIO also comes with its own pytest_ plugin which also supports asynchronous fixtures.
It even works with the popular Hypothesis_ library.
.. _asyncio: https://docs.python.org/3/library/asyncio.html
.. _trio: https://github.com/python-trio/trio
.. _structured concurrency: https://en.wikipedia.org/wiki/Structured_concurrency
.. _nurseries: https://trio.readthedocs.io/en/stable/reference-core.html#nurseries-and-spawning
.. _Happy eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs
.. _pytest: https://docs.pytest.org/en/latest/
.. _Hypothesis: https://hypothesis.works/

@ -0,0 +1,82 @@
anyio-3.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
anyio-3.6.1.dist-info/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081
anyio-3.6.1.dist-info/METADATA,sha256=cXu3CLppFqT_rBl4Eo2HOb0J1zm6Ltu_tMFuzjuQnew,4654
anyio-3.6.1.dist-info/RECORD,,
anyio-3.6.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
anyio-3.6.1.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39
anyio-3.6.1.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6
anyio/__init__.py,sha256=M2R8dk6L5gL5lXHArzpSfEn2oH5jMyUKhzyrkRiv2AM,4037
anyio/__pycache__/__init__.cpython-37.pyc,,
anyio/__pycache__/from_thread.cpython-37.pyc,,
anyio/__pycache__/lowlevel.cpython-37.pyc,,
anyio/__pycache__/pytest_plugin.cpython-37.pyc,,
anyio/__pycache__/to_process.cpython-37.pyc,,
anyio/__pycache__/to_thread.cpython-37.pyc,,
anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
anyio/_backends/__pycache__/__init__.cpython-37.pyc,,
anyio/_backends/__pycache__/_asyncio.cpython-37.pyc,,
anyio/_backends/__pycache__/_trio.cpython-37.pyc,,
anyio/_backends/_asyncio.py,sha256=ZJDvRwfS4wv9WWcqWledNJyl8hx8A8-m9-gSKAJ6nBM,69238
anyio/_backends/_trio.py,sha256=CebCaqr8Szi6uCnUzwtBRLfUitR5OnDT_wfH-KiqvBQ,29696
anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
anyio/_core/__pycache__/__init__.cpython-37.pyc,,
anyio/_core/__pycache__/_compat.cpython-37.pyc,,
anyio/_core/__pycache__/_eventloop.cpython-37.pyc,,
anyio/_core/__pycache__/_exceptions.cpython-37.pyc,,
anyio/_core/__pycache__/_fileio.cpython-37.pyc,,
anyio/_core/__pycache__/_resources.cpython-37.pyc,,
anyio/_core/__pycache__/_signals.cpython-37.pyc,,
anyio/_core/__pycache__/_sockets.cpython-37.pyc,,
anyio/_core/__pycache__/_streams.cpython-37.pyc,,
anyio/_core/__pycache__/_subprocesses.cpython-37.pyc,,
anyio/_core/__pycache__/_synchronization.cpython-37.pyc,,
anyio/_core/__pycache__/_tasks.cpython-37.pyc,,
anyio/_core/__pycache__/_testing.cpython-37.pyc,,
anyio/_core/__pycache__/_typedattr.cpython-37.pyc,,
anyio/_core/_compat.py,sha256=X99W70r-O-JLdkKNtbddcIY5H2Nyg3Nk34oUYE9WZRs,5790
anyio/_core/_eventloop.py,sha256=DRn_hy679LtsJFsPX7dXjDv72bLtSFkTnWY9WVVfgCQ,4108
anyio/_core/_exceptions.py,sha256=1wqraNldZroYkoyB0HZStAruz_7yDCBaW-4zYwsKj8s,2904
anyio/_core/_fileio.py,sha256=au82uZXZX4fia8EoZq_E-JDwZFKe6ZtI0J6IkxK8FmQ,18298
anyio/_core/_resources.py,sha256=M_uN-90N8eSsWuvo-0xluWU_OG2BTyccAgsQ7XtHxzs,399
anyio/_core/_signals.py,sha256=D4btJN527tAADspKBeNKaCds-ZcEZJP8LWM_MjVuQRA,827
anyio/_core/_sockets.py,sha256=fW_Cbg6kfw4xgYuVuWbcWrAYspOcDSEjwxVATMzf2fo,19820
anyio/_core/_streams.py,sha256=gjT5xChJ1OoV8nNinljSv1yW4nqUS-QzZzIydQz3exQ,1494
anyio/_core/_subprocesses.py,sha256=pcchMI2OII0QSjiVxRiTEz4M0B7TlQPzGurfCuka-xc,5049
anyio/_core/_synchronization.py,sha256=xOOG4hF9783N6E2IcD3YKiukguA5bPrj6BodDsKNaJY,16822
anyio/_core/_tasks.py,sha256=ebGLjHvwL6I9aGyPwvCig1drebSVYFzvY3pnN3TsB4o,5273
anyio/_core/_testing.py,sha256=VZka_yebIhJ6mJ6Vo_ilO3Nbz53ieqg0WBijwciMwdY,2196
anyio/_core/_typedattr.py,sha256=k5-wBvMlDlKHIpn18INVnXAlGwI3CrAvPmWoceHjnOQ,2534
anyio/abc/__init__.py,sha256=hMa47CMs5O1twC2bBcSbzwX-3Q08BAgAPTRekQobb3E,2123
anyio/abc/__pycache__/__init__.cpython-37.pyc,,
anyio/abc/__pycache__/_resources.cpython-37.pyc,,
anyio/abc/__pycache__/_sockets.cpython-37.pyc,,
anyio/abc/__pycache__/_streams.cpython-37.pyc,,
anyio/abc/__pycache__/_subprocesses.cpython-37.pyc,,
anyio/abc/__pycache__/_tasks.cpython-37.pyc,,
anyio/abc/__pycache__/_testing.cpython-37.pyc,,
anyio/abc/_resources.py,sha256=js737mWPG6IW0fH8W4Tz9eNWLztse7dKxEC61z934Vk,752
anyio/abc/_sockets.py,sha256=i1VdcJTLAuRlYeZoL6s5RBSWbX62Cu6ln5YZBL2YrWk,5754
anyio/abc/_streams.py,sha256=0g70fhKAzbnK0KKmWwRgwmKdApBwduAcVj4TpjSzjzU,6501
anyio/abc/_subprocesses.py,sha256=iREP_YQ91it88lDU4XIcI3HZ9HUvV5UmjQk_sSPonrw,2071
anyio/abc/_tasks.py,sha256=mQQd1DANqpySKyehVVPdMfi_UEG49zZUJpt5blunOjg,3119
anyio/abc/_testing.py,sha256=ifKCUPzcQdHAEGO-weu2GQvzjMQPPIWO24mQ0z6zkdU,1928
anyio/from_thread.py,sha256=nSq6mafYMqwxKmzdJyISg8cp-AyBj9rxZPMt_b7klSM,16497
anyio/lowlevel.py,sha256=W4ydshns7f86YuSESFc2igTf46AWMXnGPQGsY_Esl2E,4679
anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
anyio/pytest_plugin.py,sha256=kWj2B8BJehePJd1sztRBmJBRh8O4hk1oGSYQRlX5Gr8,5134
anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
anyio/streams/__pycache__/__init__.cpython-37.pyc,,
anyio/streams/__pycache__/buffered.cpython-37.pyc,,
anyio/streams/__pycache__/file.cpython-37.pyc,,
anyio/streams/__pycache__/memory.cpython-37.pyc,,
anyio/streams/__pycache__/stapled.cpython-37.pyc,,
anyio/streams/__pycache__/text.cpython-37.pyc,,
anyio/streams/__pycache__/tls.cpython-37.pyc,,
anyio/streams/buffered.py,sha256=FegOSO4Xcxa5SaDfU1A3ZkTTxaPrv6G435Y_giZ8k44,4437
anyio/streams/file.py,sha256=pujJ-m6BX-gOLnVoZwkE5kh-YDs5Vx9eJFVkvliQ0S4,4353
anyio/streams/memory.py,sha256=3RGeZoevoGIgBWfD2_X1cqxIPOz-BqQkRf6lUcOnBYc,9209
anyio/streams/stapled.py,sha256=0E0V15v8M5GVelpHe5RT0S33tQ9hGe4ZCXo_KJEjtt4,4258
anyio/streams/text.py,sha256=WRFyjsRpBjQKdCmR4ZuzYTEAJqGx2s5oTJmGI1C6Ng0,5014
anyio/streams/tls.py,sha256=-WXGsMV14XHXAxc38WpBvGusjuY7e449g4UCEHIlnWw,12040
anyio/to_process.py,sha256=hu0ES3HJC-VEjcdPJMzAzjyTaekaCNToO3coj3jvnus,9247
anyio/to_thread.py,sha256=VeMQoo8Va2zz0WFk2p123QikDpqk2wYZGw20COC3wqw,2124

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.37.1)
Root-Is-Purelib: true
Tag: py3-none-any

@ -0,0 +1,167 @@
__all__ = (
"maybe_async",
"maybe_async_cm",
"run",
"sleep",
"sleep_forever",
"sleep_until",
"current_time",
"get_all_backends",
"get_cancelled_exc_class",
"BrokenResourceError",
"BrokenWorkerProcess",
"BusyResourceError",
"ClosedResourceError",
"DelimiterNotFound",
"EndOfStream",
"ExceptionGroup",
"IncompleteRead",
"TypedAttributeLookupError",
"WouldBlock",
"AsyncFile",
"Path",
"open_file",
"wrap_file",
"aclose_forcefully",
"open_signal_receiver",
"connect_tcp",
"connect_unix",
"create_tcp_listener",
"create_unix_listener",
"create_udp_socket",
"create_connected_udp_socket",
"getaddrinfo",
"getnameinfo",
"wait_socket_readable",
"wait_socket_writable",
"create_memory_object_stream",
"run_process",
"open_process",
"create_lock",
"CapacityLimiter",
"CapacityLimiterStatistics",
"Condition",
"ConditionStatistics",
"Event",
"EventStatistics",
"Lock",
"LockStatistics",
"Semaphore",
"SemaphoreStatistics",
"create_condition",
"create_event",
"create_semaphore",
"create_capacity_limiter",
"open_cancel_scope",
"fail_after",
"move_on_after",
"current_effective_deadline",
"TASK_STATUS_IGNORED",
"CancelScope",
"create_task_group",
"TaskInfo",
"get_current_task",
"get_running_tasks",
"wait_all_tasks_blocked",
"run_sync_in_worker_thread",
"run_async_from_thread",
"run_sync_from_thread",
"current_default_worker_thread_limiter",
"create_blocking_portal",
"start_blocking_portal",
"typed_attribute",
"TypedAttributeSet",
"TypedAttributeProvider",
)
from typing import Any
from ._core._compat import maybe_async, maybe_async_cm
from ._core._eventloop import (
current_time,
get_all_backends,
get_cancelled_exc_class,
run,
sleep,
sleep_forever,
sleep_until,
)
from ._core._exceptions import (
BrokenResourceError,
BrokenWorkerProcess,
BusyResourceError,
ClosedResourceError,
DelimiterNotFound,
EndOfStream,
ExceptionGroup,
IncompleteRead,
TypedAttributeLookupError,
WouldBlock,
)
from ._core._fileio import AsyncFile, Path, open_file, wrap_file
from ._core._resources import aclose_forcefully
from ._core._signals import open_signal_receiver
from ._core._sockets import (
connect_tcp,
connect_unix,
create_connected_udp_socket,
create_tcp_listener,
create_udp_socket,
create_unix_listener,
getaddrinfo,
getnameinfo,
wait_socket_readable,
wait_socket_writable,
)
from ._core._streams import create_memory_object_stream
from ._core._subprocesses import open_process, run_process
from ._core._synchronization import (
CapacityLimiter,
CapacityLimiterStatistics,
Condition,
ConditionStatistics,
Event,
EventStatistics,
Lock,
LockStatistics,
Semaphore,
SemaphoreStatistics,
create_capacity_limiter,
create_condition,
create_event,
create_lock,
create_semaphore,
)
from ._core._tasks import (
TASK_STATUS_IGNORED,
CancelScope,
create_task_group,
current_effective_deadline,
fail_after,
move_on_after,
open_cancel_scope,
)
from ._core._testing import (
TaskInfo,
get_current_task,
get_running_tasks,
wait_all_tasks_blocked,
)
from ._core._typedattr import TypedAttributeProvider, TypedAttributeSet, typed_attribute
# Re-exported here, for backwards compatibility
# isort: off
from .to_thread import current_default_worker_thread_limiter, run_sync_in_worker_thread
from .from_thread import (
create_blocking_portal,
run_async_from_thread,
run_sync_from_thread,
start_blocking_portal,
)
# Re-export imports so they look like they live directly in this package
key: str
value: Any
for key, value in list(locals().items()):
if getattr(value, "__module__", "").startswith("anyio."):
value.__module__ = __name__

@ -0,0 +1,988 @@
import array
import math
import socket
from concurrent.futures import Future
from contextvars import copy_context
from dataclasses import dataclass
from functools import partial
from io import IOBase
from os import PathLike
from signal import Signals
from types import TracebackType
from typing import (
IO,
TYPE_CHECKING,
Any,
AsyncGenerator,
Awaitable,
Callable,
Collection,
ContextManager,
Coroutine,
Deque,
Dict,
Generic,
Iterable,
List,
Mapping,
NoReturn,
Optional,
Sequence,
Set,
Tuple,
Type,
TypeVar,
Union,
cast,
)
import sniffio
import trio.from_thread
from outcome import Error, Outcome, Value
from trio.socket import SocketType as TrioSocketType
from trio.to_thread import run_sync
from .. import CapacityLimiterStatistics, EventStatistics, TaskInfo, abc
from .._core._compat import DeprecatedAsyncContextManager, DeprecatedAwaitable, T
from .._core._eventloop import claim_worker_thread
from .._core._exceptions import (
BrokenResourceError,
BusyResourceError,
ClosedResourceError,
EndOfStream,
)
from .._core._exceptions import ExceptionGroup as BaseExceptionGroup
from .._core._sockets import convert_ipv6_sockaddr
from .._core._synchronization import CapacityLimiter as BaseCapacityLimiter
from .._core._synchronization import Event as BaseEvent
from .._core._synchronization import ResourceGuard
from .._core._tasks import CancelScope as BaseCancelScope
from ..abc import IPSockAddrType, UDPPacketType
if TYPE_CHECKING:
from trio_typing import TaskStatus
try:
from trio import lowlevel as trio_lowlevel
except ImportError:
from trio import hazmat as trio_lowlevel # type: ignore[no-redef]
from trio.hazmat import wait_readable, wait_writable
else:
from trio.lowlevel import wait_readable, wait_writable
try:
trio_open_process = trio_lowlevel.open_process # type: ignore[attr-defined]
except AttributeError:
from trio import open_process as trio_open_process
T_Retval = TypeVar("T_Retval")
T_SockAddr = TypeVar("T_SockAddr", str, IPSockAddrType)
#
# Event loop
#
run = trio.run
current_token = trio.lowlevel.current_trio_token
RunVar = trio.lowlevel.RunVar
#
# Miscellaneous
#
sleep = trio.sleep
#
# Timeouts and cancellation
#
class CancelScope(BaseCancelScope):
def __new__(
cls, original: Optional[trio.CancelScope] = None, **kwargs: object
) -> "CancelScope":
return object.__new__(cls)
def __init__(
self, original: Optional[trio.CancelScope] = None, **kwargs: Any
) -> None:
self.__original = original or trio.CancelScope(**kwargs)
def __enter__(self) -> "CancelScope":
self.__original.__enter__()
return self
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> Optional[bool]:
return self.__original.__exit__(exc_type, exc_val, exc_tb)
def cancel(self) -> DeprecatedAwaitable:
self.__original.cancel()
return DeprecatedAwaitable(self.cancel)
@property
def deadline(self) -> float:
return self.__original.deadline
@deadline.setter
def deadline(self, value: float) -> None:
self.__original.deadline = value
@property
def cancel_called(self) -> bool:
return self.__original.cancel_called
@property
def shield(self) -> bool:
return self.__original.shield
@shield.setter
def shield(self, value: bool) -> None:
self.__original.shield = value
CancelledError = trio.Cancelled
checkpoint = trio.lowlevel.checkpoint
checkpoint_if_cancelled = trio.lowlevel.checkpoint_if_cancelled
cancel_shielded_checkpoint = trio.lowlevel.cancel_shielded_checkpoint
current_effective_deadline = trio.current_effective_deadline
current_time = trio.current_time
#
# Task groups
#
class ExceptionGroup(BaseExceptionGroup, trio.MultiError):
pass
class TaskGroup(abc.TaskGroup):
def __init__(self) -> None:
self._active = False
self._nursery_manager = trio.open_nursery()
self.cancel_scope = None # type: ignore[assignment]
async def __aenter__(self) -> "TaskGroup":
self._active = True
self._nursery = await self._nursery_manager.__aenter__()
self.cancel_scope = CancelScope(self._nursery.cancel_scope)
return self
async def __aexit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> Optional[bool]:
try:
return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb)
except trio.MultiError as exc:
raise ExceptionGroup(exc.exceptions) from None
finally:
self._active = False
def start_soon(self, func: Callable, *args: object, name: object = None) -> None:
if not self._active:
raise RuntimeError(
"This task group is not active; no new tasks can be started."
)
self._nursery.start_soon(func, *args, name=name)
async def start(
self, func: Callable[..., Coroutine], *args: object, name: object = None
) -> object:
if not self._active:
raise RuntimeError(
"This task group is not active; no new tasks can be started."
)
return await self._nursery.start(func, *args, name=name)
#
# Threads
#
async def run_sync_in_worker_thread(
func: Callable[..., T_Retval],
*args: object,
cancellable: bool = False,
limiter: Optional[trio.CapacityLimiter] = None,
) -> T_Retval:
def wrapper() -> T_Retval:
with claim_worker_thread("trio"):
return func(*args)
# TODO: remove explicit context copying when trio 0.20 is the minimum requirement
context = copy_context()
context.run(sniffio.current_async_library_cvar.set, None)
return await run_sync(
context.run, wrapper, cancellable=cancellable, limiter=limiter
)
# TODO: remove this workaround when trio 0.20 is the minimum requirement
def run_async_from_thread(
fn: Callable[..., Awaitable[T_Retval]], *args: Any
) -> T_Retval:
async def wrapper() -> T_Retval:
retval: T_Retval
async def inner() -> None:
nonlocal retval
__tracebackhide__ = True
retval = await fn(*args)
async with trio.open_nursery() as n:
context.run(n.start_soon, inner)
__tracebackhide__ = True
return retval
context = copy_context()
context.run(sniffio.current_async_library_cvar.set, "trio")
return trio.from_thread.run(wrapper)
def run_sync_from_thread(fn: Callable[..., T_Retval], *args: Any) -> T_Retval:
# TODO: remove explicit context copying when trio 0.20 is the minimum requirement
retval = trio.from_thread.run_sync(copy_context().run, fn, *args)
return cast(T_Retval, retval)
class BlockingPortal(abc.BlockingPortal):
def __new__(cls) -> "BlockingPortal":
return object.__new__(cls)
def __init__(self) -> None:
super().__init__()
self._token = trio.lowlevel.current_trio_token()
def _spawn_task_from_thread(
self,
func: Callable,
args: tuple,
kwargs: Dict[str, Any],
name: object,
future: Future,
) -> None:
context = copy_context()
context.run(sniffio.current_async_library_cvar.set, "trio")
trio.from_thread.run_sync(
context.run,
partial(self._task_group.start_soon, name=name),
self._call_func,
func,
args,
kwargs,
future,
trio_token=self._token,
)
#
# Subprocesses
#
@dataclass(eq=False)
class ReceiveStreamWrapper(abc.ByteReceiveStream):
_stream: trio.abc.ReceiveStream
async def receive(self, max_bytes: Optional[int] = None) -> bytes:
try:
data = await self._stream.receive_some(max_bytes)
except trio.ClosedResourceError as exc:
raise ClosedResourceError from exc.__cause__
except trio.BrokenResourceError as exc:
raise BrokenResourceError from exc.__cause__
if data:
return data
else:
raise EndOfStream
async def aclose(self) -> None:
await self._stream.aclose()
@dataclass(eq=False)
class SendStreamWrapper(abc.ByteSendStream):
_stream: trio.abc.SendStream
async def send(self, item: bytes) -> None:
try:
await self._stream.send_all(item)
except trio.ClosedResourceError as exc:
raise ClosedResourceError from exc.__cause__
except trio.BrokenResourceError as exc:
raise BrokenResourceError from exc.__cause__
async def aclose(self) -> None:
await self._stream.aclose()
@dataclass(eq=False)
class Process(abc.Process):
_process: trio.Process
_stdin: Optional[abc.ByteSendStream]
_stdout: Optional[abc.ByteReceiveStream]
_stderr: Optional[abc.ByteReceiveStream]
async def aclose(self) -> None:
if self._stdin:
await self._stdin.aclose()
if self._stdout:
await self._stdout.aclose()
if self._stderr:
await self._stderr.aclose()
await self.wait()
async def wait(self) -> int:
return await self._process.wait()
def terminate(self) -> None:
self._process.terminate()
def kill(self) -> None:
self._process.kill()
def send_signal(self, signal: Signals) -> None:
self._process.send_signal(signal)
@property
def pid(self) -> int:
return self._process.pid
@property
def returncode(self) -> Optional[int]:
return self._process.returncode
@property
def stdin(self) -> Optional[abc.ByteSendStream]:
return self._stdin
@property
def stdout(self) -> Optional[abc.ByteReceiveStream]:
return self._stdout
@property
def stderr(self) -> Optional[abc.ByteReceiveStream]:
return self._stderr
async def open_process(
command: Union[str, bytes, Sequence[Union[str, bytes]]],
*,
shell: bool,
stdin: Union[int, IO[Any], None],
stdout: Union[int, IO[Any], None],
stderr: Union[int, IO[Any], None],
cwd: Union[str, bytes, PathLike, None] = None,
env: Optional[Mapping[str, str]] = None,
start_new_session: bool = False,
) -> Process:
process = await trio_open_process(
command,
stdin=stdin,
stdout=stdout,
stderr=stderr,
shell=shell,
cwd=cwd,
env=env,
start_new_session=start_new_session,
)
stdin_stream = SendStreamWrapper(process.stdin) if process.stdin else None
stdout_stream = ReceiveStreamWrapper(process.stdout) if process.stdout else None
stderr_stream = ReceiveStreamWrapper(process.stderr) if process.stderr else None
return Process(process, stdin_stream, stdout_stream, stderr_stream)
class _ProcessPoolShutdownInstrument(trio.abc.Instrument):
def after_run(self) -> None:
super().after_run()
current_default_worker_process_limiter: RunVar = RunVar(
"current_default_worker_process_limiter"
)
async def _shutdown_process_pool(workers: Set[Process]) -> None:
process: Process
try:
await sleep(math.inf)
except trio.Cancelled:
for process in workers:
if process.returncode is None:
process.kill()
with CancelScope(shield=True):
for process in workers:
await process.aclose()
def setup_process_pool_exit_at_shutdown(workers: Set[Process]) -> None:
trio.lowlevel.spawn_system_task(_shutdown_process_pool, workers)
#
# Sockets and networking
#
class _TrioSocketMixin(Generic[T_SockAddr]):
def __init__(self, trio_socket: TrioSocketType) -> None:
self._trio_socket = trio_socket
self._closed = False
def _check_closed(self) -> None:
if self._closed:
raise ClosedResourceError
if self._trio_socket.fileno() < 0:
raise BrokenResourceError
@property
def _raw_socket(self) -> socket.socket:
return self._trio_socket._sock # type: ignore[attr-defined]
async def aclose(self) -> None:
if self._trio_socket.fileno() >= 0:
self._closed = True
self._trio_socket.close()
def _convert_socket_error(self, exc: BaseException) -> "NoReturn":
if isinstance(exc, trio.ClosedResourceError):
raise ClosedResourceError from exc
elif self._trio_socket.fileno() < 0 and self._closed:
raise ClosedResourceError from None
elif isinstance(exc, OSError):
raise BrokenResourceError from exc
else:
raise exc
class SocketStream(_TrioSocketMixin, abc.SocketStream):
def __init__(self, trio_socket: TrioSocketType) -> None:
super().__init__(trio_socket)
self._receive_guard = ResourceGuard("reading from")
self._send_guard = ResourceGuard("writing to")
async def receive(self, max_bytes: int = 65536) -> bytes:
with self._receive_guard:
try:
data = await self._trio_socket.recv(max_bytes)
except BaseException as exc:
self._convert_socket_error(exc)
if data:
return data
else:
raise EndOfStream
async def send(self, item: bytes) -> None:
with self._send_guard:
view = memoryview(item)
while view:
try:
bytes_sent = await self._trio_socket.send(view)
except BaseException as exc:
self._convert_socket_error(exc)
view = view[bytes_sent:]
async def send_eof(self) -> None:
self._trio_socket.shutdown(socket.SHUT_WR)
class UNIXSocketStream(SocketStream, abc.UNIXSocketStream):
async def receive_fds(self, msglen: int, maxfds: int) -> Tuple[bytes, List[int]]:
if not isinstance(msglen, int) or msglen < 0:
raise ValueError("msglen must be a non-negative integer")
if not isinstance(maxfds, int) or maxfds < 1:
raise ValueError("maxfds must be a positive integer")
fds = array.array("i")
await checkpoint()
with self._receive_guard:
while True:
try:
message, ancdata, flags, addr = await self._trio_socket.recvmsg(
msglen, socket.CMSG_LEN(maxfds * fds.itemsize)
)
except BaseException as exc:
self._convert_socket_error(exc)
else:
if not message and not ancdata:
raise EndOfStream
break
for cmsg_level, cmsg_type, cmsg_data in ancdata:
if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS:
raise RuntimeError(
f"Received unexpected ancillary data; message = {message!r}, "
f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}"
)
fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
return message, list(fds)
async def send_fds(
self, message: bytes, fds: Collection[Union[int, IOBase]]
) -> None:
if not message:
raise ValueError("message must not be empty")
if not fds:
raise ValueError("fds must not be empty")
filenos: List[int] = []
for fd in fds:
if isinstance(fd, int):
filenos.append(fd)
elif isinstance(fd, IOBase):
filenos.append(fd.fileno())
fdarray = array.array("i", filenos)
await checkpoint()
with self._send_guard:
while True:
try:
await self._trio_socket.sendmsg(
[message],
[
(
socket.SOL_SOCKET,
socket.SCM_RIGHTS, # type: ignore[list-item]
fdarray,
)
],
)
break
except BaseException as exc:
self._convert_socket_error(exc)
class TCPSocketListener(_TrioSocketMixin, abc.SocketListener):
def __init__(self, raw_socket: socket.socket):
super().__init__(trio.socket.from_stdlib_socket(raw_socket))
self._accept_guard = ResourceGuard("accepting connections from")
async def accept(self) -> SocketStream:
with self._accept_guard:
try:
trio_socket, _addr = await self._trio_socket.accept()
except BaseException as exc:
self._convert_socket_error(exc)
trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
return SocketStream(trio_socket)
class UNIXSocketListener(_TrioSocketMixin, abc.SocketListener):
def __init__(self, raw_socket: socket.socket):
super().__init__(trio.socket.from_stdlib_socket(raw_socket))
self._accept_guard = ResourceGuard("accepting connections from")
async def accept(self) -> UNIXSocketStream:
with self._accept_guard:
try:
trio_socket, _addr = await self._trio_socket.accept()
except BaseException as exc:
self._convert_socket_error(exc)
return UNIXSocketStream(trio_socket)
class UDPSocket(_TrioSocketMixin[IPSockAddrType], abc.UDPSocket):
def __init__(self, trio_socket: TrioSocketType) -> None:
super().__init__(trio_socket)
self._receive_guard = ResourceGuard("reading from")
self._send_guard = ResourceGuard("writing to")
async def receive(self) -> Tuple[bytes, IPSockAddrType]:
with self._receive_guard:
try:
data, addr = await self._trio_socket.recvfrom(65536)
return data, convert_ipv6_sockaddr(addr)
except BaseException as exc:
self._convert_socket_error(exc)
async def send(self, item: UDPPacketType) -> None:
with self._send_guard:
try:
await self._trio_socket.sendto(*item)
except BaseException as exc:
self._convert_socket_error(exc)
class ConnectedUDPSocket(_TrioSocketMixin[IPSockAddrType], abc.ConnectedUDPSocket):
def __init__(self, trio_socket: TrioSocketType) -> None:
super().__init__(trio_socket)
self._receive_guard = ResourceGuard("reading from")
self._send_guard = ResourceGuard("writing to")
async def receive(self) -> bytes:
with self._receive_guard:
try:
return await self._trio_socket.recv(65536)
except BaseException as exc:
self._convert_socket_error(exc)
async def send(self, item: bytes) -> None:
with self._send_guard:
try:
await self._trio_socket.send(item)
except BaseException as exc:
self._convert_socket_error(exc)
async def connect_tcp(
host: str, port: int, local_address: Optional[IPSockAddrType] = None
) -> SocketStream:
family = socket.AF_INET6 if ":" in host else socket.AF_INET
trio_socket = trio.socket.socket(family)
trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
if local_address:
await trio_socket.bind(local_address)
try:
await trio_socket.connect((host, port))
except BaseException:
trio_socket.close()
raise
return SocketStream(trio_socket)
async def connect_unix(path: str) -> UNIXSocketStream:
trio_socket = trio.socket.socket(socket.AF_UNIX)
try:
await trio_socket.connect(path)
except BaseException:
trio_socket.close()
raise
return UNIXSocketStream(trio_socket)
async def create_udp_socket(
family: socket.AddressFamily,
local_address: Optional[IPSockAddrType],
remote_address: Optional[IPSockAddrType],
reuse_port: bool,
) -> Union[UDPSocket, ConnectedUDPSocket]:
trio_socket = trio.socket.socket(family=family, type=socket.SOCK_DGRAM)
if reuse_port:
trio_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
if local_address:
await trio_socket.bind(local_address)
if remote_address:
await trio_socket.connect(remote_address)
return ConnectedUDPSocket(trio_socket)
else:
return UDPSocket(trio_socket)
getaddrinfo = trio.socket.getaddrinfo
getnameinfo = trio.socket.getnameinfo
async def wait_socket_readable(sock: socket.socket) -> None:
try:
await wait_readable(sock)
except trio.ClosedResourceError as exc:
raise ClosedResourceError().with_traceback(exc.__traceback__) from None
except trio.BusyResourceError:
raise BusyResourceError("reading from") from None
async def wait_socket_writable(sock: socket.socket) -> None:
try:
await wait_writable(sock)
except trio.ClosedResourceError as exc:
raise ClosedResourceError().with_traceback(exc.__traceback__) from None
except trio.BusyResourceError:
raise BusyResourceError("writing to") from None
#
# Synchronization
#
class Event(BaseEvent):
def __new__(cls) -> "Event":
return object.__new__(cls)
def __init__(self) -> None:
self.__original = trio.Event()
def is_set(self) -> bool:
return self.__original.is_set()
async def wait(self) -> None:
return await self.__original.wait()
def statistics(self) -> EventStatistics:
orig_statistics = self.__original.statistics()
return EventStatistics(tasks_waiting=orig_statistics.tasks_waiting)
def set(self) -> DeprecatedAwaitable:
self.__original.set()
return DeprecatedAwaitable(self.set)
class CapacityLimiter(BaseCapacityLimiter):
def __new__(cls, *args: object, **kwargs: object) -> "CapacityLimiter":
return object.__new__(cls)
def __init__(
self, *args: Any, original: Optional[trio.CapacityLimiter] = None
) -> None:
self.__original = original or trio.CapacityLimiter(*args)
async def __aenter__(self) -> None:
return await self.__original.__aenter__()
async def __aexit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> Optional[bool]:
return await self.__original.__aexit__(exc_type, exc_val, exc_tb)
@property
def total_tokens(self) -> float:
return self.__original.total_tokens
@total_tokens.setter
def total_tokens(self, value: float) -> None:
self.__original.total_tokens = value
@property
def borrowed_tokens(self) -> int:
return self.__original.borrowed_tokens
@property
def available_tokens(self) -> float:
return self.__original.available_tokens
def acquire_nowait(self) -> DeprecatedAwaitable:
self.__original.acquire_nowait()
return DeprecatedAwaitable(self.acquire_nowait)
def acquire_on_behalf_of_nowait(self, borrower: object) -> DeprecatedAwaitable:
self.__original.acquire_on_behalf_of_nowait(borrower)
return DeprecatedAwaitable(self.acquire_on_behalf_of_nowait)
async def acquire(self) -> None:
await self.__original.acquire()
async def acquire_on_behalf_of(self, borrower: object) -> None:
await self.__original.acquire_on_behalf_of(borrower)
def release(self) -> None:
return self.__original.release()
def release_on_behalf_of(self, borrower: object) -> None:
return self.__original.release_on_behalf_of(borrower)
def statistics(self) -> CapacityLimiterStatistics:
orig = self.__original.statistics()
return CapacityLimiterStatistics(
borrowed_tokens=orig.borrowed_tokens,
total_tokens=orig.total_tokens,
borrowers=orig.borrowers,
tasks_waiting=orig.tasks_waiting,
)
_capacity_limiter_wrapper: RunVar = RunVar("_capacity_limiter_wrapper")
def current_default_thread_limiter() -> CapacityLimiter:
try:
return _capacity_limiter_wrapper.get()
except LookupError:
limiter = CapacityLimiter(
original=trio.to_thread.current_default_thread_limiter()
)
_capacity_limiter_wrapper.set(limiter)
return limiter
#
# Signal handling
#
class _SignalReceiver(DeprecatedAsyncContextManager[T]):
def __init__(self, cm: ContextManager[T]):
self._cm = cm
def __enter__(self) -> T:
return self._cm.__enter__()
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> Optional[bool]:
return self._cm.__exit__(exc_type, exc_val, exc_tb)
def open_signal_receiver(*signals: Signals) -> _SignalReceiver:
cm = trio.open_signal_receiver(*signals)
return _SignalReceiver(cm)
#
# Testing and debugging
#
def get_current_task() -> TaskInfo:
task = trio_lowlevel.current_task()
parent_id = None
if task.parent_nursery and task.parent_nursery.parent_task:
parent_id = id(task.parent_nursery.parent_task)
return TaskInfo(id(task), parent_id, task.name, task.coro)
def get_running_tasks() -> List[TaskInfo]:
root_task = trio_lowlevel.current_root_task()
task_infos = [TaskInfo(id(root_task), None, root_task.name, root_task.coro)]
nurseries = root_task.child_nurseries
while nurseries:
new_nurseries: List[trio.Nursery] = []
for nursery in nurseries:
for task in nursery.child_tasks:
task_infos.append(
TaskInfo(id(task), id(nursery.parent_task), task.name, task.coro)
)
new_nurseries.extend(task.child_nurseries)
nurseries = new_nurseries
return task_infos
def wait_all_tasks_blocked() -> Awaitable[None]:
import trio.testing
return trio.testing.wait_all_tasks_blocked()
class TestRunner(abc.TestRunner):
def __init__(self, **options: Any) -> None:
from collections import deque
from queue import Queue
self._call_queue: "Queue[Callable[..., object]]" = Queue()
self._result_queue: Deque[Outcome] = deque()
self._stop_event: Optional[trio.Event] = None
self._nursery: Optional[trio.Nursery] = None
self._options = options
async def _trio_main(self) -> None:
self._stop_event = trio.Event()
async with trio.open_nursery() as self._nursery:
await self._stop_event.wait()
async def _call_func(
self, func: Callable[..., Awaitable[object]], args: tuple, kwargs: dict
) -> None:
try:
retval = await func(*args, **kwargs)
except BaseException as exc:
self._result_queue.append(Error(exc))
else:
self._result_queue.append(Value(retval))
def _main_task_finished(self, outcome: object) -> None:
self._nursery = None
def _get_nursery(self) -> trio.Nursery:
if self._nursery is None:
trio.lowlevel.start_guest_run(
self._trio_main,
run_sync_soon_threadsafe=self._call_queue.put,
done_callback=self._main_task_finished,
**self._options,
)
while self._nursery is None:
self._call_queue.get()()
return self._nursery
def _call(
self, func: Callable[..., Awaitable[T_Retval]], *args: object, **kwargs: object
) -> T_Retval:
self._get_nursery().start_soon(self._call_func, func, args, kwargs)
while not self._result_queue:
self._call_queue.get()()
outcome = self._result_queue.pop()
return outcome.unwrap()
def close(self) -> None:
if self._stop_event:
self._stop_event.set()
while self._nursery is not None:
self._call_queue.get()()
def run_asyncgen_fixture(
self,
fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]],
kwargs: Dict[str, Any],
) -> Iterable[T_Retval]:
async def fixture_runner(*, task_status: "TaskStatus") -> None:
agen = fixture_func(**kwargs)
retval = await agen.asend(None)
task_status.started(retval)
await teardown_event.wait()
try:
await agen.asend(None)
except StopAsyncIteration:
pass
else:
await agen.aclose()
raise RuntimeError("Async generator fixture did not stop")
teardown_event = trio.Event()
fixture_value = self._call(lambda: self._get_nursery().start(fixture_runner))
yield fixture_value
teardown_event.set()
def run_fixture(
self,
fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]],
kwargs: Dict[str, Any],
) -> T_Retval:
return self._call(fixture_func, **kwargs)
def run_test(
self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: Dict[str, Any]
) -> None:
self._call(test_func, **kwargs)

@ -0,0 +1,218 @@
from abc import ABCMeta, abstractmethod
from contextlib import AbstractContextManager
from types import TracebackType
from typing import (
TYPE_CHECKING,
Any,
AsyncContextManager,
Callable,
ContextManager,
Generator,
Generic,
Iterable,
List,
Optional,
Tuple,
Type,
TypeVar,
Union,
overload,
)
from warnings import warn
if TYPE_CHECKING:
from ._testing import TaskInfo
else:
TaskInfo = object
T = TypeVar("T")
AnyDeprecatedAwaitable = Union[
"DeprecatedAwaitable",
"DeprecatedAwaitableFloat",
"DeprecatedAwaitableList[T]",
TaskInfo,
]
@overload
async def maybe_async(__obj: TaskInfo) -> TaskInfo:
...
@overload
async def maybe_async(__obj: "DeprecatedAwaitableFloat") -> float:
...
@overload
async def maybe_async(__obj: "DeprecatedAwaitableList[T]") -> List[T]:
...
@overload
async def maybe_async(__obj: "DeprecatedAwaitable") -> None:
...
async def maybe_async(
__obj: "AnyDeprecatedAwaitable[T]",
) -> Union[TaskInfo, float, List[T], None]:
"""
Await on the given object if necessary.
This function is intended to bridge the gap between AnyIO 2.x and 3.x where some functions and
methods were converted from coroutine functions into regular functions.
Do **not** try to use this for any other purpose!
:return: the result of awaiting on the object if coroutine, or the object itself otherwise
.. versionadded:: 2.2
"""
return __obj._unwrap()
class _ContextManagerWrapper:
def __init__(self, cm: ContextManager[T]):
self._cm = cm
async def __aenter__(self) -> T:
return self._cm.__enter__()
async def __aexit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> Optional[bool]:
return self._cm.__exit__(exc_type, exc_val, exc_tb)
def maybe_async_cm(
cm: Union[ContextManager[T], AsyncContextManager[T]]
) -> AsyncContextManager[T]:
"""
Wrap a regular context manager as an async one if necessary.
This function is intended to bridge the gap between AnyIO 2.x and 3.x where some functions and
methods were changed to return regular context managers instead of async ones.
:param cm: a regular or async context manager
:return: an async context manager
.. versionadded:: 2.2
"""
if not isinstance(cm, AbstractContextManager):
raise TypeError("Given object is not an context manager")
return _ContextManagerWrapper(cm)
def _warn_deprecation(
awaitable: "AnyDeprecatedAwaitable[Any]", stacklevel: int = 1
) -> None:
warn(
f'Awaiting on {awaitable._name}() is deprecated. Use "await '
f"anyio.maybe_async({awaitable._name}(...)) if you have to support both AnyIO 2.x "
f'and 3.x, or just remove the "await" if you are completely migrating to AnyIO 3+.',
DeprecationWarning,
stacklevel=stacklevel + 1,
)
class DeprecatedAwaitable:
def __init__(self, func: Callable[..., "DeprecatedAwaitable"]):
self._name = f"{func.__module__}.{func.__qualname__}"
def __await__(self) -> Generator[None, None, None]:
_warn_deprecation(self)
if False:
yield
def __reduce__(self) -> Tuple[Type[None], Tuple[()]]:
return type(None), ()
def _unwrap(self) -> None:
return None
class DeprecatedAwaitableFloat(float):
def __new__(
cls, x: float, func: Callable[..., "DeprecatedAwaitableFloat"]
) -> "DeprecatedAwaitableFloat":
return super().__new__(cls, x)
def __init__(self, x: float, func: Callable[..., "DeprecatedAwaitableFloat"]):
self._name = f"{func.__module__}.{func.__qualname__}"
def __await__(self) -> Generator[None, None, float]:
_warn_deprecation(self)
if False:
yield
return float(self)
def __reduce__(self) -> Tuple[Type[float], Tuple[float]]:
return float, (float(self),)
def _unwrap(self) -> float:
return float(self)
class DeprecatedAwaitableList(List[T]):
def __init__(
self,
iterable: Iterable[T] = (),
*,
func: Callable[..., "DeprecatedAwaitableList[T]"],
):
super().__init__(iterable)
self._name = f"{func.__module__}.{func.__qualname__}"
def __await__(self) -> Generator[None, None, List[T]]:
_warn_deprecation(self)
if False:
yield
return list(self)
def __reduce__(self) -> Tuple[Type[List[T]], Tuple[List[T]]]:
return list, (list(self),)
def _unwrap(self) -> List[T]:
return list(self)
class DeprecatedAsyncContextManager(Generic[T], metaclass=ABCMeta):
@abstractmethod
def __enter__(self) -> T:
pass
@abstractmethod
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> Optional[bool]:
pass
async def __aenter__(self) -> T:
warn(
f"Using {self.__class__.__name__} as an async context manager has been deprecated. "
f'Use "async with anyio.maybe_async_cm(yourcontextmanager) as foo:" if you have to '
f'support both AnyIO 2.x and 3.x, or just remove the "async" from "async with" if '
f"you are completely migrating to AnyIO 3+.",
DeprecationWarning,
)
return self.__enter__()
async def __aexit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> Optional[bool]:
return self.__exit__(exc_type, exc_val, exc_tb)

@ -0,0 +1,155 @@
import math
import sys
import threading
from contextlib import contextmanager
from importlib import import_module
from typing import (
Any,
Callable,
Coroutine,
Dict,
Generator,
Optional,
Tuple,
Type,
TypeVar,
)
import sniffio
# This must be updated when new backends are introduced
from ._compat import DeprecatedAwaitableFloat
BACKENDS = "asyncio", "trio"
T_Retval = TypeVar("T_Retval")
threadlocals = threading.local()
def run(
func: Callable[..., Coroutine[Any, Any, T_Retval]],
*args: object,
backend: str = "asyncio",
backend_options: Optional[Dict[str, Any]] = None,
) -> T_Retval:
"""
Run the given coroutine function in an asynchronous event loop.
The current thread must not be already running an event loop.
:param func: a coroutine function
:param args: positional arguments to ``func``
:param backend: name of the asynchronous event loop implementation currently either
``asyncio`` or ``trio``
:param backend_options: keyword arguments to call the backend ``run()`` implementation with
(documented :ref:`here <backend options>`)
:return: the return value of the coroutine function
:raises RuntimeError: if an asynchronous event loop is already running in this thread
:raises LookupError: if the named backend is not found
"""
try:
asynclib_name = sniffio.current_async_library()
except sniffio.AsyncLibraryNotFoundError:
pass
else:
raise RuntimeError(f"Already running {asynclib_name} in this thread")
try:
asynclib = import_module(f"..._backends._{backend}", package=__name__)
except ImportError as exc:
raise LookupError(f"No such backend: {backend}") from exc
token = None
if sniffio.current_async_library_cvar.get(None) is None:
# Since we're in control of the event loop, we can cache the name of the async library
token = sniffio.current_async_library_cvar.set(backend)
try:
backend_options = backend_options or {}
return asynclib.run(func, *args, **backend_options)
finally:
if token:
sniffio.current_async_library_cvar.reset(token)
async def sleep(delay: float) -> None:
"""
Pause the current task for the specified duration.
:param delay: the duration, in seconds
"""
return await get_asynclib().sleep(delay)
async def sleep_forever() -> None:
"""
Pause the current task until it's cancelled.
This is a shortcut for ``sleep(math.inf)``.
.. versionadded:: 3.1
"""
await sleep(math.inf)
async def sleep_until(deadline: float) -> None:
"""
Pause the current task until the given time.
:param deadline: the absolute time to wake up at (according to the internal monotonic clock of
the event loop)
.. versionadded:: 3.1
"""
now = current_time()
await sleep(max(deadline - now, 0))
def current_time() -> DeprecatedAwaitableFloat:
"""
Return the current value of the event loop's internal clock.
:return: the clock value (seconds)
"""
return DeprecatedAwaitableFloat(get_asynclib().current_time(), current_time)
def get_all_backends() -> Tuple[str, ...]:
"""Return a tuple of the names of all built-in backends."""
return BACKENDS
def get_cancelled_exc_class() -> Type[BaseException]:
"""Return the current async library's cancellation exception class."""
return get_asynclib().CancelledError
#
# Private API
#
@contextmanager
def claim_worker_thread(backend: str) -> Generator[Any, None, None]:
module = sys.modules["anyio._backends._" + backend]
threadlocals.current_async_module = module
try:
yield
finally:
del threadlocals.current_async_module
def get_asynclib(asynclib_name: Optional[str] = None) -> Any:
if asynclib_name is None:
asynclib_name = sniffio.current_async_library()
modulename = "anyio._backends._" + asynclib_name
try:
return sys.modules[modulename]
except KeyError:
return import_module(modulename)

@ -0,0 +1,93 @@
from traceback import format_exception
from typing import List
class BrokenResourceError(Exception):
"""
Raised when trying to use a resource that has been rendered unusable due to external causes
(e.g. a send stream whose peer has disconnected).
"""
class BrokenWorkerProcess(Exception):
"""
Raised by :func:`run_sync_in_process` if the worker process terminates abruptly or otherwise
misbehaves.
"""
class BusyResourceError(Exception):
"""Raised when two tasks are trying to read from or write to the same resource concurrently."""
def __init__(self, action: str):
super().__init__(f"Another task is already {action} this resource")
class ClosedResourceError(Exception):
"""Raised when trying to use a resource that has been closed."""
class DelimiterNotFound(Exception):
"""
Raised during :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the
maximum number of bytes has been read without the delimiter being found.
"""
def __init__(self, max_bytes: int) -> None:
super().__init__(
f"The delimiter was not found among the first {max_bytes} bytes"
)
class EndOfStream(Exception):
"""Raised when trying to read from a stream that has been closed from the other end."""
class ExceptionGroup(BaseException):
"""
Raised when multiple exceptions have been raised in a task group.
:var ~typing.Sequence[BaseException] exceptions: the sequence of exceptions raised together
"""
SEPARATOR = "----------------------------\n"
exceptions: List[BaseException]
def __str__(self) -> str:
tracebacks = [
"".join(format_exception(type(exc), exc, exc.__traceback__))
for exc in self.exceptions
]
return (
f"{len(self.exceptions)} exceptions were raised in the task group:\n"
f"{self.SEPARATOR}{self.SEPARATOR.join(tracebacks)}"
)
def __repr__(self) -> str:
exception_reprs = ", ".join(repr(exc) for exc in self.exceptions)
return f"<{self.__class__.__name__}: {exception_reprs}>"
class IncompleteRead(Exception):
"""
Raised during :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_exactly` or
:meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the
connection is closed before the requested amount of bytes has been read.
"""
def __init__(self) -> None:
super().__init__(
"The stream was closed before the read operation could be completed"
)
class TypedAttributeLookupError(LookupError):
"""
Raised by :meth:`~anyio.TypedAttributeProvider.extra` when the given typed attribute is not
found and no default value has been given.
"""
class WouldBlock(Exception):
"""Raised by ``X_nowait`` functions if ``X()`` would block."""

@ -0,0 +1,607 @@
import os
import pathlib
import sys
from dataclasses import dataclass
from functools import partial
from os import PathLike
from typing import (
IO,
TYPE_CHECKING,
Any,
AnyStr,
AsyncIterator,
Callable,
Generic,
Iterable,
Iterator,
List,
Optional,
Sequence,
Tuple,
Union,
cast,
overload,
)
from .. import to_thread
from ..abc import AsyncResource
if sys.version_info >= (3, 8):
from typing import Final
else:
from typing_extensions import Final
if TYPE_CHECKING:
from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer
else:
ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object
class AsyncFile(AsyncResource, Generic[AnyStr]):
"""
An asynchronous file object.
This class wraps a standard file object and provides async friendly versions of the following
blocking methods (where available on the original file object):
* read
* read1
* readline
* readlines
* readinto
* readinto1
* write
* writelines
* truncate
* seek
* tell
* flush
All other methods are directly passed through.
This class supports the asynchronous context manager protocol which closes the underlying file
at the end of the context block.
This class also supports asynchronous iteration::
async with await open_file(...) as f:
async for line in f:
print(line)
"""
def __init__(self, fp: IO[AnyStr]) -> None:
self._fp: Any = fp
def __getattr__(self, name: str) -> object:
return getattr(self._fp, name)
@property
def wrapped(self) -> IO[AnyStr]:
"""The wrapped file object."""
return self._fp
async def __aiter__(self) -> AsyncIterator[AnyStr]:
while True:
line = await self.readline()
if line:
yield line
else:
break
async def aclose(self) -> None:
return await to_thread.run_sync(self._fp.close)
async def read(self, size: int = -1) -> AnyStr:
return await to_thread.run_sync(self._fp.read, size)
async def read1(self: "AsyncFile[bytes]", size: int = -1) -> bytes:
return await to_thread.run_sync(self._fp.read1, size)
async def readline(self) -> AnyStr:
return await to_thread.run_sync(self._fp.readline)
async def readlines(self) -> List[AnyStr]:
return await to_thread.run_sync(self._fp.readlines)
async def readinto(self: "AsyncFile[bytes]", b: WriteableBuffer) -> bytes:
return await to_thread.run_sync(self._fp.readinto, b)
async def readinto1(self: "AsyncFile[bytes]", b: WriteableBuffer) -> bytes:
return await to_thread.run_sync(self._fp.readinto1, b)
@overload
async def write(self: "AsyncFile[bytes]", b: ReadableBuffer) -> int:
...
@overload
async def write(self: "AsyncFile[str]", b: str) -> int:
...
async def write(self, b: Union[ReadableBuffer, str]) -> int:
return await to_thread.run_sync(self._fp.write, b)
@overload
async def writelines(
self: "AsyncFile[bytes]", lines: Iterable[ReadableBuffer]
) -> None:
...
@overload
async def writelines(self: "AsyncFile[str]", lines: Iterable[str]) -> None:
...
async def writelines(
self, lines: Union[Iterable[ReadableBuffer], Iterable[str]]
) -> None:
return await to_thread.run_sync(self._fp.writelines, lines)
async def truncate(self, size: Optional[int] = None) -> int:
return await to_thread.run_sync(self._fp.truncate, size)
async def seek(self, offset: int, whence: Optional[int] = os.SEEK_SET) -> int:
return await to_thread.run_sync(self._fp.seek, offset, whence)
async def tell(self) -> int:
return await to_thread.run_sync(self._fp.tell)
async def flush(self) -> None:
return await to_thread.run_sync(self._fp.flush)
@overload
async def open_file(
file: Union[str, "PathLike[str]", int],
mode: OpenBinaryMode,
buffering: int = ...,
encoding: Optional[str] = ...,
errors: Optional[str] = ...,
newline: Optional[str] = ...,
closefd: bool = ...,
opener: Optional[Callable[[str, int], int]] = ...,
) -> AsyncFile[bytes]:
...
@overload
async def open_file(
file: Union[str, "PathLike[str]", int],
mode: OpenTextMode = ...,
buffering: int = ...,
encoding: Optional[str] = ...,
errors: Optional[str] = ...,
newline: Optional[str] = ...,
closefd: bool = ...,
opener: Optional[Callable[[str, int], int]] = ...,
) -> AsyncFile[str]:
...
async def open_file(
file: Union[str, "PathLike[str]", int],
mode: str = "r",
buffering: int = -1,
encoding: Optional[str] = None,
errors: Optional[str] = None,
newline: Optional[str] = None,
closefd: bool = True,
opener: Optional[Callable[[str, int], int]] = None,
) -> AsyncFile[Any]:
"""
Open a file asynchronously.
The arguments are exactly the same as for the builtin :func:`open`.
:return: an asynchronous file object
"""
fp = await to_thread.run_sync(
open, file, mode, buffering, encoding, errors, newline, closefd, opener
)
return AsyncFile(fp)
def wrap_file(file: IO[AnyStr]) -> AsyncFile[AnyStr]:
"""
Wrap an existing file as an asynchronous file.
:param file: an existing file-like object
:return: an asynchronous file object
"""
return AsyncFile(file)
@dataclass(eq=False)
class _PathIterator(AsyncIterator["Path"]):
iterator: Iterator["PathLike[str]"]
async def __anext__(self) -> "Path":
nextval = await to_thread.run_sync(next, self.iterator, None, cancellable=True)
if nextval is None:
raise StopAsyncIteration from None
return Path(cast("PathLike[str]", nextval))
class Path:
"""
An asynchronous version of :class:`pathlib.Path`.
This class cannot be substituted for :class:`pathlib.Path` or :class:`pathlib.PurePath`, but
it is compatible with the :class:`os.PathLike` interface.
It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for the
deprecated :meth:`~pathlib.Path.link_to` method.
Any methods that do disk I/O need to be awaited on. These methods are:
* :meth:`~pathlib.Path.absolute`
* :meth:`~pathlib.Path.chmod`
* :meth:`~pathlib.Path.cwd`
* :meth:`~pathlib.Path.exists`
* :meth:`~pathlib.Path.expanduser`
* :meth:`~pathlib.Path.group`
* :meth:`~pathlib.Path.hardlink_to`
* :meth:`~pathlib.Path.home`
* :meth:`~pathlib.Path.is_block_device`
* :meth:`~pathlib.Path.is_char_device`
* :meth:`~pathlib.Path.is_dir`
* :meth:`~pathlib.Path.is_fifo`
* :meth:`~pathlib.Path.is_file`
* :meth:`~pathlib.Path.is_mount`
* :meth:`~pathlib.Path.lchmod`
* :meth:`~pathlib.Path.lstat`
* :meth:`~pathlib.Path.mkdir`
* :meth:`~pathlib.Path.open`
* :meth:`~pathlib.Path.owner`
* :meth:`~pathlib.Path.read_bytes`
* :meth:`~pathlib.Path.read_text`
* :meth:`~pathlib.Path.readlink`
* :meth:`~pathlib.Path.rename`
* :meth:`~pathlib.Path.replace`
* :meth:`~pathlib.Path.rmdir`
* :meth:`~pathlib.Path.samefile`
* :meth:`~pathlib.Path.stat`
* :meth:`~pathlib.Path.touch`
* :meth:`~pathlib.Path.unlink`
* :meth:`~pathlib.Path.write_bytes`
* :meth:`~pathlib.Path.write_text`
Additionally, the following methods return an async iterator yielding :class:`~.Path` objects:
* :meth:`~pathlib.Path.glob`
* :meth:`~pathlib.Path.iterdir`
* :meth:`~pathlib.Path.rglob`
"""
__slots__ = "_path", "__weakref__"
__weakref__: Any
def __init__(self, *args: Union[str, "PathLike[str]"]) -> None:
self._path: Final[pathlib.Path] = pathlib.Path(*args)
def __fspath__(self) -> str:
return self._path.__fspath__()
def __str__(self) -> str:
return self._path.__str__()
def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.as_posix()!r})"
def __bytes__(self) -> bytes:
return self._path.__bytes__()
def __hash__(self) -> int:
return self._path.__hash__()
def __eq__(self, other: object) -> bool:
target = other._path if isinstance(other, Path) else other
return self._path.__eq__(target)
def __lt__(self, other: "Path") -> bool:
target = other._path if isinstance(other, Path) else other
return self._path.__lt__(target)
def __le__(self, other: "Path") -> bool:
target = other._path if isinstance(other, Path) else other
return self._path.__le__(target)
def __gt__(self, other: "Path") -> bool:
target = other._path if isinstance(other, Path) else other
return self._path.__gt__(target)
def __ge__(self, other: "Path") -> bool:
target = other._path if isinstance(other, Path) else other
return self._path.__ge__(target)
def __truediv__(self, other: Any) -> "Path":
return Path(self._path / other)
def __rtruediv__(self, other: Any) -> "Path":
return Path(other) / self
@property
def parts(self) -> Tuple[str, ...]:
return self._path.parts
@property
def drive(self) -> str:
return self._path.drive
@property
def root(self) -> str:
return self._path.root
@property
def anchor(self) -> str:
return self._path.anchor
@property
def parents(self) -> Sequence["Path"]:
return tuple(Path(p) for p in self._path.parents)
@property
def parent(self) -> "Path":
return Path(self._path.parent)
@property
def name(self) -> str:
return self._path.name
@property
def suffix(self) -> str:
return self._path.suffix
@property
def suffixes(self) -> List[str]:
return self._path.suffixes
@property
def stem(self) -> str:
return self._path.stem
async def absolute(self) -> "Path":
path = await to_thread.run_sync(self._path.absolute)
return Path(path)
def as_posix(self) -> str:
return self._path.as_posix()
def as_uri(self) -> str:
return self._path.as_uri()
def match(self, path_pattern: str) -> bool:
return self._path.match(path_pattern)
def is_relative_to(self, *other: Union[str, "PathLike[str]"]) -> bool:
try:
self.relative_to(*other)
return True
except ValueError:
return False
async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None:
func = partial(os.chmod, follow_symlinks=follow_symlinks)
return await to_thread.run_sync(func, self._path, mode)
@classmethod
async def cwd(cls) -> "Path":
path = await to_thread.run_sync(pathlib.Path.cwd)
return cls(path)
async def exists(self) -> bool:
return await to_thread.run_sync(self._path.exists, cancellable=True)
async def expanduser(self) -> "Path":
return Path(await to_thread.run_sync(self._path.expanduser, cancellable=True))
def glob(self, pattern: str) -> AsyncIterator["Path"]:
gen = self._path.glob(pattern)
return _PathIterator(gen)
async def group(self) -> str:
return await to_thread.run_sync(self._path.group, cancellable=True)
async def hardlink_to(self, target: Union[str, pathlib.Path, "Path"]) -> None:
if isinstance(target, Path):
target = target._path
await to_thread.run_sync(os.link, target, self)
@classmethod
async def home(cls) -> "Path":
home_path = await to_thread.run_sync(pathlib.Path.home)
return cls(home_path)
def is_absolute(self) -> bool:
return self._path.is_absolute()
async def is_block_device(self) -> bool:
return await to_thread.run_sync(self._path.is_block_device, cancellable=True)
async def is_char_device(self) -> bool:
return await to_thread.run_sync(self._path.is_char_device, cancellable=True)
async def is_dir(self) -> bool:
return await to_thread.run_sync(self._path.is_dir, cancellable=True)
async def is_fifo(self) -> bool:
return await to_thread.run_sync(self._path.is_fifo, cancellable=True)
async def is_file(self) -> bool:
return await to_thread.run_sync(self._path.is_file, cancellable=True)
async def is_mount(self) -> bool:
return await to_thread.run_sync(os.path.ismount, self._path, cancellable=True)
def is_reserved(self) -> bool:
return self._path.is_reserved()
async def is_socket(self) -> bool:
return await to_thread.run_sync(self._path.is_socket, cancellable=True)
async def is_symlink(self) -> bool:
return await to_thread.run_sync(self._path.is_symlink, cancellable=True)
def iterdir(self) -> AsyncIterator["Path"]:
gen = self._path.iterdir()
return _PathIterator(gen)
def joinpath(self, *args: Union[str, "PathLike[str]"]) -> "Path":
return Path(self._path.joinpath(*args))
async def lchmod(self, mode: int) -> None:
await to_thread.run_sync(self._path.lchmod, mode)
async def lstat(self) -> os.stat_result:
return await to_thread.run_sync(self._path.lstat, cancellable=True)
async def mkdir(
self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False
) -> None:
await to_thread.run_sync(self._path.mkdir, mode, parents, exist_ok)
@overload
async def open(
self,
mode: OpenBinaryMode,
buffering: int = ...,
encoding: Optional[str] = ...,
errors: Optional[str] = ...,
newline: Optional[str] = ...,
) -> AsyncFile[bytes]:
...
@overload
async def open(
self,
mode: OpenTextMode = ...,
buffering: int = ...,
encoding: Optional[str] = ...,
errors: Optional[str] = ...,
newline: Optional[str] = ...,
) -> AsyncFile[str]:
...
async def open(
self,
mode: str = "r",
buffering: int = -1,
encoding: Optional[str] = None,
errors: Optional[str] = None,
newline: Optional[str] = None,
) -> AsyncFile[Any]:
fp = await to_thread.run_sync(
self._path.open, mode, buffering, encoding, errors, newline
)
return AsyncFile(fp)
async def owner(self) -> str:
return await to_thread.run_sync(self._path.owner, cancellable=True)
async def read_bytes(self) -> bytes:
return await to_thread.run_sync(self._path.read_bytes)
async def read_text(
self, encoding: Optional[str] = None, errors: Optional[str] = None
) -> str:
return await to_thread.run_sync(self._path.read_text, encoding, errors)
def relative_to(self, *other: Union[str, "PathLike[str]"]) -> "Path":
return Path(self._path.relative_to(*other))
async def readlink(self) -> "Path":
target = await to_thread.run_sync(os.readlink, self._path)
return Path(cast(str, target))
async def rename(self, target: Union[str, pathlib.PurePath, "Path"]) -> "Path":
if isinstance(target, Path):
target = target._path
await to_thread.run_sync(self._path.rename, target)
return Path(target)
async def replace(self, target: Union[str, pathlib.PurePath, "Path"]) -> "Path":
if isinstance(target, Path):
target = target._path
await to_thread.run_sync(self._path.replace, target)
return Path(target)
async def resolve(self, strict: bool = False) -> "Path":
func = partial(self._path.resolve, strict=strict)
return Path(await to_thread.run_sync(func, cancellable=True))
def rglob(self, pattern: str) -> AsyncIterator["Path"]:
gen = self._path.rglob(pattern)
return _PathIterator(gen)
async def rmdir(self) -> None:
await to_thread.run_sync(self._path.rmdir)
async def samefile(
self, other_path: Union[str, bytes, int, pathlib.Path, "Path"]
) -> bool:
if isinstance(other_path, Path):
other_path = other_path._path
return await to_thread.run_sync(
self._path.samefile, other_path, cancellable=True
)
async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result:
func = partial(os.stat, follow_symlinks=follow_symlinks)
return await to_thread.run_sync(func, self._path, cancellable=True)
async def symlink_to(
self,
target: Union[str, pathlib.Path, "Path"],
target_is_directory: bool = False,
) -> None:
if isinstance(target, Path):
target = target._path
await to_thread.run_sync(self._path.symlink_to, target, target_is_directory)
async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None:
await to_thread.run_sync(self._path.touch, mode, exist_ok)
async def unlink(self, missing_ok: bool = False) -> None:
try:
await to_thread.run_sync(self._path.unlink)
except FileNotFoundError:
if not missing_ok:
raise
def with_name(self, name: str) -> "Path":
return Path(self._path.with_name(name))
def with_stem(self, stem: str) -> "Path":
return Path(self._path.with_name(stem + self._path.suffix))
def with_suffix(self, suffix: str) -> "Path":
return Path(self._path.with_suffix(suffix))
async def write_bytes(self, data: bytes) -> int:
return await to_thread.run_sync(self._path.write_bytes, data)
async def write_text(
self,
data: str,
encoding: Optional[str] = None,
errors: Optional[str] = None,
newline: Optional[str] = None,
) -> int:
# Path.write_text() does not support the "newline" parameter before Python 3.10
def sync_write_text() -> int:
with self._path.open(
"w", encoding=encoding, errors=errors, newline=newline
) as fp:
return fp.write(data)
return await to_thread.run_sync(sync_write_text)
PathLike.register(Path)

@ -0,0 +1,16 @@
from ..abc import AsyncResource
from ._tasks import CancelScope
async def aclose_forcefully(resource: AsyncResource) -> None:
"""
Close an asynchronous resource in a cancelled scope.
Doing this closes the resource without waiting on anything.
:param resource: the resource to close
"""
with CancelScope() as scope:
scope.cancel()
await resource.aclose()

@ -0,0 +1,24 @@
from typing import AsyncIterator
from ._compat import DeprecatedAsyncContextManager
from ._eventloop import get_asynclib
def open_signal_receiver(
*signals: int,
) -> DeprecatedAsyncContextManager[AsyncIterator[int]]:
"""
Start receiving operating system signals.
:param signals: signals to receive (e.g. ``signal.SIGINT``)
:return: an asynchronous context manager for an asynchronous iterator which yields signal
numbers
.. warning:: Windows does not support signals natively so it is best to avoid relying on this
in cross-platform applications.
.. warning:: On asyncio, this permanently replaces any previous signal handler for the given
signals, as set via :meth:`~asyncio.loop.add_signal_handler`.
"""
return get_asynclib().open_signal_receiver(*signals)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save