worker-automate-hub 0.5.749__py3-none-any.whl → 0.5.912__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.
- worker_automate_hub/api/client.py +186 -68
- worker_automate_hub/api/rpa_historico_service.py +1 -0
- worker_automate_hub/cli.py +91 -111
- worker_automate_hub/tasks/jobs/abertura_livros_fiscais.py +112 -229
- worker_automate_hub/tasks/jobs/descartes.py +91 -77
- worker_automate_hub/tasks/jobs/devolucao_produtos.py +1386 -0
- worker_automate_hub/tasks/jobs/entrada_de_notas_15.py +3 -46
- worker_automate_hub/tasks/jobs/entrada_de_notas_22.py +833 -0
- worker_automate_hub/tasks/jobs/entrada_de_notas_36.py +29 -9
- worker_automate_hub/tasks/jobs/entrada_de_notas_37.py +619 -0
- worker_automate_hub/tasks/jobs/entrada_de_notas_39.py +1 -1
- worker_automate_hub/tasks/jobs/entrada_de_notas_9.py +63 -16
- worker_automate_hub/tasks/jobs/extracao_dados_nielsen.py +504 -0
- worker_automate_hub/tasks/jobs/extracao_saldo_estoque.py +242 -108
- worker_automate_hub/tasks/jobs/extracao_saldo_estoque_fiscal.py +688 -0
- worker_automate_hub/tasks/jobs/fidc_gerar_nosso_numero.py +2 -2
- worker_automate_hub/tasks/jobs/fidc_remessa_cobranca_cnab240.py +25 -16
- worker_automate_hub/tasks/jobs/geracao_balancetes_filial.py +330 -0
- worker_automate_hub/tasks/jobs/importacao_extratos.py +538 -0
- worker_automate_hub/tasks/jobs/importacao_extratos_748.py +800 -0
- worker_automate_hub/tasks/jobs/inclusao_pedidos_ipiranga.py +222 -0
- worker_automate_hub/tasks/jobs/inclusao_pedidos_raizen.py +174 -0
- worker_automate_hub/tasks/jobs/inclusao_pedidos_vibra.py +327 -0
- worker_automate_hub/tasks/jobs/notas_faturamento_sap.py +438 -157
- worker_automate_hub/tasks/jobs/opex_capex.py +540 -326
- worker_automate_hub/tasks/jobs/sped_fiscal.py +8 -8
- worker_automate_hub/tasks/jobs/transferencias.py +52 -41
- worker_automate_hub/tasks/task_definitions.py +46 -1
- worker_automate_hub/tasks/task_executor.py +11 -0
- worker_automate_hub/utils/util.py +252 -215
- worker_automate_hub/utils/utils_nfe_entrada.py +1 -1
- worker_automate_hub/worker.py +1 -9
- {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/METADATA +4 -2
- {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/RECORD +36 -25
- {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/WHEEL +1 -1
- {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
# IMPORT SELENIUM
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
import json
|
|
6
|
+
import asyncio
|
|
7
|
+
import shutil
|
|
8
|
+
from typing import List
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
from selenium import webdriver
|
|
14
|
+
from selenium.webdriver.common.by import By
|
|
15
|
+
from selenium.webdriver.remote.webelement import WebElement
|
|
16
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
|
17
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
18
|
+
from selenium.webdriver.chrome.service import Service
|
|
19
|
+
from selenium.webdriver.chrome.options import Options
|
|
20
|
+
from selenium.common.exceptions import TimeoutException
|
|
21
|
+
from webdriver_manager.chrome import ChromeDriverManager
|
|
22
|
+
from os import name
|
|
23
|
+
|
|
24
|
+
# from selenium.webdriver.support.ui import WebDriverWait
|
|
25
|
+
from selenium.webdriver.support.wait import WebDriverWait
|
|
26
|
+
from selenium.webdriver.chrome.webdriver import WebDriver
|
|
27
|
+
|
|
28
|
+
ROOT_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
|
29
|
+
sys.path.append(ROOT_PATH)
|
|
30
|
+
|
|
31
|
+
from worker_automate_hub.api.datalake_service import send_file_to_datalake
|
|
32
|
+
from worker_automate_hub.api.client import get_config_by_name
|
|
33
|
+
from worker_automate_hub.models.dto.rpa_historico_request_dto import (
|
|
34
|
+
RpaHistoricoStatusEnum,
|
|
35
|
+
RpaRetornoProcessoDTO,
|
|
36
|
+
RpaTagDTO,
|
|
37
|
+
RpaTagEnum,
|
|
38
|
+
)
|
|
39
|
+
from worker_automate_hub.models.dto.rpa_processo_entrada_dto import (
|
|
40
|
+
RpaProcessoEntradaDTO,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
from worker_automate_hub.utils.util import (
|
|
44
|
+
kill_all_emsys,
|
|
45
|
+
)
|
|
46
|
+
USERNAME = os.environ.get("USERNAME")
|
|
47
|
+
console = Console()
|
|
48
|
+
|
|
49
|
+
HISTORICO_ID = "e8ca47cf-c49b-437c-9028-50bcfa5fe021"
|
|
50
|
+
|
|
51
|
+
# Diretório/bucket no datalake onde os arquivos serão enviados
|
|
52
|
+
DATALAKE_DIRECTORY = "nielsen_arquivos/raw"
|
|
53
|
+
|
|
54
|
+
# Funcões Selenium
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def aguardar_elemento_ser_clicavel_xpath(
|
|
58
|
+
driver: WebDriver, xpath: str, tempo: int
|
|
59
|
+
) -> bool:
|
|
60
|
+
aguardar = WebDriverWait(driver, tempo)
|
|
61
|
+
return aguardar.until(EC.element_to_be_clickable((By.XPATH, xpath)))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def inserir_texto_por_letra_xpath(driver: WebDriver, xpath: str, texto: str):
|
|
65
|
+
for letter in texto:
|
|
66
|
+
driver.find_element(By.XPATH, xpath).send_keys(letter)
|
|
67
|
+
time.sleep(0.1)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def inserir_texto_por_xpath(driver: WebDriver, xpath: str, texto: str):
|
|
71
|
+
driver.find_element(By.XPATH, xpath).send_keys(texto)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def clicar_elemento_por_xpath(driver: WebDriver, xpath: str) -> None:
|
|
75
|
+
driver.find_element(By.XPATH, xpath).click()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def busca_lista_elementos_por_xpath(driver: WebDriver, xpath: str) -> List[WebElement]:
|
|
79
|
+
return driver.find_elements(By.XPATH, xpath)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def limpar_pasta_downloads(caminho: str, remover_pastas: bool = False):
|
|
83
|
+
"""
|
|
84
|
+
Limpa a pasta de downloads utilizada pelo robô.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
caminho (str): Caminho da pasta a ser limpa.
|
|
88
|
+
remover_pastas (bool): Se True, remove também subpastas.
|
|
89
|
+
"""
|
|
90
|
+
if not os.path.exists(caminho):
|
|
91
|
+
console.print(f"Pasta não existe: {caminho}", style="yellow")
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
itens = os.listdir(caminho)
|
|
96
|
+
|
|
97
|
+
if not itens:
|
|
98
|
+
console.print("Pasta já estava vazia.", style="green")
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
for item in itens:
|
|
102
|
+
item_path = os.path.join(caminho, item)
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
# Remover arquivos
|
|
106
|
+
if os.path.isfile(item_path):
|
|
107
|
+
os.remove(item_path)
|
|
108
|
+
console.print(f"Arquivo removido: {item_path}", style="green")
|
|
109
|
+
|
|
110
|
+
# Remover pastas (opcional)
|
|
111
|
+
elif remover_pastas and os.path.isdir(item_path):
|
|
112
|
+
shutil.rmtree(item_path, ignore_errors=True)
|
|
113
|
+
console.print(f"Pasta removida: {item_path}", style="green")
|
|
114
|
+
|
|
115
|
+
except Exception as e:
|
|
116
|
+
console.print(
|
|
117
|
+
f"Não foi possível remover {item_path}: {e}",
|
|
118
|
+
style="bold red",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
console.print("Limpeza concluída!", style="bold green")
|
|
122
|
+
|
|
123
|
+
except Exception as e:
|
|
124
|
+
console.print(f"Erro ao limpar pasta: {e}", style="bold red")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class ExtracaoDados:
|
|
128
|
+
def __init__(self, driver: webdriver.Chrome):
|
|
129
|
+
self.driver = driver
|
|
130
|
+
self.caminho_downloads = rf"C:\Users\{USERNAME}\Downloads"
|
|
131
|
+
self.handles = []
|
|
132
|
+
|
|
133
|
+
async def limpar_downloads(self):
|
|
134
|
+
limpar_pasta_downloads(self.caminho_downloads, remover_pastas=False)
|
|
135
|
+
|
|
136
|
+
async def abrir_site(self):
|
|
137
|
+
await asyncio.sleep(2)
|
|
138
|
+
try:
|
|
139
|
+
console.print(
|
|
140
|
+
"Iniciando navegador e acessando site da Nielsen...", style="cyan"
|
|
141
|
+
)
|
|
142
|
+
self.driver.get("https://web.na-mft.nielseniq.com/cfcc/bclient/index.jsp#/")
|
|
143
|
+
except Exception as e:
|
|
144
|
+
console.print(f"Erro ao abrir o site: {e}", style="bold red")
|
|
145
|
+
raise
|
|
146
|
+
|
|
147
|
+
# Lê credenciais via API usando RpaConfiguracao.conConfiguracao (JSON)
|
|
148
|
+
async def load_config(self):
|
|
149
|
+
try:
|
|
150
|
+
config = await get_config_by_name("nielsen_credenciais")
|
|
151
|
+
|
|
152
|
+
if config is None:
|
|
153
|
+
raise Exception(
|
|
154
|
+
"get_config_by_name retornou None para 'nielsen_credenciais'."
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# conConfiguracao é onde fica o conteúdo da configuração
|
|
158
|
+
raw_valor = getattr(config, "conConfiguracao", None)
|
|
159
|
+
if raw_valor is None:
|
|
160
|
+
raise Exception(
|
|
161
|
+
"Objeto RpaConfiguracao não possui conteúdo em 'conConfiguracao'. "
|
|
162
|
+
f"Atributos disponíveis: {dir(config)}"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Se for string JSON, fazer parse
|
|
166
|
+
if isinstance(raw_valor, str):
|
|
167
|
+
try:
|
|
168
|
+
valor_json = json.loads(raw_valor)
|
|
169
|
+
except Exception as e_json:
|
|
170
|
+
raise Exception(
|
|
171
|
+
f"Falha ao fazer json.loads em conConfiguracao: {e_json}. "
|
|
172
|
+
f"valor bruto: {raw_valor}"
|
|
173
|
+
)
|
|
174
|
+
# Se já vier como dict
|
|
175
|
+
elif isinstance(raw_valor, dict):
|
|
176
|
+
valor_json = raw_valor
|
|
177
|
+
else:
|
|
178
|
+
raise Exception(
|
|
179
|
+
f"Tipo inesperado em conConfiguracao: {type(raw_valor)}. "
|
|
180
|
+
"Esperado str (JSON) ou dict."
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
username = valor_json.get("usuario")
|
|
184
|
+
password = valor_json.get("senha")
|
|
185
|
+
|
|
186
|
+
if not username or not password:
|
|
187
|
+
raise Exception(
|
|
188
|
+
"Usuário ou senha não encontrados no JSON da configuração. "
|
|
189
|
+
f"JSON: {valor_json}"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
return username, password
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
console.print(
|
|
196
|
+
f"Erro ao obter credenciais via get_config_by_name: {e}",
|
|
197
|
+
style="bold red",
|
|
198
|
+
)
|
|
199
|
+
raise
|
|
200
|
+
|
|
201
|
+
async def clicar_diretorio_principal(self, nome_diretorio: str):
|
|
202
|
+
"""
|
|
203
|
+
Clica no diretório principal pelo nome, tentando vários seletores
|
|
204
|
+
para não quebrar com mudanças pequenas de HTML.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
# Possíveis XPaths para localizar o item
|
|
208
|
+
xpaths_possiveis = [
|
|
209
|
+
# 1) span com classe 'file' e texto exato
|
|
210
|
+
f"//span[contains(@class, 'file') and normalize-space(text())='{nome_diretorio}']",
|
|
211
|
+
|
|
212
|
+
# 2) elemento com title igual ao nome (span, a, td, etc.)
|
|
213
|
+
f"//*[@title='{nome_diretorio}' and (self::span or self::a or self::td)]",
|
|
214
|
+
|
|
215
|
+
# 3) tr que tenha aria-label contendo o nome
|
|
216
|
+
f"//tr[contains(@aria-label, '{nome_diretorio}')]",
|
|
217
|
+
|
|
218
|
+
# 4) Qualquer elemento com texto visível igual ao nome
|
|
219
|
+
f"//*[normalize-space(text())='{nome_diretorio}']",
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
elemento_encontrado = False
|
|
223
|
+
ultimo_erro = None
|
|
224
|
+
|
|
225
|
+
for xpath in xpaths_possiveis:
|
|
226
|
+
try:
|
|
227
|
+
print(f"Tentando localizar diretório com XPath: {xpath}")
|
|
228
|
+
aguardar_elemento_ser_clicavel_xpath(self.driver, xpath, 20)
|
|
229
|
+
clicar_elemento_por_xpath(self.driver, xpath)
|
|
230
|
+
print(f"Diretório '{nome_diretorio}' clicado com sucesso usando XPath: {xpath}.")
|
|
231
|
+
elemento_encontrado = True
|
|
232
|
+
break
|
|
233
|
+
except Exception as e:
|
|
234
|
+
print(f"Não encontrado/clicável com esse XPath. Erro: {e}")
|
|
235
|
+
ultimo_erro = e
|
|
236
|
+
|
|
237
|
+
if not elemento_encontrado:
|
|
238
|
+
raise RuntimeError(
|
|
239
|
+
f"Não foi possível clicar no diretório '{nome_diretorio}' "
|
|
240
|
+
f"com nenhum dos seletores. Último erro: {ultimo_erro}"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
async def clicar_ultima_pasta(self, timeout: int = 20):
|
|
244
|
+
|
|
245
|
+
wait = WebDriverWait(self.driver, timeout)
|
|
246
|
+
|
|
247
|
+
# 1) Esperar o tbody da tabela ficar presente
|
|
248
|
+
try:
|
|
249
|
+
tbody = wait.until(
|
|
250
|
+
EC.presence_of_element_located(
|
|
251
|
+
# tabela de listagem de arquivos/pastas
|
|
252
|
+
(By.CSS_SELECTOR, "table.cursor-pointer tbody, table.w-full.cursor-pointer.table-auto tbody")
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
except TimeoutException:
|
|
256
|
+
raise RuntimeError("Tabela de transfers não encontrada na página.")
|
|
257
|
+
|
|
258
|
+
# 2) Pegar todas as linhas clicáveis
|
|
259
|
+
# No HTML do TIBCO cada linha tem tabindex="0" e aria-label com o nome
|
|
260
|
+
rows = tbody.find_elements(By.CSS_SELECTOR, "tr[tabindex='0'][aria-label]")
|
|
261
|
+
|
|
262
|
+
# Filtra apenas as visíveis (caso tenha linha escondida)
|
|
263
|
+
rows = [r for r in rows if r.is_displayed()]
|
|
264
|
+
|
|
265
|
+
if not rows:
|
|
266
|
+
raise RuntimeError("Nenhuma linha clicável encontrada na tabela.")
|
|
267
|
+
|
|
268
|
+
# 3) Última linha da lista (mais recente)
|
|
269
|
+
last_row = rows[-1]
|
|
270
|
+
print("Última linha encontrada com aria-label:",
|
|
271
|
+
last_row.get_attribute("aria-label"))
|
|
272
|
+
|
|
273
|
+
# 4) Dentro da linha, tentamos clicar no elemento mais específico
|
|
274
|
+
target = None
|
|
275
|
+
try:
|
|
276
|
+
# span com classe 'file' (texto do nome)
|
|
277
|
+
target = last_row.find_element(By.CSS_SELECTOR, "span.file")
|
|
278
|
+
except Exception:
|
|
279
|
+
# fallback: qualquer span da primeira coluna de nome
|
|
280
|
+
try:
|
|
281
|
+
target = last_row.find_element(By.CSS_SELECTOR, "td span")
|
|
282
|
+
except Exception:
|
|
283
|
+
# fallback final: a própria <tr>
|
|
284
|
+
target = last_row
|
|
285
|
+
|
|
286
|
+
# 5) Garantir que o elemento está visível na tela
|
|
287
|
+
self.driver.execute_script(
|
|
288
|
+
"arguments[0].scrollIntoView({block: 'center'});", target
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# 6) Esperar ficar clicável e clicar
|
|
292
|
+
try:
|
|
293
|
+
wait.until(EC.element_to_be_clickable(target))
|
|
294
|
+
except TimeoutException:
|
|
295
|
+
# se não ficar "clicável" oficialmente, ainda tentamos via JS
|
|
296
|
+
print("⚠️ Elemento não ficou clicável pelo EC, tentando mesmo assim...")
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
target.click()
|
|
300
|
+
except Exception:
|
|
301
|
+
# fallback robusto: clique via JavaScript
|
|
302
|
+
self.driver.execute_script("arguments[0].click();", target)
|
|
303
|
+
|
|
304
|
+
print("Última pasta/arquivo clicado com sucesso!")
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
async def login_e_baixar(self):
|
|
309
|
+
# limpa downloads antes de começar
|
|
310
|
+
await self.limpar_downloads()
|
|
311
|
+
|
|
312
|
+
# pega credenciais via API (async)
|
|
313
|
+
username, password = await self.load_config()
|
|
314
|
+
|
|
315
|
+
console.print("Realizando login na NielsenIQ...", style="cyan")
|
|
316
|
+
|
|
317
|
+
# Inserir usuário
|
|
318
|
+
xpath = "//input[@id='userid']"
|
|
319
|
+
aguardar_elemento_ser_clicavel_xpath(self.driver, xpath, 30)
|
|
320
|
+
inserir_texto_por_letra_xpath(self.driver, xpath, username)
|
|
321
|
+
|
|
322
|
+
# Inserir senha
|
|
323
|
+
xpath = "//input[@id='password']"
|
|
324
|
+
inserir_texto_por_letra_xpath(self.driver, xpath, password)
|
|
325
|
+
|
|
326
|
+
# Clicar em OK
|
|
327
|
+
xpath = "//input[@id='button']"
|
|
328
|
+
clicar_elemento_por_xpath(self.driver, xpath)
|
|
329
|
+
|
|
330
|
+
time.sleep(2)
|
|
331
|
+
|
|
332
|
+
# Clicar no diretório principal LA_BR_REDE_SIM_DO_SUL
|
|
333
|
+
await self.clicar_diretorio_principal("LA_BR_REDE_SIM_DO_SUL")
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
await asyncio.sleep(5)
|
|
337
|
+
|
|
338
|
+
# ====== CLICAR NA PASTA COM O ÚLTIMO MÊS ======
|
|
339
|
+
await self.clicar_ultima_pasta()
|
|
340
|
+
|
|
341
|
+
await asyncio.sleep(5)
|
|
342
|
+
|
|
343
|
+
# ====== BAIXAR OS ARQUIVOS DENTRO DA PASTA ======
|
|
344
|
+
|
|
345
|
+
# Localiza as linhas de arquivo na tabela atual (TIBCO MFT)
|
|
346
|
+
xpath_linhas_arquivos = (
|
|
347
|
+
"//table[contains(@class,'cursor-pointer') and "
|
|
348
|
+
"contains(@class,'table-auto')]/tbody/tr[@tabindex='0']"
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
# Aguarda até 20 segundos pelo menos 1 linha da tabela aparecer
|
|
352
|
+
WebDriverWait(self.driver, 20).until(
|
|
353
|
+
EC.presence_of_element_located((By.XPATH, xpath_linhas_arquivos))
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
linhas_arquivos = busca_lista_elementos_por_xpath(
|
|
357
|
+
self.driver, xpath_linhas_arquivos
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
qtd_arquivos = len(linhas_arquivos)
|
|
361
|
+
console.print(
|
|
362
|
+
f"Total de linhas (arquivos visíveis): {qtd_arquivos}", style="cyan"
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Arquivos já existentes na pasta de download (baseline)
|
|
366
|
+
conhecidos = set(os.listdir(self.caminho_downloads))
|
|
367
|
+
|
|
368
|
+
for r in range(1, qtd_arquivos + 1):
|
|
369
|
+
# Linha r
|
|
370
|
+
xpath_linha = f"{xpath_linhas_arquivos}[{r}]"
|
|
371
|
+
|
|
372
|
+
# Coluna Name: span com classe 'file' (link do arquivo)
|
|
373
|
+
xpath_baixar = rf"{xpath_linha}//span[contains(@class, 'file')]"
|
|
374
|
+
clicar_elemento_por_xpath(self.driver, xpath_baixar)
|
|
375
|
+
|
|
376
|
+
await asyncio.sleep(1)
|
|
377
|
+
|
|
378
|
+
# Esperar o download completar (sem .crdownload/.tmp)
|
|
379
|
+
timeout_segundos = 180
|
|
380
|
+
inicio = time.time()
|
|
381
|
+
novos_arquivos = set()
|
|
382
|
+
|
|
383
|
+
while time.time() - inicio < timeout_segundos:
|
|
384
|
+
arquivos_atual = [
|
|
385
|
+
f
|
|
386
|
+
for f in os.listdir(self.caminho_downloads)
|
|
387
|
+
if not f.endswith(".crdownload") and not f.endswith(".tmp")
|
|
388
|
+
]
|
|
389
|
+
novos = set(arquivos_atual) - conhecidos
|
|
390
|
+
if novos:
|
|
391
|
+
novos_arquivos = novos
|
|
392
|
+
conhecidos.update(novos)
|
|
393
|
+
break
|
|
394
|
+
await asyncio.sleep(1)
|
|
395
|
+
|
|
396
|
+
if not novos_arquivos:
|
|
397
|
+
console.print(
|
|
398
|
+
"Nenhum arquivo novo foi baixado dentro do tempo limite.",
|
|
399
|
+
style="bold red",
|
|
400
|
+
)
|
|
401
|
+
continue
|
|
402
|
+
|
|
403
|
+
for arquivo_baixado in sorted(novos_arquivos):
|
|
404
|
+
caminho_arquivo = os.path.join(self.caminho_downloads, arquivo_baixado)
|
|
405
|
+
console.print(f"Arquivo baixado: {caminho_arquivo}", style="bold green")
|
|
406
|
+
|
|
407
|
+
try:
|
|
408
|
+
with open(caminho_arquivo, "rb") as file:
|
|
409
|
+
file_bytes = file.read()
|
|
410
|
+
|
|
411
|
+
nome_arquivo = arquivo_baixado
|
|
412
|
+
ext = "doc" # tipo lógico esperado pelo datalake
|
|
413
|
+
|
|
414
|
+
await send_file_to_datalake(
|
|
415
|
+
directory=DATALAKE_DIRECTORY,
|
|
416
|
+
file=file_bytes,
|
|
417
|
+
filename=nome_arquivo,
|
|
418
|
+
file_extension=ext,
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
os.remove(caminho_arquivo)
|
|
422
|
+
console.print(
|
|
423
|
+
f"Arquivo {nome_arquivo} enviado ao datalake e removido da pasta de download.",
|
|
424
|
+
style="bold green",
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
except Exception as e:
|
|
428
|
+
result = (
|
|
429
|
+
f"Arquivo baixado com sucesso, porém erro ao enviar para o datalake: {e} "
|
|
430
|
+
f"- Arquivo mantido em {caminho_arquivo}"
|
|
431
|
+
)
|
|
432
|
+
console.print(result, style="bold red")
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
async def extracao_dados_nielsen(task: RpaProcessoEntradaDTO) -> RpaRetornoProcessoDTO:
|
|
436
|
+
"""
|
|
437
|
+
Função principal para ser chamada pelo worker.
|
|
438
|
+
"""
|
|
439
|
+
console.print("Iniciando processo de extração Nielsen...", style="bold cyan")
|
|
440
|
+
driver = None
|
|
441
|
+
await kill_all_emsys()
|
|
442
|
+
try:
|
|
443
|
+
service = Service(ChromeDriverManager().install())
|
|
444
|
+
|
|
445
|
+
# === CONFIG PARA REMOVER POPUPS ===
|
|
446
|
+
chrome_options = Options()
|
|
447
|
+
chrome_prefs = {
|
|
448
|
+
"profile.default_content_setting_values.notifications": 2, # bloqueia notificações
|
|
449
|
+
"profile.default_content_setting_values.automatic_downloads": 1, # permite múltiplos downloads
|
|
450
|
+
"download.prompt_for_download": False, # não perguntar onde salvar
|
|
451
|
+
"download.directory_upgrade": True,
|
|
452
|
+
}
|
|
453
|
+
chrome_options.add_experimental_option("prefs", chrome_prefs)
|
|
454
|
+
chrome_options.add_argument("--disable-popup-blocking") # evita popups gerais
|
|
455
|
+
chrome_options.add_argument("--no-first-run")
|
|
456
|
+
chrome_options.add_argument("--no-default-browser-check")
|
|
457
|
+
chrome_options.add_argument("--disable-notifications")
|
|
458
|
+
chrome_options.add_argument("--disable-gpu")
|
|
459
|
+
chrome_options.add_argument("--disable-software-rasterizer")
|
|
460
|
+
chrome_options.add_argument("--disable-features=VizDisplayCompositor")
|
|
461
|
+
chrome_options.add_argument("--disable-dev-shm-usage")
|
|
462
|
+
|
|
463
|
+
driver = webdriver.Chrome(service=service, options=chrome_options)
|
|
464
|
+
driver.maximize_window()
|
|
465
|
+
console.print("Driver Chrome inicializado e maximizado.", style="green")
|
|
466
|
+
|
|
467
|
+
extracao = ExtracaoDados(driver)
|
|
468
|
+
await extracao.abrir_site()
|
|
469
|
+
await extracao.login_e_baixar()
|
|
470
|
+
|
|
471
|
+
# Fecha o driver com segurança
|
|
472
|
+
if driver:
|
|
473
|
+
try:
|
|
474
|
+
driver.quit()
|
|
475
|
+
driver = None
|
|
476
|
+
console.print("Driver fechado com sucesso.", style="green")
|
|
477
|
+
except Exception as e:
|
|
478
|
+
console.print(f"Erro ao fechar o driver: {e}", style="yellow")
|
|
479
|
+
|
|
480
|
+
console.print(
|
|
481
|
+
"Processo de extração Nielsen finalizado com sucesso.",
|
|
482
|
+
style="bold green",
|
|
483
|
+
)
|
|
484
|
+
return RpaRetornoProcessoDTO(
|
|
485
|
+
sucesso=True,
|
|
486
|
+
retorno="Processo de extração Nielsen finalizado com sucesso.",
|
|
487
|
+
status=RpaHistoricoStatusEnum.Sucesso,
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
except Exception as ex:
|
|
491
|
+
console.print(f"Erro na automação Nielsen: {ex}", style="bold red")
|
|
492
|
+
if driver:
|
|
493
|
+
try:
|
|
494
|
+
driver.quit()
|
|
495
|
+
except Exception:
|
|
496
|
+
pass
|
|
497
|
+
|
|
498
|
+
return RpaRetornoProcessoDTO(
|
|
499
|
+
sucesso=False,
|
|
500
|
+
retorno=f"Erro na automação Nielsen: {ex}",
|
|
501
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
502
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
503
|
+
)
|
|
504
|
+
|