csc-cia-stne 0.0.64__py3-none-any.whl → 0.0.66__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
@@ -2,8 +2,55 @@ from requests.auth import HTTPBasicAuth
2
2
  import requests
3
3
  import xml.etree.ElementTree as ET
4
4
  import hashlib
5
- from pydantic import BaseModel, ValidationError, field_validator
6
- from typing import Literal, Dict, Union, Optional
5
+ from pydantic import BaseModel, ValidationError, field_validator, Field, HttpUrl
6
+ from typing import Literal, Dict, Union, Optional, List
7
+ import json
8
+ import logging
9
+
10
+ log = logging.getLogger('__main__')
11
+
12
+ def xml_to_dict(element):
13
+ """Converte um elemento XML recursivamente para um dicionário."""
14
+ if not list(element): # Se não tem filhos, retorna apenas o texto
15
+ return element.text.strip() if element.text else ""
16
+
17
+ result = {}
18
+ for child in element:
19
+ child_data = xml_to_dict(child)
20
+ if child.tag in result:
21
+ if isinstance(result[child.tag], list):
22
+ result[child.tag].append(child_data)
23
+ else:
24
+ result[child.tag] = [result[child.tag], child_data]
25
+ else:
26
+ result[child.tag] = child_data
27
+
28
+ if element.attrib:
29
+ result["@atributos"] = element.attrib # Adiciona atributos XML se existirem
30
+
31
+ return result
32
+
33
+ def xml_response_to_json(response_text):
34
+ """Converte a resposta XML para um dicionário JSON válido."""
35
+
36
+ root = ET.fromstring(response_text)
37
+ lista = xml_to_dict(root)
38
+ if not isinstance(lista, dict):
39
+ return []
40
+ return list(lista.values())[0] # Agora retorna um dicionário em vez de uma string JSON
41
+
42
+ def print_element(element, indent=0):
43
+ """Função recursiva para exibir campos e subcampos"""
44
+ prefix = " " * (indent * 2) # Indentação para visualização hierárquica
45
+ print(f"{prefix}- {element.tag}: {element.text.strip() if element.text else ''}")
46
+
47
+ # Se o elemento tiver atributos, exibir
48
+ if element.attrib:
49
+ print(f"{prefix} Atributos: {element.attrib}")
50
+
51
+ # Percorrer subelementos
52
+ for child in element:
53
+ print_element(child, indent + 1)
7
54
 
8
55
  # Validações dos inputs
9
56
  class InitParamsValidator(BaseModel):
@@ -118,7 +165,44 @@ class EnviarArquivoValidator(BaseModel):
118
165
  raise ValueError("Cada destinatário deve conter as chaves 'unidade', 'dependencia' e 'operador'. Verifique a documentação da API BC STA para entender o que colocar cada campo")
119
166
 
120
167
  return value
121
-
168
+
169
+ class ListarArquivosParams(BaseModel):
170
+ """
171
+ Parâmetros para listar arquivos disponíveis na API STA.
172
+
173
+ Atributos:
174
+ nivel (str): Nível de detalhe da consulta. Aceita apenas 'RES', 'BAS' ou 'COMPL'.
175
+ inicio (str): Data e hora de início no formato ISO 8601 (yyyy-MM-ddTHH:mm:ss).
176
+ fim (str): Data e hora de fim no formato ISO 8601 (yyyy-MM-ddTHH:mm:ss).
177
+ situacao (Optional[str]): Situação da transmissão, podendo ser 'REC' ou 'A_REC'.
178
+ identificadorDocumento (Optional[str]): Identificador do documento, se aplicável.
179
+ qtd (int): Quantidade máxima de resultados (valor padrão: 100, máximo permitido: 100).
180
+ tipo_arquivo (list): lista de tipos de arquivo para filtrar ['ACCS002','ACCS003','AJUD301','AJUD302','AJUD303','AJUD304','AJUD305','AJUD308','AJUD309','AJUD310','AJUD331','AMES102','AMTF102','ASVR9810','ATXB001']
181
+ """
182
+ nivel: Literal['RES', 'BAS', 'COMPL']
183
+ #inicio: str = Field(..., regex=r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$")
184
+ inicio: str = Field(..., pattern=r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$")
185
+ fim: Optional[str] = Field(None, pattern=r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$")
186
+ situacao: Optional[Literal['REC', 'A_REC']] = None
187
+ identificadorDocumento: Optional[str] = None
188
+ qtd: int = Field(default=100, le=100)
189
+ #tipo_arquivo: Optional[str] = None
190
+ tipo_arquivo: Optional[List[
191
+ Literal[
192
+ 'ACCS002', 'ACCS003', 'AJUD301', 'AJUD302', 'AJUD303', 'AJUD304', 'AJUD305', 'AJUD308',
193
+ 'AJUD309', 'AJUD310', 'AJUD331', 'AMES102', 'AMTF102', 'ASVR9810', 'ATXB001'
194
+ ]
195
+ ]] = None
196
+
197
+ class DownloadArquivoParams(BaseModel):
198
+ protocolo: str = Field(..., min_length=1, description="Código do protocolo")
199
+ filename: Optional[str] = Field(None, description="Nome e caminho do arquivo")
200
+
201
+ class DownloadArquivoResponse(BaseModel):
202
+ success: bool = Field(..., description="Indica se o download foi bem-sucedido")
203
+ status_code: int = Field(..., ge=100, le=599, description="Código de status HTTP")
204
+ content: Union[bytes, str] = Field(..., description="Conteúdo do arquivo (em bytes se sucesso, em string se erro)")
205
+
122
206
  class BC_STA:
123
207
 
124
208
  def __init__(self, usuario:str, senha:str, ambiente:str):
@@ -414,3 +498,128 @@ class BC_STA:
414
498
  #return f"Failed to create protocol. Status code: {response.status_code}, Reason: {response.reason}"
415
499
  return resposta
416
500
 
501
+ 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):
502
+ """Lista os arquivos do STA com os parâmetros informados.
503
+
504
+ Args:
505
+ nivel (Literal['RES', 'BAS', 'COMPL']): nivel de detalhamento da consulta
506
+ inicio (str): data de inicio, no padrao 'AAAA-MM-DDTHH:MM:SS'
507
+ fim (str, optional): data de fim da consulta, no padrao 'AAAA-MM-DDTHH:MM:SS'
508
+ situacao (Optional[Literal['REC', 'A_REC']], optional): Arquivos já recebidos ou ainda não recebidos. Default=None (Todos).
509
+ identificadorDocumento (Optional[str], optional): Identificador documento. Default=None (Todos).
510
+ qtd (int, optional): Quantidade de arquivos na resposta. Default=100 (máximo=100).
511
+ 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'].
512
+
513
+ Returns:
514
+ list: Lista de arquivos do STA
515
+ """
516
+ resultados = []
517
+ ultima_data = inicio
518
+
519
+ while True:
520
+ params = ListarArquivosParams(nivel=nivel, inicio=ultima_data, fim=fim, situacao=situacao, identificadorDocumento=identificadorDocumento, qtd=qtd, tipo_arquivo=tipo_arquivo)
521
+
522
+ url = f"{self.base_url}/arquivos?tipoConsulta=AVANC&nivelDetalhe={params.nivel}"
523
+ url += f"&dataHoraInicio={params.inicio}&situacaoTransmissao={params.situacao}" if params.situacao else ""
524
+ url += f"&identificadorDocumento={params.identificadorDocumento}" if params.identificadorDocumento else ""
525
+ url += f"&dataHoraFim={params.fim}" if params.fim else ""
526
+ url += f"&qtdMaxResultados={params.qtd}"
527
+
528
+ response = requests.get(
529
+ url,
530
+ headers=self.headers,
531
+ auth=self.auth,
532
+ timeout=60,
533
+ )
534
+
535
+ if response.status_code == 200:
536
+
537
+ try:
538
+
539
+ dados = xml_response_to_json(response.text)
540
+
541
+ if not dados: # Verifica se a lista está vazia
542
+
543
+ break # Sai do loop se não houver mais dados
544
+
545
+ # Filtra apenas os arquivos do tipo 'AJUD308'
546
+ if tipo_arquivo is not None:
547
+
548
+ dados_filtrados = [arquivo for arquivo in dados if arquivo.get("TipoArquivo") in tipo_arquivo]
549
+ resultados.extend(dados_filtrados)
550
+
551
+ else:
552
+
553
+ resultados.extend(dados)
554
+
555
+ # Verifica se o campo 'DataHoraDisponibilizacao' existe no último registro
556
+ if dados and isinstance(dados, list) and 'DataHoraDisponibilizacao' in dados[-1]:
557
+
558
+ ultima_data = dados[-1]['DataHoraDisponibilizacao'] # Atualiza a data para a próxima requisição
559
+
560
+ else:
561
+
562
+ log.error("Campo 'DataHoraDisponibilizacao' não encontrado ou estrutura inesperada.")
563
+ return False
564
+
565
+ except ET.ParseError as e:
566
+
567
+ log.error(f"Erro ao processar XML: {e}")
568
+ return False
569
+
570
+ else:
571
+
572
+ log.error(f"Erro ao listar arquivos: {response.status_code}")
573
+ return False
574
+
575
+ return resultados
576
+
577
+ def download_arquivo(self,protocolo:str,filename:str=None):
578
+ """Faz o download de um arquivo de um protocolo especifico
579
+
580
+ Args:
581
+ protocolo (str): protocolo
582
+ filename (str): path+nome do arquivo
583
+
584
+ Returns:
585
+ dict: {"success": bool, "status_code": int, "content": bytes/str}
586
+ """
587
+
588
+ # Validação dos parâmetros
589
+ try:
590
+
591
+ params = DownloadArquivoParams(protocolo=protocolo, filename=filename)
592
+
593
+ except ValidationError as e:
594
+
595
+ return Exception(str(e))
596
+
597
+ url = f"/arquivos/{protocolo}/conteudo"
598
+ response = requests.get(
599
+ self.base_url + url,
600
+ auth=self.auth,
601
+ timeout=60,
602
+ headers={"Connection": "keep-alive"},
603
+ )
604
+
605
+ if response.status_code == 200:
606
+
607
+ if filename is not None:
608
+
609
+ try:
610
+
611
+ with open(filename, "wb") as arquivo:
612
+
613
+ arquivo.write(response.content)
614
+
615
+ except Exception as e:
616
+
617
+ raise Exception(f"Falha ao salvar o arquivo em disco\n{str(e)}")
618
+
619
+ return {"success": True, "status_code": int(response.status_code), "content": response.content }
620
+
621
+ else:
622
+
623
+ return {"success": False, "status_code": int(response.status_code), "content": response.text}
624
+
625
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: csc_cia_stne
3
- Version: 0.0.64
3
+ Version: 0.0.66
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,6 +1,6 @@
1
1
  csc_cia_stne/__init__.py,sha256=Io-gKis1evws5cHUqyOrcsZKNCQRviYj3zbp__5lgKU,2512
2
2
  csc_cia_stne/bc_correios.py,sha256=pQAnRrcXEMrx3N1MWydZVIhEQLerh3x8-0B045zZIzk,24174
3
- csc_cia_stne/bc_sta.py,sha256=f75HJ7FLIDSJFLDTvvSvCYo9z0HchzP7rDY5WIdiKXY,16830
3
+ csc_cia_stne/bc_sta.py,sha256=KWHTytM3msAFayt8y5_97RwWHE8Dv8ssfwyh6zU7xI0,25836
4
4
  csc_cia_stne/email.py,sha256=RK_TzWBVnUfpP-s5NvjTJJjzhICy8e2fME9EuaiySMY,8162
5
5
  csc_cia_stne/gcp_bigquery.py,sha256=jYxvqrWDOPkxc05U4aef7V5lL8ptqsE93lfn0dLFyvc,7385
6
6
  csc_cia_stne/google_drive.py,sha256=lgcOd27vk2Mb_wP_fAWIbec-S3MIBKyh4TpRth6REXc,12788
@@ -26,8 +26,8 @@ csc_cia_stne/utilitarios/validations/GoogleDriveValidator.py,sha256=PBo-AV2bjR__
26
26
  csc_cia_stne/utilitarios/validations/ServiceNowValidator.py,sha256=yleKUIo1ZfyloP9fDPNjv3JJXdLcocT81WIgRSYmqEA,14423
27
27
  csc_cia_stne/utilitarios/validations/__init__.py,sha256=O_qyEU2ji3u6LHUXZCXvUFsMpoMWL625qqHTXyXivTA,106
28
28
  csc_cia_stne/utilitarios/validations/web_validator.py,sha256=HYKYSpDv1RvRjZIuwTPt-AbEz-9392MxM_O329iYuSA,5722
29
- csc_cia_stne-0.0.64.dist-info/LICENCE,sha256=LPGMtgKki2C3KEZP7hDhA1HBrlq5JCHkIeStUCLEMx4,1073
30
- csc_cia_stne-0.0.64.dist-info/METADATA,sha256=KYbJwXirJeL6A39Cz8ohmuBV8BD7y_M2E4bCVs3yBBY,1312
31
- csc_cia_stne-0.0.64.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
32
- csc_cia_stne-0.0.64.dist-info/top_level.txt,sha256=ldo7GVv3tQx5KJvwBzdZzzQmjPys2NDVVn1rv0BOF2Q,13
33
- csc_cia_stne-0.0.64.dist-info/RECORD,,
29
+ csc_cia_stne-0.0.66.dist-info/LICENCE,sha256=LPGMtgKki2C3KEZP7hDhA1HBrlq5JCHkIeStUCLEMx4,1073
30
+ csc_cia_stne-0.0.66.dist-info/METADATA,sha256=xTDZdZe5LPIZRHdvcJ3Vhk0C6KLrIXMopa7JXmSdXaY,1312
31
+ csc_cia_stne-0.0.66.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
32
+ csc_cia_stne-0.0.66.dist-info/top_level.txt,sha256=ldo7GVv3tQx5KJvwBzdZzzQmjPys2NDVVn1rv0BOF2Q,13
33
+ csc_cia_stne-0.0.66.dist-info/RECORD,,