diff --git a/actions/compress_decompress/compress_decompress.py b/actions/compress_decompress/compress_decompress.py index fe10383..a6f2072 100644 --- a/actions/compress_decompress/compress_decompress.py +++ b/actions/compress_decompress/compress_decompress.py @@ -1,91 +1,99 @@ -# Importa a biblioteca nativa 'zlib' usada para descompressão de dados binários. import base64 import zlib -# Define uma classe utilitária chamada 'String', contendo apenas métodos estáticos. -# Essa abordagem permite o uso direto sem necessidade de instanciar a classe. -class String: - @staticmethod - def decompress(vf_string): - """ - Descomprime e decodifica texto de origem WPTools/Firebird. +class DataCompressor: + """ + Classe utilitária para compressão e descompressão de dados. - Finalidade: - Converter o conteúdo de campos BLOB ou strings compactadas (como no Delphi) - em texto legível, detectando automaticamente se o conteúdo está: - - Compactado com zlib - - Codificado em ISO-8859-1 (padrão ANSI) - - Representado como bytes puros + Funções: + - compress(): compacta texto/bytes com zlib e opcional Base64 + - decompress(): decodifica Base64, descompacta e retorna texto legível + """ + + @staticmethod + def decompress(data): """ - # Verifica se o valor recebido é nulo, vazio ou None. - # Se for, retorna string vazia para evitar erros de processamento. - if not vf_string: + Descomprime dados vindos de Firebird/Delphi/strings compactadas. + + Processos executados automaticamente: + 1. Se vier Base64 → faz base64.b64decode() + 2. Se vier stream/BLOB → lê com .read() + 3. Se for string → converte para bytes + 4. Detecta se é zlib → descompacta + 5. Retorna texto ISO-8859-1 + + Retorna: + Texto descompactado (str) + """ + + # Caso seja None ou vazio + if not data: return "" - # Caso seja um objeto tipo stream (ex: campo BLOB do Firebird) - # Campos BLOB geralmente possuem o método `.read()` para leitura de bytes. - if hasattr(vf_string, "read"): - vf_string = vf_string.read() # Lê o conteúdo completo do stream + # 1. Se vier como stream (ex.: campo BLOB do Firebird) + if hasattr(data, "read"): + data = data.read() - # Garante que o valor trabalhado é uma sequência de bytes (não string) - # Se o dado já for texto (str), converte para bytes em codificação Latin-1, - # que é compatível com ISO-8859-1 usado por sistemas Delphi/Firebird. - if isinstance(vf_string, str): - vf_bytes = vf_string.encode("latin1", errors="ignore") - else: - vf_bytes = vf_string # Já está em bytes, então apenas reaproveita - - # Detecta se o conteúdo foi compactado com zlib. - # A assinatura padrão do formato zlib começa com bytes: 0x78 0x9C ou 0x78 0xDA. - is_zlib = ( - len(vf_bytes) > 2 and vf_bytes[0] == 0x78 and vf_bytes[1] in (0x9C, 0xDA) - ) - - # Se a detecção confirmar que o conteúdo é zlib, tenta descompactar. - if is_zlib: + # 2. Se for string, pode ser Base64 → tentamos primeiro decodificar base64 + if isinstance(data, str): try: - # Descompacta os bytes e decodifica o texto usando ISO-8859-1 (ANSI), - # que preserva corretamente acentuação e caracteres especiais. - text = zlib.decompress(vf_bytes).decode("iso-8859-1", errors="ignore") - return text + # Tentamos decodificar Base64 + data_bytes = base64.b64decode(data, validate=False) + except Exception: + # Não era Base64, convertemos string para bytes ISO-8859-1 + data_bytes = data.encode("latin1", errors="ignore") + else: + # Já são bytes + data_bytes = data + + # Tentativa extra: bytes podem ter vindo em Base64 também + try: + data_bytes = base64.b64decode(data_bytes, validate=False) except Exception: - # Caso falhe (por dados corrompidos ou não comprimidos de fato), - # o fluxo continua normalmente sem interromper a execução. pass - # Se não for zlib, tenta tratar o conteúdo como texto puro (não compactado) - try: - # Decodifica os bytes diretamente de ISO-8859-1 (padrão usado pelo Delphi) - return vf_bytes.decode("iso-8859-1", errors="ignore") - except Exception: - # Como fallback, converte para string bruta para evitar falhas. - return str(vf_string) + # 3. Detectar assinatura zlib (0x78 0x9C ou 0x78 0xDA) + is_zlib = ( + len(data_bytes) > 2 + and data_bytes[0] == 0x78 + and data_bytes[1] in (0x9C, 0xDA) + ) + + if is_zlib: + try: + text = zlib.decompress(data_bytes).decode("iso-8859-1", errors="ignore") + return text + except Exception: + pass + + # 4. Se não era zlib, tentamos decodificar direto + try: + return data_bytes.decode("iso-8859-1", errors="ignore") + except Exception: + return str(data) + + # ===================================================================== - # >>> NOVO MÉTODO <<< @staticmethod - def compress(text, *, encoding: str = "iso-8859-1", as_base64: bool = True): + def compress(text, *, encoding="iso-8859-1", as_base64=True): """ - Comprime texto/dados com zlib. + Compacta texto/bytes usando zlib. Parâmetros: - text: str | bytes | stream (com .read()) - encoding: encoding usado quando 'text' for str (padrão: ISO-8859-1) - as_base64: se True, retorna string Base64 do conteúdo comprimido; - caso False, retorna bytes comprimidos. + text -> str | bytes | stream + encoding -> usado ao converter str para bytes + as_base64 -> retorna Base64 (True) ou bytes puros (False) Retorno: - - bytes (zlib) quando as_base64=False - - str (Base64) quando as_base64=True - - Observações: - - Use o mesmo 'encoding' ao descomprimir para simetria. - - Ideal para armazenar em BLOB ou trafegar seguro (Base64). + bytes (quando as_base64=False) + str Base64 (quando as_base64=True) """ + if text is None or text == "": return "" if as_base64 else b"" - # Se for stream (ex.: BLOB do Firebird) + # Se for stream/BLOB if hasattr(text, "read"): raw = text.read() else: @@ -97,10 +105,10 @@ class String: else: raw_bytes = bytes(raw) - # Comprime com zlib + # Compressão zlib comp = zlib.compress(raw_bytes) - # Opcional: codifica em Base64 para transporte/JSON + # Retorna Base64 se solicitado if as_base64: return base64.b64encode(comp).decode("ascii") diff --git a/packages/v1/administrativo/repositories/ato_principal/ato_principal_show_atos_repository.py b/packages/v1/administrativo/repositories/ato_principal/ato_principal_show_atos_repository.py index 22d1e37..457a9e2 100644 --- a/packages/v1/administrativo/repositories/ato_principal/ato_principal_show_atos_repository.py +++ b/packages/v1/administrativo/repositories/ato_principal/ato_principal_show_atos_repository.py @@ -2,7 +2,7 @@ from typing import Optional, Dict, Any, List from sqlalchemy import func from fastapi import HTTPException, status from database.mysql import SessionLocal, get_database_settings -from actions.compress_decompress.compress_decompress import decompress +from actions.compress_decompress.compress_decompress import DataCompressor from packages.v1.administrativo.models.ato_principal_model import AtoPrincipal from packages.v1.administrativo.models.ato_parte_model import AtoParte from packages.v1.administrativo.models.ato_documento_model import AtoDocumento @@ -232,8 +232,12 @@ class ShowAtosRepository: atos_vinculados_list.append( { - "inteiro_teor": ( - av.inteiro_teor.decode("utf-8") if av.inteiro_teor else None + "inteiro_teor": DataCompressor.decompress( + ( + av.inteiro_teor.decode("utf-8") + if av.inteiro_teor + else None + ) ), "identificacao_pedido_na_cgj": av.identificacao_pedido_cgj, "tipo_de_ato": av.tipo_ato, @@ -268,7 +272,7 @@ class ShowAtosRepository: # === 5. Montar JSON final === return { - "inteiro_teor": decompress( + "inteiro_teor": DataCompressor.decompress( (ato.inteiro_teor.decode("utf-8") if ato.inteiro_teor else None) ), "identificacao_pedido_na_cgj": ato.identificacao_pedido_cgj,