rpa-suite 1.4.2__py3-none-any.whl → 1.4.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rpa_suite/__init__.py +4 -1
- rpa_suite/core/__init__.py +1 -0
- rpa_suite/core/browser.py +166 -0
- rpa_suite/core/clock.py +1 -1
- rpa_suite/core/log.py +55 -381
- rpa_suite/suite.py +36 -22
- rpa_suite/utils/__init__.py +10 -0
- rpa_suite/utils/system.py +33 -0
- {rpa_suite-1.4.2.dist-info → rpa_suite-1.4.5.dist-info}/METADATA +25 -15
- {rpa_suite-1.4.2.dist-info → rpa_suite-1.4.5.dist-info}/RECORD +13 -10
- {rpa_suite-1.4.2.dist-info → rpa_suite-1.4.5.dist-info}/WHEEL +0 -0
- {rpa_suite-1.4.2.dist-info → rpa_suite-1.4.5.dist-info}/licenses/LICENSE +0 -0
- {rpa_suite-1.4.2.dist-info → rpa_suite-1.4.5.dist-info}/top_level.txt +0 -0
rpa_suite/__init__.py
CHANGED
@@ -26,6 +26,7 @@
|
|
26
26
|
``printer``: Functions for formatted output
|
27
27
|
``regex``: Operations with regular expressions
|
28
28
|
``validate``: Data validation functions
|
29
|
+
``Browser``: Object Browser automation functions
|
29
30
|
|
30
31
|
pt-br
|
31
32
|
-----
|
@@ -55,6 +56,8 @@
|
|
55
56
|
``printer``: Funções para output formatado
|
56
57
|
``regex``: Operações com expressões regulares
|
57
58
|
``validate``: Funções de validação de dados
|
59
|
+
``Browser``: Objeto de Automação de Navegadores
|
58
60
|
"""
|
59
61
|
|
60
|
-
from .suite import
|
62
|
+
from .suite import rpa
|
63
|
+
rpa
|
rpa_suite/core/__init__.py
CHANGED
@@ -0,0 +1,166 @@
|
|
1
|
+
# default import
|
2
|
+
import os, requests
|
3
|
+
|
4
|
+
# imports
|
5
|
+
from rpa_suite.functions._printer import error_print, alert_print, success_print
|
6
|
+
|
7
|
+
from selenium import webdriver
|
8
|
+
from selenium.webdriver.common.by import By
|
9
|
+
from selenium.webdriver.chrome.options import Options
|
10
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
11
|
+
from selenium.webdriver.support import expected_conditions as EC
|
12
|
+
from webdriver_manager.chrome import ChromeDriverManager
|
13
|
+
from time import sleep
|
14
|
+
|
15
|
+
class Browser():
|
16
|
+
|
17
|
+
"""
|
18
|
+
WIP ...
|
19
|
+
"""
|
20
|
+
|
21
|
+
driver: None
|
22
|
+
port: int = None
|
23
|
+
path_driver = None
|
24
|
+
|
25
|
+
def __init__(self, port: int = 9393, close_all_chrome_on_this_port: bool = True):
|
26
|
+
self.port = port
|
27
|
+
self.path_driver = ChromeDriverManager().install()
|
28
|
+
|
29
|
+
if close_all_chrome_on_this_port: self._close_all_chrome()
|
30
|
+
...
|
31
|
+
|
32
|
+
def configure_browser(self) -> None:
|
33
|
+
|
34
|
+
try:
|
35
|
+
# Use the absolute path from comment
|
36
|
+
|
37
|
+
options = Options()
|
38
|
+
options.add_experimental_option("debuggerAddress",
|
39
|
+
f"127.0.0.1:{str(self.port)}")
|
40
|
+
|
41
|
+
# Additional configs
|
42
|
+
options.add_argument("--start-maximized")
|
43
|
+
options.add_argument("--disable-notifications")
|
44
|
+
|
45
|
+
# Verifica se o caminho do driver está correto
|
46
|
+
if not os.path.exists(self.path_driver):
|
47
|
+
raise FileNotFoundError(f'O caminho do driver não foi encontrado: {self.path_driver}')
|
48
|
+
|
49
|
+
# Create driver with options and chromedriver path
|
50
|
+
self.driver = webdriver.Chrome(
|
51
|
+
#service=self.path_driver,
|
52
|
+
options=options,
|
53
|
+
keep_alive=True
|
54
|
+
)
|
55
|
+
|
56
|
+
except Exception as e:
|
57
|
+
error_print(f'Erro durante a função: {self.configure_browser.__name__}! Error: {str(e)}.')
|
58
|
+
|
59
|
+
def start_browser(self, close_chrome_on_this_port: bool = True, display_message: bool = False):
|
60
|
+
|
61
|
+
try:
|
62
|
+
if close_chrome_on_this_port: self.close_browser()
|
63
|
+
|
64
|
+
# Inicia o Chrome com debugging port
|
65
|
+
os.system(f'start chrome.exe --remote-debugging-port={str(self.port)} --user-data-dir="C:/temp/chrome_profile"')
|
66
|
+
|
67
|
+
# Aguardar até que o Chrome esteja realmente aberto
|
68
|
+
while True:
|
69
|
+
try:
|
70
|
+
# Tenta conectar ao Chrome na porta de depuração
|
71
|
+
response = requests.get(f'http://127.0.0.1:{self.port}/json')
|
72
|
+
if response.status_code == 200:
|
73
|
+
break # O Chrome está aberto
|
74
|
+
except requests.ConnectionError:
|
75
|
+
sleep(1) # Espera um segundo antes de tentar novamente
|
76
|
+
|
77
|
+
# Inicializa o Chrome com as opções
|
78
|
+
self.configure_browser()
|
79
|
+
|
80
|
+
if display_message: success_print(f'Browser: Iniciado com sucesso!')
|
81
|
+
|
82
|
+
except Exception as e:
|
83
|
+
error_print(f'Erro ao iniciar navegador: {str(e)}.')
|
84
|
+
|
85
|
+
|
86
|
+
def find_ele(self, value, by=By.XPATH, timeout=12, display_message=True):
|
87
|
+
|
88
|
+
try:
|
89
|
+
sleep(2)
|
90
|
+
element = WebDriverWait(self.driver, timeout).until(
|
91
|
+
EC.presence_of_element_located((by, value))
|
92
|
+
); return element
|
93
|
+
|
94
|
+
except Exception as e:
|
95
|
+
|
96
|
+
if display_message:
|
97
|
+
error_print(f'Erro durante a função: {self.find_ele.__name__}! Error: {str(e)}.')
|
98
|
+
return None
|
99
|
+
else: return None
|
100
|
+
|
101
|
+
# find elements (needs implementation)
|
102
|
+
...
|
103
|
+
|
104
|
+
# navigate
|
105
|
+
def get(self, url: str, display_message: bool = False):
|
106
|
+
|
107
|
+
try:
|
108
|
+
self.driver.get(url)
|
109
|
+
if display_message: success_print(f'Browser: Navegando para: {url}')
|
110
|
+
|
111
|
+
except Exception as e:
|
112
|
+
error_print(f'Erro ao navegar para a URL: {url}. Error: {str(e)}.')
|
113
|
+
|
114
|
+
|
115
|
+
def _close_all_chrome(self):
|
116
|
+
|
117
|
+
try:
|
118
|
+
os.system('taskkill /F /IM chrome.exe >nul 2>&1')
|
119
|
+
except:
|
120
|
+
pass
|
121
|
+
|
122
|
+
|
123
|
+
def close_browser(self, display_message: bool = False):
|
124
|
+
|
125
|
+
try:
|
126
|
+
# Primeiro tenta fechar todas as janelas via Selenium
|
127
|
+
try:
|
128
|
+
self.driver.close()
|
129
|
+
except:
|
130
|
+
pass
|
131
|
+
|
132
|
+
# Depois tenta encerrar a sessão
|
133
|
+
try:
|
134
|
+
self.driver.quit()
|
135
|
+
except:
|
136
|
+
pass
|
137
|
+
|
138
|
+
# Aguarda um momento para o processo ser liberado
|
139
|
+
sleep(1)
|
140
|
+
|
141
|
+
# Força o fechamento do processo específico do Chrome
|
142
|
+
os.system(f'taskkill /f /im chrome.exe /fi "commandline like *--remote-debugging-port={str(self.port)}*" >nul 2>&1')
|
143
|
+
|
144
|
+
# Verifica se o processo foi realmente terminado
|
145
|
+
check = os.system(f'tasklist /fi "imagename eq chrome.exe" /fi "commandline like *--remote-debugging-port={str(self.port)}*" >nul 2>&1')
|
146
|
+
|
147
|
+
if check == 0:
|
148
|
+
# Processo ainda existe, tenta método mais agressivo
|
149
|
+
os.system(f'taskkill /f /im chrome.exe /fi "commandline like *--remote-debugging-port={str(self.port)}*" /t >nul 2>&1')
|
150
|
+
if display_message: alert_print(f'Browser: Fechado via força!')
|
151
|
+
|
152
|
+
else:
|
153
|
+
if display_message: success_print(f'Browser: Fechado com sucesso!')
|
154
|
+
|
155
|
+
except Exception as e:
|
156
|
+
|
157
|
+
|
158
|
+
try:
|
159
|
+
if display_message: alert_print(f'Erro ao fechar navegador: {str(e)}, Tentando meio mais forte!')
|
160
|
+
|
161
|
+
# Último recurso - mata todos os processos do Chrome (use com cautela)
|
162
|
+
os.system(f'taskkill /f /im chrome.exe /fi "commandline like *--remote-debugging-port={str(self.port)}*" /t >nul 2>&1')
|
163
|
+
if display_message: alert_print(f'Browser: Fechado via força extrema!')
|
164
|
+
|
165
|
+
except Exception as error_ultimate:
|
166
|
+
if display_message: error_print(f'Falha crítica ao tentar fechar o navegador! Error: {str(error_ultimate)}!')
|
rpa_suite/core/clock.py
CHANGED
rpa_suite/core/log.py
CHANGED
@@ -1,91 +1,24 @@
|
|
1
1
|
from typing import Optional as Op
|
2
|
-
from
|
3
|
-
from rpa_suite.functions._printer import error_print, alert_print
|
2
|
+
from rpa_suite.functions._printer import error_print, alert_print, success_print
|
4
3
|
from loguru import logger
|
5
4
|
import sys, os, inspect
|
6
5
|
|
7
6
|
|
8
|
-
class Filters
|
9
|
-
"""
|
10
|
-
Class that provides utilities for filtering log messages based on specific keywords.
|
11
|
-
|
12
|
-
This class allows you to define a list of words that, if found in a log message, will trigger a modification of that message.
|
13
|
-
The modified message will indicate that it has been altered due to the presence of a filtered word.
|
14
|
-
|
15
|
-
Methods:
|
16
|
-
__call__: Checks if any of the specified words are present in the log message and alters the message if a match is found.
|
17
|
-
|
18
|
-
Example:
|
19
|
-
>>> filter = Filters()
|
20
|
-
>>> filter.word_filter = ['error', 'warning']
|
21
|
-
>>> record = {"message": "This is an error message."}
|
22
|
-
>>> filter(record) # This will alter the message to 'Log Alterado devido a palavra Filtrada!'
|
23
|
-
|
24
|
-
pt-br
|
25
|
-
----------
|
26
|
-
Classe que fornece utilitários para filtrar mensagens de log com base em palavras-chave específicas.
|
27
|
-
|
28
|
-
Esta classe permite que você defina uma lista de palavras que, se encontradas em uma mensagem de log, acionarão uma modificação dessa mensagem.
|
29
|
-
A mensagem modificada indicará que foi alterada devido à presença de uma palavra filtrada.
|
30
|
-
|
31
|
-
Métodos:
|
32
|
-
__call__: Verifica se alguma das palavras especificadas está presente na mensagem de log e altera a mensagem se uma correspondência for encontrada.
|
33
|
-
|
34
|
-
Exemplo:
|
35
|
-
>>> filtro = Filters()
|
36
|
-
>>> filtro.word_filter = ['erro', 'aviso']
|
37
|
-
>>> registro = {"message": "Esta é uma mensagem de erro."}
|
38
|
-
>>> filtro(registro) # Isso alterará a mensagem para 'Log Alterado devido a palavra Filtrada!'
|
39
|
-
"""
|
40
|
-
|
7
|
+
class Filters:
|
41
8
|
word_filter: Op[list[str]]
|
42
9
|
|
43
10
|
def __call__(self, record):
|
44
|
-
|
45
|
-
if len(self.word_filter) > 0:
|
46
|
-
|
11
|
+
if self.word_filter and len(self.word_filter) > 0:
|
47
12
|
for words in self.word_filter:
|
48
|
-
|
49
13
|
string_words: list[str] = [str(word) for word in words]
|
50
|
-
|
51
14
|
for word in string_words:
|
52
15
|
if word in record["message"]:
|
53
16
|
record["message"] = 'Log Alterado devido a palavra Filtrada!'
|
54
17
|
return True
|
55
|
-
|
56
18
|
return True
|
57
19
|
|
58
20
|
|
59
|
-
class CustomHandler
|
60
|
-
"""
|
61
|
-
Class that provides a custom logging handler to manage log messages.
|
62
|
-
|
63
|
-
This class allows for the formatting and writing of log messages to a specified output.
|
64
|
-
It utilizes a custom formatter to structure the log messages, including details such as
|
65
|
-
the time of logging, log level, and the message itself.
|
66
|
-
|
67
|
-
Methods:
|
68
|
-
write: Writes the formatted log message to the output.
|
69
|
-
|
70
|
-
Example:
|
71
|
-
>>> handler = CustomHandler(formatter=CustomFormatter())
|
72
|
-
>>> handler.write({"time": "12:00", "level": "INFO", "message": "This is a log message."})
|
73
|
-
|
74
|
-
pt-br
|
75
|
-
----------
|
76
|
-
Classe que fornece um manipulador de log personalizado para gerenciar mensagens de log.
|
77
|
-
|
78
|
-
Esta classe permite a formatação e escrita de mensagens de log em uma saída especificada.
|
79
|
-
Ela utiliza um formatador personalizado para estruturar as mensagens de log, incluindo detalhes como
|
80
|
-
o horário do log, nível de log e a própria mensagem.
|
81
|
-
|
82
|
-
Métodos:
|
83
|
-
write: Escreve a mensagem de log formatada na saída.
|
84
|
-
|
85
|
-
Exemplo:
|
86
|
-
>>> manipulador = CustomHandler(formatter=CustomFormatter())
|
87
|
-
>>> manipulador.write({"time": "12:00", "level": "INFO", "message": "Esta é uma mensagem de log."})
|
88
|
-
"""
|
21
|
+
class CustomHandler:
|
89
22
|
def __init__(self, formatter):
|
90
23
|
self.formatter = formatter
|
91
24
|
|
@@ -96,63 +29,14 @@ class CustomHandler():
|
|
96
29
|
|
97
30
|
|
98
31
|
class CustomFormatter:
|
99
|
-
"""
|
100
|
-
Class that provides a custom formatter for log messages.
|
101
|
-
|
102
|
-
This class is responsible for formatting log messages in a structured way,
|
103
|
-
allowing for easy readability and understanding of log entries. It formats
|
104
|
-
the log messages to include details such as the time of logging, log level,
|
105
|
-
the filename, line number, and the actual log message.
|
106
|
-
|
107
|
-
Methods:
|
108
|
-
format: Formats the log message based on the provided record.
|
109
|
-
|
110
|
-
Example:
|
111
|
-
>>> formatter = CustomFormatter()
|
112
|
-
>>> log_message = formatter.format({
|
113
|
-
... "time": "12:00",
|
114
|
-
... "level": "INFO",
|
115
|
-
... "message": "This is a log message."
|
116
|
-
... })
|
117
|
-
>>> print(log_message)
|
118
|
-
|
119
|
-
pt-br
|
120
|
-
----------
|
121
|
-
Classe que fornece um formatador personalizado para mensagens de log.
|
122
|
-
|
123
|
-
Esta classe é responsável por formatar mensagens de log de maneira estruturada,
|
124
|
-
permitindo fácil leitura e compreensão das entradas de log. Ela formata
|
125
|
-
as mensagens de log para incluir detalhes como o horário do log, nível de log,
|
126
|
-
o nome do arquivo, número da linha e a mensagem de log real.
|
127
|
-
|
128
|
-
Métodos:
|
129
|
-
format: Formata a mensagem de log com base no registro fornecido.
|
130
|
-
|
131
|
-
Exemplo:
|
132
|
-
>>> formatador = CustomFormatter()
|
133
|
-
>>> mensagem_log = formatador.format({
|
134
|
-
... "time": "12:00",
|
135
|
-
... "level": "INFO",
|
136
|
-
... "message": "Esta é uma mensagem de log."
|
137
|
-
... })
|
138
|
-
>>> print(mensagem_log)
|
139
|
-
"""
|
140
32
|
def format(self, record):
|
141
|
-
|
142
33
|
frame = inspect.currentframe().f_back
|
143
34
|
full_path_filename = frame.f_code.co_filename
|
144
|
-
|
145
|
-
# Obtenha o nome do arquivo e o nome da pasta
|
146
35
|
filename = os.path.basename(full_path_filename)
|
147
36
|
foldername = os.path.basename(os.path.dirname(full_path_filename))
|
148
|
-
|
149
|
-
# Combine o nome da pasta e o nome do arquivo
|
150
37
|
filename = os.path.join(foldername, filename)
|
151
38
|
lineno = frame.f_lineno
|
152
|
-
|
153
|
-
# Formate a mensagem de log TERMINAL
|
154
39
|
format_string = "<green>{time:DD.MM.YY.HH:mm}</green> <level>{level: <8}</level> <level>{message}</level>\n"
|
155
|
-
|
156
40
|
log_msg = format_string.format(
|
157
41
|
time=record["time"],
|
158
42
|
level=record["level"].name,
|
@@ -163,303 +47,93 @@ class CustomFormatter:
|
|
163
47
|
return log_msg
|
164
48
|
|
165
49
|
|
166
|
-
|
167
|
-
|
168
|
-
class Log():
|
169
|
-
|
170
|
-
"""
|
171
|
-
Class that provides utilities for logging messages in a structured manner.
|
172
|
-
|
173
|
-
This class is responsible for managing log entries, allowing for easy tracking
|
174
|
-
and debugging of application behavior. It supports various logging levels and
|
175
|
-
can be configured to log messages to different outputs, such as files or the console.
|
176
|
-
|
177
|
-
Methods:
|
178
|
-
config_logger: Configures the logger with specified parameters.
|
179
|
-
|
180
|
-
Example:
|
181
|
-
>>> logger = Log()
|
182
|
-
>>> logger.config_logger(path_dir='logs', name_log_dir='my_logs', name_file_log='app_log')
|
183
|
-
|
184
|
-
pt-br
|
185
|
-
----------
|
186
|
-
Classe que fornece utilitários para registrar mensagens de forma estruturada.
|
187
|
-
|
188
|
-
Esta classe é responsável por gerenciar entradas de log, permitindo fácil rastreamento
|
189
|
-
e depuração do comportamento da aplicação. Suporta vários níveis de log e
|
190
|
-
pode ser configurada para registrar mensagens em diferentes saídas, como arquivos ou console.
|
191
|
-
|
192
|
-
Métodos:
|
193
|
-
config_logger: Configura o logger com parâmetros especificados.
|
194
|
-
|
195
|
-
Exemplo:
|
196
|
-
>>> logger = Log()
|
197
|
-
>>> logger.config_logger(path_dir='logs', name_log_dir='meus_logs', name_file_log='log_app')
|
198
|
-
"""
|
199
|
-
|
50
|
+
class Log:
|
200
51
|
filters: Filters
|
201
52
|
custom_handler: CustomHandler
|
202
53
|
custom_formatter: CustomFormatter
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
def config_logger(self,
|
209
|
-
path_dir:str = None,
|
210
|
-
name_log_dir:str = None,
|
211
|
-
name_file_log: str = 'log',
|
212
|
-
use_default_path_and_name: bool = True,
|
213
|
-
filter_words: list[str] = None):
|
54
|
+
path_dir: str | None = None
|
55
|
+
name_file_log: str | None = None
|
56
|
+
full_path: str | None = None
|
57
|
+
file_handler = None
|
214
58
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
pt-br
|
219
|
-
----------
|
220
|
-
Function responsible for create a object logger with fileHandler and streamHandler
|
221
|
-
"""
|
59
|
+
def __init__(self):
|
60
|
+
self.logger = logger
|
222
61
|
|
62
|
+
def config_logger(self, path_dir: str = 'default', name_log_dir: str = 'Logs', name_file_log: str = 'log', filter_words: list[str] = None):
|
223
63
|
try:
|
64
|
+
self.path_dir = path_dir
|
65
|
+
self.name_file_log = name_file_log
|
224
66
|
|
225
|
-
if
|
226
|
-
|
227
|
-
path_dir = result_tryed['path_created']
|
228
|
-
else:
|
229
|
-
if path_dir == None and name_log_dir == None:
|
230
|
-
result_tryed: dict = _create_log_dir()
|
231
|
-
path_dir = result_tryed['path_created']
|
67
|
+
if self.path_dir == 'default':
|
68
|
+
self.path_dir = os.getcwd()
|
232
69
|
|
70
|
+
full_path = os.path.join(self.path_dir, name_log_dir)
|
71
|
+
self.full_path = full_path
|
233
72
|
|
234
|
-
|
235
|
-
|
73
|
+
try:
|
74
|
+
os.makedirs(self.full_path, exist_ok=True)
|
75
|
+
success_print(f"Diretório:'{self.full_path}' foi criado com sucesso.")
|
76
|
+
except FileExistsError:
|
77
|
+
alert_print(f"Diretório:'{self.full_path}' já existe.")
|
78
|
+
except PermissionError:
|
79
|
+
alert_print(f"Permissão negada: não é possível criar o diretório '{self.full_path}'.")
|
80
|
+
|
81
|
+
new_filter = None
|
236
82
|
if filter_words is not None:
|
237
|
-
new_filter
|
83
|
+
new_filter = Filters()
|
238
84
|
new_filter.word_filter = [filter_words]
|
239
85
|
|
86
|
+
file_handler = os.path.join(self.full_path, f"{self.name_file_log}.log")
|
87
|
+
self.logger.remove()
|
240
88
|
|
241
|
-
|
242
|
-
file_handler = fr'{path_dir}\{name_file_log}.log'
|
243
|
-
logger.remove()
|
244
|
-
|
245
|
-
# Formate a mensagem de log FILE
|
246
|
-
log_format: str = "<green>{time:DD.MM.YY.HH:mm}</green> <level>{level: <8}</level> <green>{extra[filename]}</green>:{extra[lineno]: <4} <level>{message}</level>"
|
247
|
-
|
89
|
+
log_format = "<green>{time:DD.MM.YY.HH:mm}</green> <level>{level: <8}</level> <green>{extra[filename]}</green>:{extra[lineno]: <4} <level>{message}</level>"
|
248
90
|
|
249
91
|
formatter = CustomFormatter()
|
250
|
-
if new_filter is not None:
|
251
|
-
logger.add(file_handler, filter=new_filter, level="DEBUG", format=log_format)
|
252
|
-
else:
|
253
|
-
logger.add(file_handler, level="DEBUG", format=log_format)
|
254
92
|
|
255
|
-
|
256
|
-
|
93
|
+
if new_filter:
|
94
|
+
self.logger.add(file_handler, filter=new_filter, level="DEBUG", format=log_format)
|
95
|
+
else:
|
96
|
+
self.logger.add(file_handler, level="DEBUG", format=log_format)
|
257
97
|
|
98
|
+
self.logger.add(sys.stderr, level="DEBUG", format=formatter.format)
|
99
|
+
self.file_handler = file_handler
|
258
100
|
return file_handler
|
259
101
|
|
260
102
|
except Exception as e:
|
261
|
-
|
262
103
|
error_print(f'Houve um erro durante a execução da função: {self.config_logger.__name__}! Error: {str(e)}.')
|
263
104
|
return None
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
def log_start_run_debug(self,
|
268
|
-
msg_start_loggin: str) -> None: # represent start application
|
269
|
-
|
270
|
-
"""
|
271
|
-
Function responsable to generate ``start run log level debug``, in file and print on terminal the same log captured on this call.
|
272
|
-
|
273
|
-
pt-br
|
274
|
-
----------
|
275
|
-
Função responsável por gerar o nível de log ``início execução nível debug``, em arquivo e imprimir no terminal o mesmo log capturado nesta chamada.
|
276
|
-
"""
|
277
|
-
|
278
|
-
file_h: False
|
279
|
-
|
280
|
-
try:
|
281
|
-
file_h = self.config_logger()
|
282
|
-
|
283
|
-
except Exception as e:
|
284
|
-
|
285
|
-
error_print(f'Para usar o log_start_run_debug é necessario instanciar file_handler usando o arquivo "logger_uru" em algum arquivo de configuração do seu projeto primeiramente! Error: {str(e)}')
|
286
105
|
|
106
|
+
def _log(self, level: str, msg: str):
|
287
107
|
try:
|
288
|
-
try:
|
289
|
-
if file_h:
|
290
|
-
with open(file_h, 'a') as f:
|
291
|
-
f.write('\n')
|
292
|
-
|
293
|
-
except Exception as e:
|
294
|
-
alert_print(f'Não foi possivel gerar break_row para log inicial!')
|
295
|
-
|
296
|
-
# logger.debug(f'{msg_start_loggin}')
|
297
108
|
frame = inspect.currentframe().f_back
|
298
109
|
full_path_filename = frame.f_code.co_filename
|
299
|
-
|
300
|
-
# Obtenha o nome do arquivo e o nome da pasta
|
301
110
|
filename = os.path.basename(full_path_filename)
|
302
111
|
foldername = os.path.basename(os.path.dirname(full_path_filename))
|
303
|
-
|
304
|
-
# Combine o nome da pasta e o nome do arquivo
|
305
112
|
filename = os.path.join(foldername, filename)
|
306
113
|
lineno = frame.f_lineno
|
307
|
-
|
308
|
-
# Vincule o nome do arquivo e a linha à mensagem de log
|
309
|
-
logger.bind(filename=filename, lineno=lineno).debug(f'{msg_start_loggin}')
|
310
|
-
|
114
|
+
self.logger.bind(filename=filename, lineno=lineno).log(level, msg)
|
311
115
|
except Exception as e:
|
312
|
-
error_print(f'Erro durante a função
|
313
|
-
|
314
|
-
|
315
|
-
def log_debug(
|
316
|
-
self,
|
317
|
-
msg: str) -> None:
|
318
|
-
|
319
|
-
"""
|
320
|
-
Function responsable to generate log level ``debug``, in file and print on terminal the same log captured on this call.
|
321
|
-
|
322
|
-
pt-br
|
323
|
-
-----
|
324
|
-
Função responsável por gerar o nível de log ``debug``, em arquivo e imprimir no terminal o mesmo log capturado nesta chamada.
|
325
|
-
"""
|
116
|
+
error_print(f'Erro durante a função de log! Error: {str(e)}')
|
326
117
|
|
118
|
+
def log_start_run_debug(self, msg_start_loggin: str) -> None:
|
327
119
|
try:
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
# Obtenha o nome do arquivo e o nome da pasta
|
332
|
-
filename = os.path.basename(full_path_filename)
|
333
|
-
foldername = os.path.basename(os.path.dirname(full_path_filename))
|
334
|
-
|
335
|
-
# Combine o nome da pasta e o nome do arquivo
|
336
|
-
filename = os.path.join(foldername, filename)
|
337
|
-
lineno = frame.f_lineno
|
338
|
-
|
339
|
-
# Vincule o nome do arquivo e a linha à mensagem de log
|
340
|
-
logger.bind(filename=filename, lineno=lineno).debug(msg)
|
341
|
-
|
342
|
-
except Exception as e:
|
343
|
-
error_print(f'Erro durante a função: {self.log_debug.__name__}! Error: {str(e)}')
|
344
|
-
|
345
|
-
def log_info(
|
346
|
-
self,
|
347
|
-
msg: str) -> None:
|
348
|
-
|
349
|
-
"""
|
350
|
-
Function responsable to generate log level ``info``, in file and print on terminal the same log captured on this call.
|
351
|
-
|
352
|
-
pt-br
|
353
|
-
-----
|
354
|
-
Função responsável por gerar o nível de log ``info``, em arquivo e imprimir no terminal o mesmo log capturado nesta chamada.
|
355
|
-
"""
|
356
|
-
|
357
|
-
try:
|
358
|
-
frame = inspect.currentframe().f_back
|
359
|
-
full_path_filename = frame.f_code.co_filename
|
360
|
-
|
361
|
-
# Obtenha o nome do arquivo e o nome da pasta
|
362
|
-
filename = os.path.basename(full_path_filename)
|
363
|
-
foldername = os.path.basename(os.path.dirname(full_path_filename))
|
364
|
-
|
365
|
-
# Combine o nome da pasta e o nome do arquivo
|
366
|
-
filename = os.path.join(foldername, filename)
|
367
|
-
lineno = frame.f_lineno
|
368
|
-
|
369
|
-
# Vincule o nome do arquivo e a linha à mensagem de log
|
370
|
-
logger.bind(filename=filename, lineno=lineno).info(msg)
|
371
|
-
|
372
|
-
except Exception as e:
|
373
|
-
error_print(f'Erro durante a função: {self.log_info.__name__}! Error: {str(e)}')
|
374
|
-
|
375
|
-
def log_warning(self,
|
376
|
-
msg: str) -> None:
|
377
|
-
|
378
|
-
"""
|
379
|
-
Function responsable to generate log level ``warning``, in file and print on terminal the same log captured on this call.
|
380
|
-
|
381
|
-
pt-br
|
382
|
-
-----
|
383
|
-
Função responsável por gerar o nível de log ``aviso``, em arquivo e imprimir no terminal o mesmo log capturado nesta chamada.
|
384
|
-
"""
|
385
|
-
|
386
|
-
try:
|
387
|
-
frame = inspect.currentframe().f_back
|
388
|
-
full_path_filename = frame.f_code.co_filename
|
389
|
-
|
390
|
-
# Obtenha o nome do arquivo e o nome da pasta
|
391
|
-
filename = os.path.basename(full_path_filename)
|
392
|
-
foldername = os.path.basename(os.path.dirname(full_path_filename))
|
393
|
-
|
394
|
-
# Combine o nome da pasta e o nome do arquivo
|
395
|
-
filename = os.path.join(foldername, filename)
|
396
|
-
lineno = frame.f_lineno
|
397
|
-
|
398
|
-
# Vincule o nome do arquivo e a linha à mensagem de log
|
399
|
-
logger.bind(filename=filename, lineno=lineno).warning(msg)
|
400
|
-
|
401
|
-
except Exception as e:
|
402
|
-
error_print(f'Erro durante a função: {self.log_warning.__name__}! Error: {str(e)}')
|
403
|
-
|
404
|
-
|
405
|
-
def log_error(
|
406
|
-
self,
|
407
|
-
msg: str) -> None:
|
408
|
-
|
409
|
-
"""
|
410
|
-
Function responsable to generate log level ``error``, in file and print on terminal the same log captured on this call.
|
411
|
-
|
412
|
-
pt-br
|
413
|
-
-----
|
414
|
-
Função responsável por gerar o nível de log ``erro``, em arquivo e imprimir no terminal o mesmo log capturado nesta chamada.
|
415
|
-
"""
|
416
|
-
|
417
|
-
try:
|
418
|
-
frame = inspect.currentframe().f_back
|
419
|
-
full_path_filename = frame.f_code.co_filename
|
420
|
-
|
421
|
-
# Obtenha o nome do arquivo e o nome da pasta
|
422
|
-
filename = os.path.basename(full_path_filename)
|
423
|
-
foldername = os.path.basename(os.path.dirname(full_path_filename))
|
424
|
-
|
425
|
-
# Combine o nome da pasta e o nome do arquivo
|
426
|
-
filename = os.path.join(foldername, filename)
|
427
|
-
lineno = frame.f_lineno
|
428
|
-
|
429
|
-
# Vincule o nome do arquivo e a linha à mensagem de log
|
430
|
-
logger.bind(filename=filename, lineno=lineno).error(msg)
|
431
|
-
|
120
|
+
with open(self.file_handler, "a") as log_file:
|
121
|
+
log_file.write("\n") # Add a blank line before logging the start message
|
122
|
+
self._log("DEBUG", msg_start_loggin)
|
432
123
|
except Exception as e:
|
433
|
-
error_print(f'Erro
|
434
|
-
|
435
|
-
|
436
|
-
def log_critical(self,
|
437
|
-
msg: str) -> None:
|
438
|
-
|
439
|
-
"""
|
440
|
-
Function responsable to generate log level ``critical``, in file and print on terminal the same log captured on this call.
|
441
|
-
|
442
|
-
pt-br
|
443
|
-
----------
|
444
|
-
|
445
|
-
Função responsável por gerar o nível de log ``crítico``, em arquivo e imprimir no terminal o mesmo log capturado nesta chamada.
|
446
|
-
"""
|
447
|
-
|
448
|
-
try:
|
449
|
-
frame = inspect.currentframe().f_back
|
450
|
-
full_path_filename = frame.f_code.co_filename
|
124
|
+
error_print(f'Erro fn: {self.log_start_run_debug.__name__} ao tentar acessar o arquivo de log Confira se foi criado a configuração de log correta com a função config_logger e se a pasta e arquivo estão nos diretório desejado! Error: {str(e)}.')
|
451
125
|
|
452
|
-
|
453
|
-
|
454
|
-
foldername = os.path.basename(os.path.dirname(full_path_filename))
|
126
|
+
def log_debug(self, msg: str) -> None:
|
127
|
+
self._log("DEBUG", msg)
|
455
128
|
|
456
|
-
|
457
|
-
|
458
|
-
lineno = frame.f_lineno
|
129
|
+
def log_info(self, msg: str) -> None:
|
130
|
+
self._log("INFO", msg)
|
459
131
|
|
460
|
-
|
461
|
-
|
132
|
+
def log_warning(self, msg: str) -> None:
|
133
|
+
self._log("WARNING", msg)
|
462
134
|
|
463
|
-
|
464
|
-
|
135
|
+
def log_error(self, msg: str) -> None:
|
136
|
+
self._log("ERROR", msg)
|
465
137
|
|
138
|
+
def log_critical(self, msg: str) -> None:
|
139
|
+
self._log("CRITICAL", msg)
|
rpa_suite/suite.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# rpa_suite/suite.py
|
2
|
+
|
1
3
|
from .core.clock import Clock
|
2
4
|
from .core.date import Date
|
3
5
|
from .core.email import Email
|
@@ -7,8 +9,12 @@ from .core.log import Log
|
|
7
9
|
from .core.print import Print
|
8
10
|
from .core.regex import Regex
|
9
11
|
from .core.validate import Validate
|
12
|
+
|
10
13
|
from colorama import Fore
|
11
14
|
|
15
|
+
import subprocess
|
16
|
+
import sys
|
17
|
+
import hashlib
|
12
18
|
|
13
19
|
# Windows bash colors
|
14
20
|
class Colors():
|
@@ -122,6 +128,7 @@ class Suite():
|
|
122
128
|
``validate``: Funções de validação de dados
|
123
129
|
"""
|
124
130
|
|
131
|
+
# SUBMODULES
|
125
132
|
clock: Clock = Clock()
|
126
133
|
date: Date = Date()
|
127
134
|
email: Email = Email()
|
@@ -131,30 +138,36 @@ class Suite():
|
|
131
138
|
printer: Print = Print()
|
132
139
|
regex: Regex = Regex()
|
133
140
|
validate: Validate = Validate()
|
141
|
+
|
142
|
+
|
143
|
+
|
144
|
+
# VARIABLES INTERNAL
|
145
|
+
__version__ = '1.4.5'
|
146
|
+
__id_hash__ = 'rpa_suite'
|
147
|
+
|
134
148
|
|
135
149
|
def __init__(self):
|
136
|
-
|
150
|
+
self.__id_hash__ = hashlib.sha256(self.__version__.encode()).hexdigest()
|
137
151
|
|
138
152
|
def success_print(self,
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
print(f'{color}{string_text}{Colors.default}', end=ending)
|
153
|
+
string_text: str,
|
154
|
+
color=Colors.green,
|
155
|
+
ending="\n") -> None:
|
156
|
+
|
157
|
+
"""
|
158
|
+
Print that indicates ``SUCCESS``. Customized with the color Green \n
|
159
|
+
Return:
|
160
|
+
----------
|
161
|
+
>>> type:None
|
162
|
+
pt-br
|
163
|
+
----------
|
164
|
+
Print que indica ``SUCESSO``. Personalizado com a cor Verde \n
|
165
|
+
Retorno:
|
166
|
+
----------
|
167
|
+
>>> type:None
|
168
|
+
"""
|
169
|
+
|
170
|
+
print(f'{color}{string_text}{Colors.default}', end=ending)
|
158
171
|
|
159
172
|
|
160
173
|
def alert_print(self,
|
@@ -311,8 +324,7 @@ class Suite():
|
|
311
324
|
Metodo responsavel por instalar todas libs para uso avançado do RPA-Suite com todas funcionalidades incluindo OCR e agente de IA
|
312
325
|
"""
|
313
326
|
|
314
|
-
|
315
|
-
import sys
|
327
|
+
|
316
328
|
|
317
329
|
libs = [
|
318
330
|
'colorama',
|
@@ -334,3 +346,5 @@ class Suite():
|
|
334
346
|
|
335
347
|
except subprocess.CalledProcessError:
|
336
348
|
self.error_print(f'Erro ao instalar biblioteca {lib}')
|
349
|
+
|
350
|
+
rpa = Suite()
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# rpa_suite/utils/__init__.py
|
2
|
+
"""
|
3
|
+
The utils module of the rpa-suite provides a collection of utility functions and classes that facilitate various tasks in process automation. It includes helper methods for logging, file handling, and other common operations that enhance the functionality and usability of the RPA Suite.
|
4
|
+
|
5
|
+
pt-br
|
6
|
+
------
|
7
|
+
|
8
|
+
O módulo de utilitários da rpa-suite fornece uma coleção de funções e classes utilitárias que facilitam diversas tarefas na automação de processos. Inclui métodos auxiliares para registro de log, manipulação de arquivos e outras operações comuns que aprimoram a funcionalidade e a usabilidade da RPA Suite.
|
9
|
+
|
10
|
+
"""
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from rpa_suite.functions._printer import error_print, success_print
|
2
|
+
import sys, os
|
3
|
+
|
4
|
+
def set_importable_dir(display_message: bool = False):
|
5
|
+
"""
|
6
|
+
Sets the directory to be importable by appending it to the system path.
|
7
|
+
|
8
|
+
Parameters:
|
9
|
+
----------
|
10
|
+
display_message: bool - If True, displays a success message after setting the directory.
|
11
|
+
|
12
|
+
Returns:
|
13
|
+
----------
|
14
|
+
None
|
15
|
+
|
16
|
+
pt-br
|
17
|
+
----------
|
18
|
+
Define o diretório para ser importável, adicionando-o ao caminho do sistema.
|
19
|
+
|
20
|
+
Parâmetros:
|
21
|
+
----------
|
22
|
+
display_message: bool - Se True, exibe uma mensagem de sucesso após definir o diretório.
|
23
|
+
|
24
|
+
Retornos:
|
25
|
+
----------
|
26
|
+
Nenhum
|
27
|
+
"""
|
28
|
+
try:
|
29
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
30
|
+
|
31
|
+
if display_message: success_print(f'Successfully set the directory for importation!')
|
32
|
+
except Exception as e:
|
33
|
+
error_print(f'An error occurred while executing the function: {set_importable_dir.__name__}! Error: {str(e)}.')
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: rpa_suite
|
3
|
-
Version: 1.4.
|
3
|
+
Version: 1.4.5
|
4
4
|
Summary: Conjunto de ferramentas essenciais para Automação RPA com Python, que facilitam o dia a dia de desenvolvimento.
|
5
5
|
Author: Camilo Costa de Carvalho
|
6
6
|
Author-email: camilo.carvalho@triasoftware.com.br
|
@@ -145,6 +145,14 @@ No setup do nosso projeto já estão inclusas as dependências, só será necess
|
|
145
145
|
- loguru
|
146
146
|
- email-validator
|
147
147
|
- colorlog
|
148
|
+
- pillow
|
149
|
+
- pyautogui
|
150
|
+
- typing
|
151
|
+
|
152
|
+
opcionalmente para automação de navegador:
|
153
|
+
|
154
|
+
- selenium
|
155
|
+
- webdriver_manager
|
148
156
|
|
149
157
|
[!IMPORTANT]
|
150
158
|
No caso da função de screenshot é necessario ter as libs 'pyautogui' 'pillow' e 'pyscreeze' instalados, geralmente a instalação de pyautogui já instala as demais dependências deste caso.
|
@@ -153,6 +161,8 @@ No caso da função de screenshot é necessario ter as libs 'pyautogui' 'pillow'
|
|
153
161
|
|
154
162
|
O módulo principal do rpa-suite é dividido em categorias. Cada categoria contém módulos com funções destinadas a cada tipo de tarefa
|
155
163
|
|
164
|
+
! Divisao e nomes de funções precisam ser atualizadas nesta sessão de Estrutura do módulo !
|
165
|
+
|
156
166
|
- **rpa_suite**
|
157
167
|
- **clock**
|
158
168
|
- **waiter** - Função capaz de aguardar para executar a função do argumento, ou executar a função do argumento para aguardar posteriormente
|
@@ -178,26 +188,26 @@ O módulo principal do rpa-suite é dividido em categorias. Cada categoria cont
|
|
178
188
|
|
179
189
|
## Release
|
180
190
|
|
181
|
-
Versão: **Beta 1.
|
191
|
+
Versão: **Beta 1.4.5**
|
182
192
|
|
183
193
|
Lançamento: *20/02/2024*
|
184
194
|
|
185
|
-
Última atualização: *10/
|
195
|
+
Última atualização: *10/04/2025*
|
186
196
|
|
187
197
|
Status: Em desenvolvimento.
|
188
198
|
|
189
|
-
### Notas da atualização: 1.4.
|
190
|
-
|
191
|
-
-
|
192
|
-
-
|
193
|
-
-
|
194
|
-
-
|
195
|
-
-
|
196
|
-
-
|
197
|
-
-
|
198
|
-
-
|
199
|
-
-
|
200
|
-
-
|
199
|
+
### Notas da atualização: 1.4.5
|
200
|
+
|
201
|
+
- Mudança dos submodulos para Objetos, agora Rpa_suite é um Objeto de Suite que compoe diversos sub-objetos separados pelas mesmas categorias anteriormente ja definidas
|
202
|
+
- Reformulada a arquitetura do projeto para melhor coeção e menos subpastas e arquivos, agora a estrutura é mais simples e de melhor manutenção contendo apenas uma pasta core para o nucleo de nossos modulos, e uma pasta utils com ferramentas utilitarias que vamos adicionar varias novidades
|
203
|
+
- Adicionado setor utils com funcionalidade de tornar o diretorio atual em um importavel relativo para o python
|
204
|
+
- Adicionado Automação de Navegadores! Sim estamos devendo esta feature a bastante tempo, porem estamos com a primeira versão disponivel, em breve teremos mais recursos e disponibilidade para outros navegadores, atualmente suporte apenas para *Chrome.*
|
205
|
+
- Mantemos o alerta! **get_dma** atualizada e **renomeada** para **get_dmy** para manter o padrão em ingles
|
206
|
+
- Função *send_email* atualizada para simplificar seu uso e funcionalidade em uma maior variedade de maquinas
|
207
|
+
- Melhoria nas descrições das funções e adicionado docstring (documentação explicativa) de todos Objetos e respectivas funções
|
208
|
+
- Sub-modulos agora como são Objetos internos do Objeto principal Suite pode ser acessado de duas formas, ex1: " from rpa_suite import rpa ; rpa.modulo.function() " ou então ex2: "from rpa_suite.core.Submodulo import NomeModulo ; meu_obj = NomeModulo() ; meu_obj.function()",
|
209
|
+
- Funções de regex e busca em textos foi simplificada e em breve estaremos adicionando funcionalidades mais interessantes.
|
210
|
+
- Correção e melhoria do Submodulo de Log. Tinhamos dois formatos de Log com duas bibliotecas e decidimos optar apenas pela Loguru para podermos dedicar mais atenção e fornecer recursos de Log mais completos, agora possui funcionalidade para indicar o caminho da pasta, nome da pasta, e também nome do arquivo, realizando stream tanto para o console (terminal) como também para o arquivo com todos levels já configurados e personalizados para ter distinção e facilitar o reconhecimento visual do que esta acontecendo no seu projeto.
|
201
211
|
|
202
212
|
## Mais Sobre
|
203
213
|
|
@@ -1,12 +1,13 @@
|
|
1
|
-
rpa_suite/__init__.py,sha256=
|
2
|
-
rpa_suite/suite.py,sha256=
|
3
|
-
rpa_suite/core/__init__.py,sha256=
|
4
|
-
rpa_suite/core/
|
1
|
+
rpa_suite/__init__.py,sha256=hTuXV88YlUdc8It7kyIOC9X61iFm1kZ7Nc8Vi8x0hE0,2412
|
2
|
+
rpa_suite/suite.py,sha256=pCbceWTf9WM_xt0GSf_k7Xnk4w_qHKS1hLscYqV5Ga0,10280
|
3
|
+
rpa_suite/core/__init__.py,sha256=e7jg0y6Zqcextw3DETOGQ4jAT9g9JpG5G9qLMtO8zqc,1076
|
4
|
+
rpa_suite/core/browser.py,sha256=HyO-nFHrxPr-StMmtAh_wdaf5YD_J5poh85B71BVbyM,6341
|
5
|
+
rpa_suite/core/clock.py,sha256=1QnlOLs9YCRIq-tMFfk9OgaoicKtCL9sBzJmJmz9m_w,13808
|
5
6
|
rpa_suite/core/date.py,sha256=PBU999Acxiwoep029ElqKSzK6T4DrF3eIP-LB_J-BbM,6568
|
6
7
|
rpa_suite/core/dir.py,sha256=y6YDyRYQdf9Bj0z3Gs6ugNZ0tKWYgWdDI5R5BjjRIEY,9783
|
7
8
|
rpa_suite/core/email.py,sha256=kQJAc6Nb9y7jo-oBAo8X1EZS2y-_gTJRoRc9ylS04CE,8675
|
8
9
|
rpa_suite/core/file.py,sha256=xuXaV_jon8hJ1Fb2j5DukXfnAzZRmtFgVJhTQs8dptU,11572
|
9
|
-
rpa_suite/core/log.py,sha256=
|
10
|
+
rpa_suite/core/log.py,sha256=xCAoXLxnG2bVzXIafULvW45I4-ljo296E4auU1CppYY,5537
|
10
11
|
rpa_suite/core/print.py,sha256=tLHIKo6LDTrV91gWKvwlTrxb1idgx3EV_fIRkqtzWBM,6389
|
11
12
|
rpa_suite/core/regex.py,sha256=wsTxe8-baKul2Fv1-fycXZ-DVa5krhkc9qMkP04giGs,3303
|
12
13
|
rpa_suite/core/validate.py,sha256=0HnCUgT19LPBMhKxIV_hOAoixpqM7TAL9EkC0S56DFc,10976
|
@@ -18,8 +19,10 @@ rpa_suite/functions/_logger.py,sha256=gTYO9JlbX5_jDfu_4FTTajJw3_GotE2BHUbDDB1Hf5
|
|
18
19
|
rpa_suite/functions/_printer.py,sha256=r72zeobAi2baVbYgbfFH0h5-WMv4tSDGPNlcpZen7O0,3949
|
19
20
|
rpa_suite/functions/_variables.py,sha256=vCcktifFUriBQTyUaayZW8BlE8Gr7VP-tFbfomKOS5U,312
|
20
21
|
rpa_suite/functions/_variables_uru.py,sha256=xRqYp49l1fFNrHczOmJ6Pqw1PKIWs0f9kxlgvuYGYys,303
|
21
|
-
rpa_suite
|
22
|
-
rpa_suite
|
23
|
-
rpa_suite-1.4.
|
24
|
-
rpa_suite-1.4.
|
25
|
-
rpa_suite-1.4.
|
22
|
+
rpa_suite/utils/__init__.py,sha256=f0qiYRZ7VzBarp1yNj91V0UMZG9QY2mgDXEbWvNZDYs,673
|
23
|
+
rpa_suite/utils/system.py,sha256=JIONQutSbNBkQIATcN1wC0NUE4ILlgVZ-TOnuoMPxHI,1055
|
24
|
+
rpa_suite-1.4.5.dist-info/licenses/LICENSE,sha256=5D8PIbs31iGd9i1_MDNg4SzaQnp9sEIULALh2y3WyMI,1102
|
25
|
+
rpa_suite-1.4.5.dist-info/METADATA,sha256=rygafgfaMRQ36VhJ7kRuy0i7NbGn-YCOPCRNISY9rAs,10667
|
26
|
+
rpa_suite-1.4.5.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
27
|
+
rpa_suite-1.4.5.dist-info/top_level.txt,sha256=HYkDtg-kJNAr3F2XAIPyJ-QBbNhk7q6jrqsFt10lz4Y,10
|
28
|
+
rpa_suite-1.4.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|