From 45e26f7c12910f0278f82b5ed9ce4643b06decf3 Mon Sep 17 00:00:00 2001 From: Kenio de Souza Date: Sun, 16 Nov 2025 12:37:14 -0300 Subject: [PATCH] Debug --- .../services/log/log_show_database_service.py | 84 ++++++++++++------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/packages/v1/administrativo/services/log/log_show_database_service.py b/packages/v1/administrativo/services/log/log_show_database_service.py index 43e9ad5..ee84d26 100644 --- a/packages/v1/administrativo/services/log/log_show_database_service.py +++ b/packages/v1/administrativo/services/log/log_show_database_service.py @@ -16,26 +16,46 @@ def is_ignored(name: str) -> bool: # Garante que a comparação é feita em caixa alta para robustez return FILTER_SUBSTRING in str(name).upper() +# --- NOVA FUNÇÃO HELPER: NORMALIZAÇÃO DE CHAVES --- + +def _normalize_keys_to_upper(data: Any) -> Any: + """ + Recursivamente converte todas as chaves de dicionário para UPPERCASE. + Mantém os valores (conteúdo) originais. + """ + if isinstance(data, dict): + new_dict = {} + for k, v in data.items(): + # Converte a chave para UPPERCASE se for string + new_k = k.upper() if isinstance(k, str) else k + # Chama recursivamente para o valor + new_dict[new_k] = _normalize_keys_to_upper(v) + return new_dict + elif isinstance(data, list): + # Itera sobre a lista + return [_normalize_keys_to_upper(item) for item in data] + else: + # Retorna o valor (string, int, etc.) + return data + # --- FUNÇÃO HELPER PARA REMOÇÃO DE SOURCE_CODE --- def _remove_source_code(item: Dict) -> Dict: """ Remove o campo 'SOURCE_CODE' ou 'source_code' de um item de dicionário. - (Case-insensitive para chaves comuns de código-fonte). + (Funciona após a normalização, pois a chave sempre será 'SOURCE_CODE'). """ item_copy = item.copy() + # Após a normalização para UPPERCASE, só precisamos checar 'SOURCE_CODE' if 'SOURCE_CODE' in item_copy: del item_copy['SOURCE_CODE'] - if 'source_code' in item_copy: - del item_copy['source_code'] - return item_copy # --- FUNÇÃO HELPER PARA OBTER IDENTIFICADOR --- def get_element_identifier(item): - """Retorna o principal identificador de um elemento de schema.""" + """Retorna o principal identificador de um elemento de schema (assume UPPERCASE).""" return ( item.get('TABLE_NAME') or item.get('CONSTRAINT_NAME') or @@ -45,33 +65,32 @@ def get_element_identifier(item): item.get('TRIGGER_NAME') ) -# --- FUNÇÃO AUXILIAR PARA COMPARAÇÃO DE CAMPOS (Omitida, pois não é chamada) --- +# --- FUNÇÕES DE COMPARAÇÃO (Mantidas mas não chamadas para o retorno) --- def compare_fields(standard_fields: List[Dict], client_fields: List[Dict], table_name: str) -> List[Dict]: pass - -# --- FUNÇÃO PRINCIPAL DE COMPARAÇÃO DE ESTRUTURAS (Omitida, pois não é chamada) --- def compare_structures(standard_structure: Dict[str, Any], client_structure: Dict[str, Any]) -> Dict[str, Any]: pass -# --- FUNÇÃO: ELEMENTOS SOMENTE NO CLIENTE --- +# --- FUNÇÃO: ELEMENTOS SOMENTE NO CLIENTE (Agora usa chaves UPPERCASE) --- def find_client_only_elements(standard_structure: Dict[str, Any], client_structure: Dict[str, Any]) -> Dict[str, Any]: """ Identifica elementos presentes na estrutura do cliente mas ausentes na estrutura padrão. - Filtra elementos que contenham a substring CONVERSAO. + Assume que as estruturas de entrada já estão com as chaves em UPPERCASE. """ client_only = {} # --- 1. TABLES (Lidando com a estrutura achatada do cliente) --- # 1a. Normalize e Filtra Tabelas Padrão - standard_tables_names = {t.get('TABLE_NAME') for t in standard_structure.get('tables', []) + standard_tables_names = {t.get('TABLE_NAME') for t in standard_structure.get('TABLES', []) if t.get('TABLE_NAME') and not is_ignored(t.get('TABLE_NAME'))} # 1b. Agrupa e Filtra Campos do Cliente por Nome de Tabela client_fields_by_table: Dict[str, List[Dict]] = {} - for field_item in client_structure.get('tables', []): + # A chave do nível superior agora é 'TABLES' (em UPPERCASE) + for field_item in client_structure.get('TABLES', []): table_name = field_item.get('TABLE_NAME') # Filtra a tabela e o elemento, se for CONVERSAO if table_name and not is_ignored(table_name): @@ -93,11 +112,12 @@ def find_client_only_elements(standard_structure: Dict[str, Any], client_structu }) if client_only_tables: - client_only["tabelas_unicas"] = client_only_tables + client_only["TABELAS_UNICAS"] = client_only_tables # --- 2. OUTROS ELEMENTOS (PKs, FKs, etc.) --- - elements_to_check = ['primary_keys', 'foreign_keys', 'indexes', 'views', 'procedures', 'triggers'] + # Chaves de nível superior agora em UPPERCASE + elements_to_check = ['PRIMARY_KEYS', 'FOREIGN_KEYS', 'INDEXES', 'VIEWS', 'PROCEDURES', 'TRIGGERS'] for element_key in elements_to_check: standard_elements = standard_structure.get(element_key, []) @@ -108,6 +128,7 @@ def find_client_only_elements(standard_structure: Dict[str, Any], client_structu # Standard Set (Filtrado para IGNORED) standard_elements_filtered = [item for item in standard_elements if not is_ignored(get_element_identifier(item))] + # O _remove_source_code é chamado após a normalização. standard_set = {json.dumps(_remove_source_code(item), sort_keys=True) for item in standard_elements_filtered} @@ -126,7 +147,7 @@ def find_client_only_elements(standard_structure: Dict[str, Any], client_structu unique_client_elements_str = client_set - standard_set if unique_client_elements_str: - client_only[element_key + "_unicos"] = [ + client_only[element_key + "_UNICOS"] = [ client_set_normalized[json_str] for json_str in unique_client_elements_str ] @@ -138,19 +159,20 @@ def find_client_only_elements(standard_structure: Dict[str, Any], client_structu class ShowDatabaseService: def _clean_full_structure_for_output(self, structure: Dict[str, Any]) -> Dict[str, Any]: - """Cria uma cópia limpa da estrutura, removendo 'SOURCE_CODE' em todos os elementos relevantes.""" + """Cria uma cópia limpa da estrutura, removendo 'SOURCE_CODE' em todos os elementos relevantes. + Assume que a estrutura já está em UPPERCASE.""" cleaned = json.loads(json.dumps(structure)) - keys_to_clean = ['tables', 'views', 'procedures', 'triggers', 'primary_keys', 'foreign_keys', 'indexes'] + keys_to_clean = ['TABLES', 'VIEWS', 'PROCEDURES', 'TRIGGERS', 'PRIMARY_KEYS', 'FOREIGN_KEYS', 'INDEXES'] for key in keys_to_clean: if key in cleaned and isinstance(cleaned[key], list): # Aplica a limpeza a cada item da lista cleaned[key] = [_remove_source_code(item) for item in cleaned[key]] - # Garante que, se houver campos (FIELDS) aninhados (no padrão), eles também sejam limpos - if 'tables' in cleaned and isinstance(cleaned['tables'], list): - for table in cleaned['tables']: + # Garante que, se houver campos (FIELDS) aninhados, eles também sejam limpos + if 'TABLES' in cleaned and isinstance(cleaned['TABLES'], list): + for table in cleaned['TABLES']: if 'FIELDS' in table and isinstance(table['FIELDS'], list): table['FIELDS'] = [_remove_source_code(field) for field in table['FIELDS']] @@ -176,6 +198,10 @@ class ShowDatabaseService: database_data = dados_json.get("database", {}) client_structure: Dict[str, Any] = database_data.get("structure", {}) + # 🚨 NOVO PASSO CRUCIAL: Normaliza as chaves para UPPERCASE em ambas as estruturas + standard_structure_data = _normalize_keys_to_upper(standard_structure_data) + client_structure = _normalize_keys_to_upper(client_structure) + # Limpa ambas as estruturas de 'source_code' para o output de debug debug_cliente = self._clean_full_structure_for_output(client_structure) debug_padrao = self._clean_full_structure_for_output(standard_structure_data) @@ -189,22 +215,22 @@ class ShowDatabaseService: # 1. Lista para Análise Visual (estruturas Padrão e Cliente) data_list_for_visual_check = [ { - "id": 1, - "estrutura": "Padrão (standard_structure_json)", - "conteudo": debug_padrao + "ID": 1, + "ESTRUTURA": "PADRÃO (STANDARD_STRUCTURE_JSON) - CHAVES NORMALIZADAS", + "CONTEUDO": debug_padrao }, { - "id": 2, - "estrutura": "Cliente (client_structure)", - "conteudo": debug_cliente + "ID": 2, + "ESTRUTURA": "CLIENTE (CLIENT_STRUCTURE) - CHAVES NORMALIZADAS", + "CONTEUDO": debug_cliente } ] # 2. Monta o retorno final com as duas seções return { - "message": "Estruturas de Banco de Dados para Análise Visual e Itens Exclusivos do Cliente", - "data": data_list_for_visual_check, - "itens_nao_encontrados_no_padrao": elementos_unicos_cliente + "MESSAGE": "ESTRUTURAS DE BANCO DE DADOS PARA ANÁLISE VISUAL E ITENS EXCLUSIVOS DO CLIENTE", + "DATA": data_list_for_visual_check, + "ITENS_NAO_ENCONTRADOS_NO_PADRAO": elementos_unicos_cliente } else: