v1.3.1 релиз!

prd v1.3.1
Ivan Maslov 2 years ago
parent b5996fad45
commit 5b9274570f

@ -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()

@ -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,7 +405,8 @@ class Process():
#self.MuteWait()
self.mAgentMuteBool=True
lGUIDStr = __Orchestrator__.AgentActivityItemAdd(inHostNameStr=self.mAgentHostNameStr,inUserStr=self.mAgentUserNameStr,inActivityItemDict=lActivityItemUserProcessList)
lUserProcessList = __Orchestrator__.AgentActivityItemReturnGet(inGUIDStr=lGUIDStr)
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
@ -420,6 +421,11 @@ class 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,102 +6,69 @@
# 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 = {}
# 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
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__
: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
import base64
import uuid
import datetime
#Authenticate function ()
# return dict
# {
# "Domain": "", #Empty if Auth is not success
# "User": "" #Empty if Auth is not success
# }
def AuthenticateVerify(inRequest):
# ИНИЦИАЛИЗАЦИЯ 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(inRequest.headers.get("Cookie", ""))
global gSettingsDict
#pdb.set_trace()
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 gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}):
if lCookieAuthToken in __Orchestrator__.GSettingsGet().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"]
lResult["Domain"] = __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["Domain"]
lResult["User"] = __Orchestrator__.GSettingsGet()["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
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
lHeaderAuthorization = inRequest.headers.get("Authorization", "").split(" ")
if len(lHeaderAuthorization) == 2:
llHeaderAuthorizationDecodedUserPasswordList = base64.b64decode(lHeaderAuthorization[1]).decode("utf-8").split(
":")
@ -118,573 +85,142 @@ def AuthenticateVerify(inRequest):
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()
__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
inRequest.OpenRPA["AuthToken"] = lAuthToken
inRequest.OpenRPA["Domain"] = lResult["Domain"]
inRequest.OpenRPA["User"] = lResult["User"]
inRequest.OpenRPASetCookie = {}
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
#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)
#inRequest.OpenRPAResponseDict["SetCookies"]["AuthToken"] = lAuthToken
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
raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (неверная пара логин / пароль)", headers={})
######################################
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
raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (неполная пара логин / пароль)", headers={'Content-type':'text/html', 'WWW-Authenticate':'Basic'})
else: return None # Credentials are not required - return none
# 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"]]
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, 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])")
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"]
)
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
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)
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")
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:
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:
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}")

@ -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)
lValueStr = inBodyStr
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
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)
inGSettings = __Orchestrator__.GSettingsGet()
lL = __Orchestrator__.OrchestratorLoggerGet()
lValueStr = inBodyStr
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
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)
#lValueStr = inBodyDict
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
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,41 +401,42 @@ 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)
#lValueStr = inBodyStr
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
lInput = inBodyDict#json.loads(lValueStr)
lAgentDictItemKeyTurple = (lInput["HostNameUpperStr"], lInput["UserUpperStr"])
if "LogList" in lInput:
for lLogItemStr in lInput["LogList"]:
@ -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):
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]
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,7 +842,7 @@ 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()
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,11 +82,12 @@
<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>
<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"
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>
<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>
</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

@ -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()

@ -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,7 +405,8 @@ class Process():
#self.MuteWait()
self.mAgentMuteBool=True
lGUIDStr = __Orchestrator__.AgentActivityItemAdd(inHostNameStr=self.mAgentHostNameStr,inUserStr=self.mAgentUserNameStr,inActivityItemDict=lActivityItemUserProcessList)
lUserProcessList = __Orchestrator__.AgentActivityItemReturnGet(inGUIDStr=lGUIDStr)
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
@ -420,6 +421,11 @@ class 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,102 +6,69 @@
# 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 = {}
# 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
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__
: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
import base64
import uuid
import datetime
#Authenticate function ()
# return dict
# {
# "Domain": "", #Empty if Auth is not success
# "User": "" #Empty if Auth is not success
# }
def AuthenticateVerify(inRequest):
# ИНИЦИАЛИЗАЦИЯ 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(inRequest.headers.get("Cookie", ""))
global gSettingsDict
#pdb.set_trace()
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 gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}):
if lCookieAuthToken in __Orchestrator__.GSettingsGet().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"]
lResult["Domain"] = __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["Domain"]
lResult["User"] = __Orchestrator__.GSettingsGet()["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
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
lHeaderAuthorization = inRequest.headers.get("Authorization", "").split(" ")
if len(lHeaderAuthorization) == 2:
llHeaderAuthorizationDecodedUserPasswordList = base64.b64decode(lHeaderAuthorization[1]).decode("utf-8").split(
":")
@ -118,573 +85,142 @@ def AuthenticateVerify(inRequest):
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()
__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
inRequest.OpenRPA["AuthToken"] = lAuthToken
inRequest.OpenRPA["Domain"] = lResult["Domain"]
inRequest.OpenRPA["User"] = lResult["User"]
inRequest.OpenRPASetCookie = {}
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
#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)
#inRequest.OpenRPAResponseDict["SetCookies"]["AuthToken"] = lAuthToken
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
raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (неверная пара логин / пароль)", headers={})
######################################
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
raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (неполная пара логин / пароль)", headers={'Content-type':'text/html', 'WWW-Authenticate':'Basic'})
else: return None # Credentials are not required - return none
# 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"]]
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, 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])")
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"]
)
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
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)
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")
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:
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:
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}")

@ -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)
lValueStr = inBodyStr
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
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)
inGSettings = __Orchestrator__.GSettingsGet()
lL = __Orchestrator__.OrchestratorLoggerGet()
lValueStr = inBodyStr
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
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)
#lValueStr = inBodyDict
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
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,41 +401,42 @@ 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)
#lValueStr = inBodyStr
# Превращение массива байт в объект
lInput = json.loads(lInputByteArray.decode('utf8'))
lInput = inBodyDict#json.loads(lValueStr)
lAgentDictItemKeyTurple = (lInput["HostNameUpperStr"], lInput["UserUpperStr"])
if "LogList" in lInput:
for lLogItemStr in lInput["LogList"]:
@ -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>

@ -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):
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]
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,7 +842,7 @@ 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()
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,11 +82,12 @@
<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>
<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"
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>
<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>
</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

@ -1,7 +1,7 @@
.. _module.robot.audio:
####################################
7. Функции Audio
8. Функции Audio
####################################
!ВНИМАНИЕ! ДЛЯ КОРРЕКТНОЙ РАБОТЫ МОДУЛЯ ТРЕБУЕТСЯ КОМПОНЕНТ ffmpeg.exe (Для Windows x64 можно найти в pyOpenRPA\Resources\WAudio). На него должен указывать один из путей в переменной окружения PATH. Или ffmpeg.exe должен быть расположен в рабочей директории (working directory)

@ -1 +1 @@
"..\..\Resources\wkhtmltopdf\bin\wkhtmltopdf.exe" --javascript-delay 5000 --load-error-handling ignore --enable-local-file-access ..\..\Wiki\RUS_Guide\html\index.html ..\..\Wiki\RUS_Guide\html\01_HowToInstall.html ..\..\Wiki\RUS_Guide\html\03_Copyrights_Contacts.html ..\..\Wiki\RUS_Guide\html\Robot\01_Robot.html ..\..\Wiki\RUS_Guide\html\Robot\02_uidesktop.html ..\..\Wiki\RUS_Guide\html\Robot\03_uiweb.html ..\..\Wiki\RUS_Guide\html\Robot\04_keyboard.html ..\..\Wiki\RUS_Guide\html\Robot\05_clipboard.html ..\..\Wiki\RUS_Guide\html\Robot\06_mouse.html ..\..\pyOpenRPA\Wiki\RUS_Guide\html\Robot\07_screen.html ..\..\Wiki\RUS_Guide\html\Robot\08_audio.html ..\..\Wiki\RUS_Guide\html\Robot\09_HowToUse.html ..\..\Wiki\RUS_Guide\html\Studio\01_Studio.html ..\..\Wiki\RUS_Guide\html\Studio\02_HowToUse.html ..\..\Wiki\RUS_Guide\html\Orchestrator\01_Orchestrator.html ..\..\Wiki\RUS_Guide\html\Orchestrator\02_Defs.html ..\..\Wiki\RUS_Guide\html\Orchestrator\03_gSettingsTemplate.html ..\..\Wiki\RUS_Guide\html\Orchestrator\04_HowToUse.html ..\..\Wiki\RUS_Guide\html\Orchestrator\05_UAC.html ..\..\Wiki\RUS_Guide\pdf\pyOpenRPA_Guide_RUS.pdf
"..\..\Resources\wkhtmltopdf\bin\wkhtmltopdf.exe" --javascript-delay 5000 --load-error-handling ignore --enable-local-file-access ..\..\Wiki\RUS_Guide\html\index.html ..\..\Wiki\RUS_Guide\html\01_HowToInstall.html ..\..\Wiki\RUS_Guide\html\03_Copyrights_Contacts.html ..\..\Wiki\RUS_Guide\html\Robot\01_Robot.html ..\..\Wiki\RUS_Guide\html\Robot\02_uidesktop.html ..\..\Wiki\RUS_Guide\html\Robot\03_uiweb.html ..\..\Wiki\RUS_Guide\html\Robot\04_keyboard.html ..\..\Wiki\RUS_Guide\html\Robot\05_clipboard.html ..\..\Wiki\RUS_Guide\html\Robot\06_mouse.html ..\..\Wiki\RUS_Guide\html\Robot\07_screen.html ..\..\Wiki\RUS_Guide\html\Robot\08_audio.html ..\..\Wiki\RUS_Guide\html\Robot\09_HowToUse.html ..\..\Wiki\RUS_Guide\html\Studio\01_Studio.html ..\..\Wiki\RUS_Guide\html\Studio\02_HowToUse.html ..\..\Wiki\RUS_Guide\html\Orchestrator\01_Orchestrator.html ..\..\Wiki\RUS_Guide\html\Orchestrator\02_Defs.html ..\..\Wiki\RUS_Guide\html\Orchestrator\03_gSettingsTemplate.html ..\..\Wiki\RUS_Guide\html\Orchestrator\04_HowToUse.html ..\..\Wiki\RUS_Guide\html\Orchestrator\05_UAC.html ..\..\Wiki\RUS_Guide\pdf\pyOpenRPA_Guide_RUS.pdf

@ -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,470 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Тест функций pyOpenRPA.Robot.Keyboard"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"import os\n",
"#sys.path.insert(0, os.path.abspath(\"..\\\\..\\\\Sources\")) # FOR WINDOWS\n",
"sys.path.insert(0, os.path.abspath(\"../../Sources\")) # FOR LINUX\n",
"import time\n",
"from pyOpenRPA.Robot import Keyboard"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"done\n"
]
}
],
"source": [
"\n",
"time.sleep(3)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_CTRL_LEFT,Keyboard.KEY_ENG_A)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_CTRL_LEFT,Keyboard.KEY_ENG_C)\n",
"print(\"done\")\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"ename": "NameError",
"evalue": "name 'ei' is not defined",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
"Input \u001b[0;32mIn [5]\u001b[0m, in \u001b[0;36m<cell line: 7>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 5\u001b[0m Keyboard\u001b[38;5;241m.\u001b[39mSend(Keyboard\u001b[38;5;241m.\u001b[39mKEY_ENG_E)\n\u001b[1;32m 6\u001b[0m Keyboard\u001b[38;5;241m.\u001b[39mSend(Keyboard\u001b[38;5;241m.\u001b[39mKEY_RUS_Ш)\n\u001b[0;32m----> 7\u001b[0m \u001b[43mei\u001b[49m\n",
"\u001b[0;31mNameError\u001b[0m: name 'ei' is not defined"
]
}
],
"source": [
"# Keyboard: Взаимодействие с клавиатурой\n",
"from pyOpenRPA.Robot import Keyboard\n",
"import time\n",
"time.sleep(2)\n",
"Keyboard.Send(Keyboard.KEY_ENG_E)\n",
"Keyboard.Send(Keyboard.KEY_RUS_Ш)\n",
"ei"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"time.sleep(2)\n",
"Keyboard.press(Keyboard.KEY_HOT_SHIFT_LEFT)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"time.sleep(2)\n",
"Keyboard.HotkeyCtrlA_CtrlC()\n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"time.sleep(2)\n",
"Keyboard.send(Keyboard.KEY_HOT_CONTEXT_MENU)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"Keyboard.wait(Keyboard.KEY_ENG_B)"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"done\n"
]
}
],
"source": [
"\n",
"time.sleep(1)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_CTRL_LEFT,Keyboard.KEY_ENG_A)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_CTRL_LEFT,Keyboard.KEY_ENG_C)\n",
"print(\"done\")"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"time.sleep(3)\n",
"\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.Write(\"cmd\")\n",
"Keyboard.Send(Keyboard.KEY_HOT_ENTER, inWaitAfterSecFloat=0.6)\n",
"Keyboard.Write(\"echo %time%\")\n",
"Keyboard.Send(Keyboard.KEY_HOT_ENTER)\n",
"Keyboard.Write(\"Привет мир!\")\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_CTRL_LEFT, Keyboard.KEY_ENG_A, inWaitAfterSecFloat=0.6)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_CTRL_LEFT, Keyboard.KEY_ENG_C, inWaitAfterSecFloat=0.6)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"57435"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Keyboard.KEY_HOT_WIN_LEFT"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"29"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Keyboard.KEY_HOT_CTRL_LEFT"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(19,)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Keyboard.key_to_scan_codes(\"win\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"Keyboard.HotkeyCombination(57435,19)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_WIN_LEFT,Keyboard.KEY_ENG_R)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"time.sleep(3)\n",
"Keyboard.is_pressed(Keyboard.KEY_HOT_CTRL_LEFT)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"TT=9\n",
"def k(hh=TT):\n",
" print(hh)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"9\n"
]
}
],
"source": [
"k()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"KEY_RUS_А\" in dir(Keyboard)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"ename": "AttributeError",
"evalue": "module 'pyOpenRPA.Robot.Keyboard' has no attribute 'KEY_RUS_'",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
"Input \u001b[0;32mIn [6]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mgetattr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mKeyboard\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mKEY_RUS_\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
"\u001b[0;31mAttributeError\u001b[0m: module 'pyOpenRPA.Robot.Keyboard' has no attribute 'KEY_RUS_'"
]
}
],
"source": [
"getattr(Keyboard, \"KEY_RUS_\")"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"time.sleep(3)\n",
"Keyboard.Write(Keyboard.KEY_ENG_B, inDelayFloat=0.001)b"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"HelloПривет!йцукенгшщзхъфывапролджэячсмитьбю.ёЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,Hi anybody"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"HelloПривет!йцукенгшщзхъфывапролджэячсмитьбю.ёЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,Hi anybody"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.2"
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,131 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"import sys\n",
"import os\n",
"#sys.path.insert(0, os.path.abspath(\"..\\\\..\\\\Sources\")) # FOR WINDOWS\n",
"sys.path.insert(0, os.path.abspath(\"../../Sources\")) # FOR LINUX \n",
"from pyOpenRPA.Robot import Mouse"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
" # Mouse: Взаимодействие с мышью\n",
" from pyOpenRPA.Robot import Mouse\n",
" Mouse.Click(10,15) #Выполнить нажатие левой клавиши мыши на экране по координатам: X(гор) 100px, Y(вер) 150px.\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"time.sleep(3)\n",
"Mouse.hscroll(-100)\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"import time\n",
"time.sleep(2)\n",
"Mouse.vscroll(-100)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
" # Mouse: Взаимодействие с мышью\n",
" from pyOpenRPA.Robot import Mouse\n",
" Mouse.ClickDouble(100,150) #Выполнить двойное нажатие левой клавиши мыши на экране по координатам: X(гор) 100px, Y(вер) 150px.\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
" # Mouse: Взаимодействие с мышью\n",
" from pyOpenRPA.Robot import Mouse\n",
" Mouse.Down() #Опустить левую клавишу мыши"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.2"
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -0,0 +1,215 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"import os\n",
"#sys.path.insert(0, os.path.abspath(\"..\\\\..\\\\Sources\")) # FOR WINDOWS\n",
"sys.path.insert(0, os.path.abspath(\"../../Sources\")) # FOR LINUX\n",
"from pyOpenRPA.Robot import UIWeb\n",
"UIWeb.BrowserChromeStart()\n",
"UIWeb.PageOpen(\"https://mail.ru\")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"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\"\n",
"lUIOSelectorStr = \"//*[@id=\\\"grid\\\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a\"\n",
"UIWeb.UIOSelectorHighlight(lUIOSelectorStr)\n",
"lUIO=UIWeb.UIOSelectorList(lUIOSelectorStr)[0]"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"UIWeb.BrowserClose()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
" # UIWeb: Взаимодействие с ui web\n",
" from pyOpenRPA.Robot import UIWeb\n",
" #UIWeb.BrowserChromeStart()\n",
" #UIWeb.PageOpen(\"https://mail.ru\")\n",
" UIWeb.PageJSExecute(\"alert(arguments[0]);\", \"hello world!\")"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"UIWeb.UIOAttributeSet(lUIO,\"test\", \"asd\")"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"UIWeb.UIOAttributeRemove(lUIO, \"test\")"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"UIWeb.UIOAttributeStyleSet(lUIO, \"color\", \"grey\")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"UIWeb.UIOAttributeStyleRemove(lUIO, \"color\")"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'rgba(0, 91, 209, 1)'"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"UIWeb.UIOAttributeStyleGet(lUIO, \"color\")"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<selenium.webdriver.remote.webelement.WebElement (session=\"2d9c81f94cb903caaa62936fe795b5ba\", element=\"98e83708-c889-4d63-9f60-f21f31b19e0c\")>\n"
]
}
],
"source": [
"import time\n",
"UIWeb.UIOMouseSearchInit()\n",
"time.sleep(3)\n",
"print(UIWeb.UIOMouseSearchReturn())"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Греция спустя 12 лет вышла из-под усиленного контроля кредиторов'"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"UIWeb.UIOTextGet(lUIO)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"UIWeb.UIOSelectorHighlight(lUIOSelectorStr)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.2"
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}

@ -419,7 +419,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
@ -433,7 +433,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.5"
"version": "3.7.2"
},
"varInspector": {
"cols": {

@ -80,7 +80,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
@ -94,7 +94,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.5"
"version": "3.7.2"
},
"varInspector": {
"cols": {

@ -164,7 +164,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
@ -178,7 +178,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.5"
"version": "3.7.2"
},
"varInspector": {
"cols": {

@ -11,265 +11,147 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Уважаемый пользователь! Данный файл содержит набор преднастроенных команд для ускоренной проверки гипотез роботизации. В"
"Уважаемый пользователь! \n",
"\n",
"Данный файл содержит набор преднастроенных команд для ускоренной прототипирования роботов. Вся необходимая инфраструктура pyOpenRPA доступна из одного файла - осуществляйте разработку максимально быстро и комфортно!\n",
"\n",
"Документация pyOpenRPA доступна по адресу: https://pyopenrpa.ru/wiki/guide/index\n",
"\n",
"По всем вопросам обращайтесь к нам.\n",
"\n",
"**Центр поддержки клиентов pyOpenRPA:**\n",
"- ⚡ Телеграм: @pyOpenRPA_Support \n",
"- ⚡ Почта: Support@pyOpenRPA.ru \n",
"- ⚡ Телефон: +7 995 233 45 31 \n",
"\n",
"❤️ pyOpenRPA - роботы помогут!"
]
},
{
"cell_type": "code",
"execution_count": 1,
"cell_type": "markdown",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Box(left=550, top=244, width=29, height=29), Box(left=550, top=298, width=29, height=29), Box(left=550, top=352, width=29, height=29), Box(left=550, top=406, width=29, height=29), Box(left=550, top=460, width=29, height=29), Box(left=550, top=514, width=29, height=29), Box(left=550, top=568, width=29, height=29), Box(left=550, top=622, width=29, height=29), Box(left=550, top=676, width=29, height=29), Box(left=550, top=730, width=29, height=29), Box(left=550, top=784, width=29, height=29), Box(left=550, top=838, width=29, height=29)]\n"
]
}
],
"source": [
"import sys\n",
"import os\n",
"sys.path.insert(0, os.path.abspath(\"..\\\\..\\\\Sources\"))\n",
"sys.path.insert(0, os.path.abspath(\"..\\\\..\\\\..\\\\KPI_Effect\\\\packages\"))\n",
"import time\n",
"from pyOpenRPA.Robot import Keyboard\n",
"\n",
"# Screen: Взаимодействие с объектами экрана\n",
"from pyOpenRPA.Robot import Screen\n",
"lBoxList = Screen.ImageLocateAll(inImgPathStr=\"screencontact.PNG\",inConfidenceFloat=0.9)\n",
"print(lBoxList)"
"## Шаг 1 - Импорт модулей pyOpenRPA"
]
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"#ИМПОРТ ДИРЕКТОРИИ\n",
"import sys\n",
"import os\n",
"sys.path.insert(0, os.path.abspath(\"..\\\\..\\\\..\\\\KPI_Effect\\\\packages\"))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Box(left=500, top=244, width=29, height=29), Box(left=500, top=298, width=29, height=29), Box(left=500, top=352, width=29, height=29), Box(left=500, top=406, width=29, height=29), Box(left=500, top=460, width=29, height=29), Box(left=500, top=514, width=29, height=29), Box(left=500, top=568, width=29, height=29), Box(left=500, top=622, width=29, height=29), Box(left=500, top=676, width=29, height=29), Box(left=500, top=730, width=29, height=29), Box(left=500, top=784, width=29, height=29), Box(left=500, top=838, width=29, height=29)]\n"
]
}
],
"source": [
"lBox2List = Screen.BoxMoveTo(inBox=lBoxList,inDXInt=-50)\n",
"print(lBox2List)\n",
"Screen.BoxDraw(inBox = lBox2List)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"lPoint = Screen.BoxGetPoint(lBox2List[0])"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"Screen.PointClick(lPoint)"
"import time\n",
"\n",
"sys.path.insert(0, os.path.abspath(\"..\\\\..\\\\..\\\\Sources\")) # FOR WINDOWS\n",
"#sys.path.insert(0, os.path.abspath(\"../../../Sources\")) # FOR LINUX"
]
},
{
"cell_type": "code",
"execution_count": 12,
"cell_type": "markdown",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Box(left=489, top=243, width=46, height=32)]\n"
]
}
],
"source": [
"lBoxList = Screen.ImageLocateAll(inImgPathStr=\"sendmessage.PNG\",inConfidenceFloat=0.9)\n",
"print(lBoxList)"
"## Шаг 2 - Пишем робота"
]
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"Screen.PointClick(Screen.BoxGetPoint(Screen.ImageLocateAll(inImgPathStr=\"sendmessage.PNG\",inConfidenceFloat=0.9)[0]))"
"# Здесь будет код вашего робота...\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[Box(left=169, top=193, width=409, height=687)]"
]
},
"execution_count": 14,
"cell_type": "markdown",
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Screen.ImageLocateAll(inImgPathStr=\"emptytray.PNG\",inConfidenceFloat=0.9)"
"## Примеры использования модулей"
]
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import importlib"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"123\n"
]
},
{
"data": {
"text/plain": [
"<module 'pak.test' from 'C:\\\\Abs\\\\Archive\\\\scopeSrcUL\\\\pyOpenRPA\\\\Tools\\\\Jupyter-notebooks\\\\pak\\\\test.py'>"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"importlib.import_module(\".test\",\"pak\")"
"# UIDesktop: Взаимодействие с объектами локального приложения\n",
"from pyOpenRPA.Robot import UIDesktop\n",
"\n",
"# 1С: UIO Селектор выбора базы\n",
"lDemoBaseUIOSelector = [{\"title\":\"Запуск 1С:Предприятия\",\"class_name\":\"V8TopLevelFrameTaxiStarter\",\"backend\":\"uia\"},{\"title\":\"DEMO\", \"depth_start\": 5, \"depth_end\": 5}] \n",
"lDemoBaseUIO = UIDesktop.UIOSelector_Get_UIO(lDemoBaseUIOSelector) #Получить 1-й UIO объект, которые удовлетворяет требованиям UIO селектора. В нашем примере либо None, либо UIO объект\n",
"\n",
"lDemoBaseUIO.click_input()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import sys"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<module 'pak' (namespace)>"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sys.modules[\"pak\"]"
"# UIWeb: Взаимодействие с объектами web страницы\n",
"from pyOpenRPA.Robot import UIWeb\n",
"\n",
"UIWeb.BrowserChromeStart()\n",
"UIWeb.PageOpen(\"https://mail.ru\")\n",
"UIWeb.PageJSExecute(\"alert(arguments[0]);\", \"hello world!\")\n",
"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\"\n",
"lUIOSelectorStr = \"//*[@id=\\\"grid\\\"]/div[2]/div[2]/div[3]/div[1]/ul/li[5]/div/a\"\n",
"UIWeb.UIOSelectorHighlight(lUIOSelectorStr)\n",
"lUIO=UIWeb.UIOSelectorList(lUIOSelectorStr)[0]\n",
"\n",
"UIWeb.BrowserClose()"
]
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import datetime"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"datetime.datetime(2022, 9, 26, 20, 58, 26, 931399)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"datetime.datetime.now()+datetime.timedelta(hours=3)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"datetime.timedelta(seconds=10800)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"datetime.timedelta(hours=3)"
"# Keyboard: Взаимодействие с клавиатурой\n",
"from pyOpenRPA.Robot import Keyboard\n",
"\n",
"time.sleep(2)\n",
"Keyboard.Write(\"Привет мир! / Hello world!\")\n",
"\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_CTRL_LEFT,Keyboard.KEY_ENG_A)\n",
"Keyboard.HotkeyCombination(Keyboard.KEY_HOT_CTRL_LEFT,Keyboard.KEY_ENG_C)\n",
"\n",
"Keyboard.Send(Keyboard.KEY_ENG_E)\n",
"Keyboard.Send(Keyboard.KEY_RUS_Ш)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'2022-09-26 17:59:17'"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"outputs": [],
"source": [
"g_datetime_format_str = '%Y-%m-%d %H:%M:%S'\n",
"(datetime.datetime.now()).strftime(g_datetime_format_str)"
"# Clipboard: Взаимодействие с буфером обмена\n",
"from pyOpenRPA.Robot import Clipboard\n",
"\n",
"Clipboard.Set(inTextStr=\"Hello!\")\n",
"lClipStr = Clipboard.Get()\n",
"print(lClipStr)"
]
},
{
@ -278,140 +160,49 @@
"metadata": {},
"outputs": [],
"source": [
"import time"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"datetime.datetime(2022, 9, 26, 18, 1, 58, 924492)"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"datetime.datetime.fromtimestamp(time.time())"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"\".endswith(\"/\")"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"ename": "ImportError",
"evalue": "cannot import name 'kpi_client' from '__main__' (unknown location)",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mImportError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-15-2f86ae4ce68c>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[1;33m.\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mkpi_client\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mImportError\u001b[0m: cannot import name 'kpi_client' from '__main__' (unknown location)"
]
}
],
"source": [
"from . import kpi_client"
"# Screen: Взаимодействие с объектами экрана\n",
"from pyOpenRPA.Robot import Screen\n",
"\n",
"lBoxList = Screen.ImageLocateAll(inImgPathStr=\"ScreenItem.png\",inConfidenceFloat=0.9)\n",
"print(lBoxList)\n",
"lBox2List = Screen.BoxMoveTo(inBox=lBoxList,inDXInt=-50)\n",
"print(lBox2List)\n",
"Screen.BoxDraw(inBox = lBox2List)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#ИМПОРТ ДИРЕКТОРИИ\n",
"import sys\n",
"import os\n",
"sys.path.insert(0, os.path.abspath(\"..\\\\..\\\\..\\\\KPI_Effect\\\\packages\"))"
"# Mouse: Взаимодействие с мышью\n",
"from pyOpenRPA.Robot import Mouse\n",
"\n",
"Mouse.Click(10,15) #Выполнить нажатие левой клавиши мыши на экране по координатам: X(гор) 100px, Y(вер) 150px.\n",
"Mouse.Down() #Опустить левую клавишу мыши\n",
"Mouse.ClickDouble(100,150) #Выполнить двойное нажатие левой клавиши мыши на экране по координатам: X(гор) 100px, Y(вер) 150px."
]
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import kpi_client\n",
"kpi_client.init(\"http://localhost:1024\", \"1992-04-03-0643-ru-b4ff-openrpa52zzz\")"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"-----------START-----------\n",
"POST http://localhost:1024/orpa/api/activity-list-execute\r\n",
"Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n",
"Cookie: AuthToken=1992-04-03-0643-ru-b4ff-openrpa52zzz\r\n",
"Content-Length: 466\r\n",
"\r\n",
"b'[{\"Def\": \"kpi_control_panel.actions.log\", \"ArgList\": [], \"ArgDict\": {\"in_code_str\": \"asd\", \"in_group_str\": \"\\\\u041f\\\\u0440\\\\u0438\\\\u0432\\\\u0435\\\\u0442!\", \"in_manual_operation_float\": 190, \"in_auto_operation_float\": 88, \"in_start_time_float\": 123124, \"in_comment_str\": \"-\", \"in_count_int\": 1, \"in_sla_percent_float\": 1.0, \"in_dev_next_mvp_float\": 1.0, \"in_revenue_float\": 0.0, \"in_fine_float\": 0.0, \"in_reputation_str\": \"-\"}, \"ArgGSettingsStr\": null, \"ArgLoggerStr\": null}]'\n"
]
}
],
"source": [
"kpi_client.log(in_code_str=\"asd\",in_group_str=\"Привет!\",in_manual_operation_float=190, in_auto_operation_float=88, in_start_time_float=123124)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"print(\"\")"
"# Audio: Взаимодействие с аудиоустройствами (захват звка микрофона или приложения)\n",
"from pyOpenRPA.Robot import Audio\n",
"\n",
"Audio.DeviceListGet()\n",
"\n",
"lRec = Audio.Recorder(inDeviceInt = Audio.DeviceMicrophoneIndex()) #Захват микрофона\n",
"lRec.CaptureStart(inFileNameStr = \"out\", inFileFormatStr = \"mp3\", inDurationSecFloat = None, inChunkSecFloat = 15.0)\n",
"lRec.CaptureStop()\n",
"\n",
"lRec = Audio.Recorder() # Звахват звуков приложений\n",
"lRec.CaptureStart(inFileNameStr = \"out\", inFileFormatStr = \"mp3\", inDurationSecFloat = None, inChunkSecFloat = 5.0)\n",
"lRec.CaptureStop()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {

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

Loading…
Cancel
Save