csc-cia-stne 0.1.18__py3-none-any.whl → 0.1.20__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.
csc_cia_stne/bc_sta.py CHANGED
@@ -6,7 +6,7 @@ import xml.etree.ElementTree as ET
6
6
  from xml.dom.minidom import parseString
7
7
  from pydantic import BaseModel, ValidationError, field_validator, Field, HttpUrl
8
8
  from typing import Literal, Dict, Union, Optional, List
9
- from datetime import datetime
9
+ from datetime import datetime, timedelta
10
10
 
11
11
  log = logging.getLogger('__main__')
12
12
 
@@ -499,90 +499,95 @@ class BC_STA:
499
499
  #return f"Failed to create protocol. Status code: {response.status_code}, Reason: {response.reason}"
500
500
  return resposta
501
501
 
502
- def listar_arquivos(self, nivel: Literal['RES', 'BAS', 'COMPL'], inicio: str, fim: str = None,situacao: Optional[Literal['REC', 'A_REC']] = None, identificadorDocumento: Optional[str] = None, qtd: int = 100, tipo_arquivo:list=None):
503
- """Lista os arquivos do STA com os parâmetros informados.
504
-
505
- Args:
506
- nivel (Literal['RES', 'BAS', 'COMPL']): nivel de detalhamento da consulta
507
- inicio (str): data de inicio, no padrao 'AAAA-MM-DDTHH:MM:SS'
508
- fim (str, optional): data de fim da consulta, no padrao 'AAAA-MM-DDTHH:MM:SS'
509
- situacao (Optional[Literal['REC', 'A_REC']], optional): Arquivos recebidos ou ainda não recebidos. Default=None (Todos).
510
- identificadorDocumento (Optional[str], optional): Identificador documento. Default=None (Todos).
511
- qtd (int, optional): Quantidade de arquivos na resposta. Default=100 (máximo=100).
512
- tipo_arquivo (list, optional): Tipo de arquivo para fazer download. Default=None (Todos). Opções: ['ACCS002', 'ACCS003', 'AJUD301', 'AJUD302', 'AJUD303', 'AJUD304', 'AJUD305', 'AJUD308', 'AJUD309', 'AJUD310', 'AJUD331', 'AMES102', 'AMTF102', 'ASVR9810', 'ATXB001'].
513
-
514
- Returns:
515
- list: Lista de arquivos do STA
516
- """
502
+ def listar_arquivos(self,
503
+ nivel: Literal['RES', 'BAS', 'COMPL'],
504
+ inicio: str,
505
+ fim: str = None,
506
+ situacao: Optional[Literal['REC', 'A_REC']] = None,
507
+ identificadorDocumento: Optional[str] = None,
508
+ qtd: int = 100,
509
+ tipo_arquivo: list = None):
517
510
  resultados = []
518
- ultima_data = inicio
511
+
512
+ # Parse seguro para datetime (assume formato ISO 'AAAA-MM-DDTHH:MM:SS')
513
+ dt_inicio = datetime.fromisoformat(inicio)
514
+ dt_fim = datetime.fromisoformat(fim) if fim else None
515
+ ultima_dt = dt_inicio
519
516
 
520
517
  while True:
521
- params = ListarArquivosParams(nivel=nivel, inicio=ultima_data, fim=fim, situacao=situacao, identificadorDocumento=identificadorDocumento, qtd=qtd, tipo_arquivo=tipo_arquivo)
522
-
523
- url = f"{self.base_url}/arquivos?tipoConsulta=AVANC&nivelDetalhe={params.nivel}"
524
- url += f"&dataHoraInicio={params.inicio}&situacaoTransmissao={params.situacao}" if params.situacao else ""
525
- url += f"&identificadorDocumento={params.identificadorDocumento}" if params.identificadorDocumento else ""
526
- url += f"&dataHoraFim={params.fim}" if params.fim else ""
527
- url += f"&qtdMaxResultados={params.qtd}"
518
+ # Se passamos do fim, encerra antes de chamar a API
519
+ if dt_fim and ultima_dt >= dt_fim:
520
+ break
521
+
522
+ # Monta a URL usando a data atual do cursor
523
+ _inicio_str = ultima_dt.strftime("%Y-%m-%dT%H:%M:%S")
524
+ url = f"{self.base_url}/arquivos?tipoConsulta=AVANC&nivelDetalhe={nivel}"
525
+ url += f"&dataHoraInicio={_inicio_str}"
526
+ if situacao:
527
+ url += f"&situacaoTransmissao={situacao}"
528
+ if identificadorDocumento:
529
+ url += f"&identificadorDocumento={identificadorDocumento}"
530
+ if dt_fim:
531
+ _fim_str = dt_fim.strftime("%Y-%m-%dT%H:%M:%S")
532
+ url += f"&dataHoraFim={_fim_str}"
533
+ url += f"&qtdMaxResultados={qtd}"
528
534
 
529
535
  response = requests.get(
530
- url,
531
- headers=self.headers,
532
- auth=self.auth,
533
- timeout=60,
536
+ url, headers=self.headers, auth=self.auth, timeout=60,
534
537
  )
535
538
 
536
- if response.status_code == 200:
537
-
538
- try:
539
-
540
- dados = xml_response_to_json(response.text)
541
-
542
- if not dados: # Verifica se a lista está vazia
543
-
544
- break # Sai do loop se não houver mais dados
545
-
546
- # Filtra apenas os arquivos do tipo 'AJUD308'
547
- if tipo_arquivo is not None:
548
-
549
- if isinstance(dados, list):
550
- dados_filtrados = [arquivo for arquivo in dados if arquivo.get("TipoArquivo") in tipo_arquivo]
551
- elif dados.get("TipoArquivo") in tipo_arquivo:
552
- dados_filtrados = [dados]
553
-
554
- resultados.extend(dados_filtrados)
555
-
556
- else:
557
-
558
- resultados.extend(dados)
539
+ if response.status_code != 200:
540
+ log.error(f"Erro ao listar arquivos: response code {response.status_code}\n{response.text}")
541
+ return False
559
542
 
560
- # Verifica se o campo 'DataHoraDisponibilizacao' existe no último registro
561
- if dados and isinstance(dados, list) and 'DataHoraDisponibilizacao' in dados[-1]:
543
+ try:
544
+ dados = xml_response_to_json(response.text)
545
+ if not dados:
546
+ break # sem mais resultados
547
+
548
+ # Normaliza dados para lista
549
+ itens = dados if isinstance(dados, list) else [dados]
550
+
551
+ # (opcional) filtra por tipo_arquivo
552
+ if tipo_arquivo is not None:
553
+ itens = [a for a in itens if a.get("TipoArquivo") in tipo_arquivo]
554
+
555
+ resultados.extend(itens)
556
+
557
+ # Atualiza o cursor a partir do último item retornado
558
+ # Busca o último com DataHoraDisponibilizacao
559
+ ultimo = None
560
+ for cand in reversed(itens):
561
+ if "DataHoraDisponibilizacao" in cand and cand["DataHoraDisponibilizacao"]:
562
+ ultimo = cand["DataHoraDisponibilizacao"]
563
+ break
564
+
565
+ if not ultimo:
566
+ # fallback: se o JSON bruto vier como dict/list diferente, tente no original
567
+ if isinstance(dados, list) and dados and "DataHoraDisponibilizacao" in dados[-1]:
568
+ ultimo = dados[-1]["DataHoraDisponibilizacao"]
569
+ elif isinstance(dados, dict) and "DataHoraDisponibilizacao" in dados:
570
+ ultimo = dados["DataHoraDisponibilizacao"]
571
+
572
+ if not ultimo:
573
+ log.error("Campo 'DataHoraDisponibilizacao' não encontrado ou estrutura inesperada.")
574
+ return False
562
575
 
563
- ultima_data = dados[-1]['DataHoraDisponibilizacao'] # Atualiza a data para a próxima requisição
576
+ # Cursor: último + 1s para evitar repetir o mesmo registro
577
+ proxima_dt = datetime.fromisoformat(ultimo) + timedelta(seconds=1)
564
578
 
565
- elif dados and isinstance(dados, dict) and 'DataHoraDisponibilizacao' in dados:
566
-
567
- ultima_data = dados['DataHoraDisponibilizacao'] # Atualiza a data para a próxima requisição
568
-
569
- else:
570
-
571
- log.error("Campo 'DataHoraDisponibilizacao' não encontrado ou estrutura inesperada.")
572
- return False
579
+ # Se a próxima dt já ultrapassa o fim, encerramos sem nova chamada
580
+ if dt_fim and proxima_dt > dt_fim:
581
+ break
573
582
 
574
- except ET.ParseError as e:
583
+ ultima_dt = proxima_dt
575
584
 
576
- log.error(f"Erro ao processar XML: {e}")
577
- return False
578
-
579
- else:
580
-
581
- log.error(f"Erro ao listar arquivos: response code {response.status_code}\n{response.text}")
585
+ except ET.ParseError as e:
586
+ log.error(f"Erro ao processar XML: {e}")
582
587
  return False
583
-
588
+
584
589
  return resultados
585
-
590
+
586
591
  def download_arquivo(self,protocolo:str,filename:str=None):
587
592
  """Faz o download de um arquivo de um protocolo especifico
588
593
 
@@ -631,7 +636,6 @@ class BC_STA:
631
636
 
632
637
  return {"success": False, "status_code": int(response.status_code), "content": response.text}
633
638
 
634
-
635
639
  def qs_gerar_xml_string_responder_nao_cliente(self,protocolo_inicial_ordem:str,numctrlccs:str,cnpj_cpf:str,numctrlenvio:str,numprocjud:str,identdemissor:str,identddestinatario:str,domsist:str,nuop:str,dtmovto:datetime,cnpjbaseentrespons:str):
636
640
 
637
641
  """Gera a string do arquivo XML de resposta para nao-cliente para ser enviado ao STA
csc_cia_stne/ftp.py CHANGED
@@ -8,7 +8,7 @@ from .utilitarios.validations.ftp import InitParamsValidator,UploadDownloadValid
8
8
 
9
9
  class FTP():
10
10
 
11
- def __init__(self, host:str , user:str , password:str , timeout:int=30, port:int=21, tls:bool=True, ssl:bool=True, verify_ssl:bool=False, verify_host:bool=False, tryouts:int=3, sftp:bool=False):
11
+ def __init__(self, host:str , user:str , password:str , timeout:int=30, port:int=21, tls:bool=True, ssl:bool=True, verify_ssl:bool=False, verify_host:bool=False, tryouts:int=3, sftp:bool=False, ssh_password:bool=False):
12
12
 
13
13
  self.host = host
14
14
  self.user = user
@@ -21,6 +21,7 @@ class FTP():
21
21
  self.verify_host = verify_host
22
22
  self.tryouts = tryouts
23
23
  self.sftp = sftp
24
+ self.ssh_password = ssh_password
24
25
 
25
26
  try:
26
27
 
@@ -95,6 +96,10 @@ class FTP():
95
96
 
96
97
  connection_ftp.setopt(connection_ftp.USERPWD, f"{self.user}:{self.password}".encode("utf-8"))
97
98
 
99
+ if self.ssh_password:
100
+
101
+ connection_ftp.setopt(connection_ftp.SSH_AUTH_TYPES, connection_ftp.SSH_AUTH_PASSWORD)
102
+
98
103
  connection_ftp.setopt(connection_ftp.CONNECTTIMEOUT, self.timeout) # Tempo limite para conexão
99
104
 
100
105
  connection_ftp.setopt(connection_ftp.TIMEOUT, self.timeout*2) # Tempo limite para operação
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csc_cia_stne
3
- Version: 0.1.18
3
+ Version: 0.1.20
4
4
  Summary: Biblioteca do time CSC-CIA utilizada no desenvolvimento de RPAs
5
5
  License: MIT
6
6
  Keywords: karavela,csc,cia,stone,rpa,botcity,stne
@@ -1,8 +1,8 @@
1
1
  csc_cia_stne/__init__.py,sha256=jwLhGpOwFCow_6cqzwLn31WcIrMzutMZtEQpLL4bQtM,2638
2
2
  csc_cia_stne/bc_correios.py,sha256=s2XjJ0iokMlcUv0mAy9saU3pc_G-6X8wltb_lFHIL6o,24717
3
- csc_cia_stne/bc_sta.py,sha256=sE-aU-ZVSAqryQrT1-nor9eAFM5npNAKF1QSm-ChhGU,28945
3
+ csc_cia_stne/bc_sta.py,sha256=urDC_apWYMiJSzgtfhpIDp42SEzdtYtTZrbDQe_485M,28611
4
4
  csc_cia_stne/email.py,sha256=y4xyPAe6_Mga5Wf6qAsDzYgn0f-zf2KshfItlWe58z8,8481
5
- csc_cia_stne/ftp.py,sha256=WSs28s7NO-mvEURIsK6VyqqIK_CVSv35ncscajyGu3o,11311
5
+ csc_cia_stne/ftp.py,sha256=eNkOUEXdw-9NYfuZjNo6Oh7EduD54g8N0cfD0LuOiTU,11516
6
6
  csc_cia_stne/gcp_bigquery.py,sha256=foq8azvvv_f7uikMDslX9RcUIrx7RAS-Sn0AGW0QFQc,7231
7
7
  csc_cia_stne/gcp_bucket.py,sha256=vMALWiW7IoBCuJAR8bUCpOV6BuBzI9AhRRk3b72OdMk,11515
8
8
  csc_cia_stne/google_drive.py,sha256=sfq2arBYrv8OLIfRJTnj067EPbem2gofwIfpbqJ475o,44500
@@ -38,8 +38,8 @@ csc_cia_stne/utilitarios/web_screen/__init__.py,sha256=5QcOPXKd95SvP2DoZiHS0gaU6
38
38
  csc_cia_stne/utilitarios/web_screen/web_screen_abstract.py,sha256=PjL8Vgfj_JdKidia7RFyCkro3avYLQu4RZRos41sh3w,3241
39
39
  csc_cia_stne/utilitarios/web_screen/web_screen_botcity.py,sha256=Xi5YJjl2pcxlX3OimqcBWRNXZEpAE7asyUjDJ4Oho5U,12297
40
40
  csc_cia_stne/utilitarios/web_screen/web_screen_selenium.py,sha256=JLIcPJE9ZX3Pd6zG6oTRMqqUAY063UzLY3ReRlxmiSM,15581
41
- csc_cia_stne-0.1.18.dist-info/licenses/LICENCE,sha256=LPGMtgKki2C3KEZP7hDhA1HBrlq5JCHkIeStUCLEMx4,1073
42
- csc_cia_stne-0.1.18.dist-info/METADATA,sha256=E61T5EEsvJQW1qLzbjCZl-EdUD3WVBdqsTfHEdBZsfM,1464
43
- csc_cia_stne-0.1.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- csc_cia_stne-0.1.18.dist-info/top_level.txt,sha256=ldo7GVv3tQx5KJvwBzdZzzQmjPys2NDVVn1rv0BOF2Q,13
45
- csc_cia_stne-0.1.18.dist-info/RECORD,,
41
+ csc_cia_stne-0.1.20.dist-info/licenses/LICENCE,sha256=LPGMtgKki2C3KEZP7hDhA1HBrlq5JCHkIeStUCLEMx4,1073
42
+ csc_cia_stne-0.1.20.dist-info/METADATA,sha256=klKwNP7-_o1IQIOzS6MTgEjDRkZ2InEKbC0epf_e7hM,1464
43
+ csc_cia_stne-0.1.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
+ csc_cia_stne-0.1.20.dist-info/top_level.txt,sha256=ldo7GVv3tQx5KJvwBzdZzzQmjPys2NDVVn1rv0BOF2Q,13
45
+ csc_cia_stne-0.1.20.dist-info/RECORD,,