from sqlalchemy import inspect from app.database.models import HistoricoAlteracoes, HistoricoDelete, HistoricoUpdate from datetime import datetime, timezone # def audit_log(func): # async def wrapper(*args, **kwargs): # self = args[0] # O primeiro argumento será 'self', que é a instância do repositório # session = self.session # # # Verificar se o modelo está habilitado para auditoria # if not getattr(self.model, 'log_auditoria_habilitado', False): # # Executa a função original sem auditoria # result = await func(*args, **kwargs) # # # Realiza o commit e refresh # await session.commit() # # # Verificar se é uma operação de UPDATE ou DELETE # db_models = result.get("db_models") # operation = result.get("operation") # rowcount = result.get("rowcount") # # # Atualiza o db_model com refresh em caso de UPDATE # if operation == "UPDATE" and db_models: # for db_model in db_models: # await session.refresh(db_model) # # # Retorna o que a rota espera: o db_model para update ou rowcount para delete # if operation == "UPDATE": # if len(db_models) == 1: # return db_models[0].__dict__ # Retorna o único modelo como dicionário # else: # return db_models # Retorna a lista de modelos # else: # return rowcount # Retorna rowcount no caso de DELETE # # # Caso a auditoria esteja habilitada, continuar com o processo de auditoria # # Tenta executar a função original e captura o retorno # result = await func(*args, **kwargs) # # # Captura o db_model e a operação realizada # db_models = result.get("db_models") # original_models = result.get("original_models") # operation = result.get("operation") # rowcount = result.get("rowcount") # # # Para operações de UPDATE # log_entries = [] # if operation == "UPDATE" and db_models: # timestamp = datetime.now(timezone.utc).replace(tzinfo=None) # # # Iterar pelos modelos atualizados # for db_model, original_model in zip(db_models, original_models): # inspector = inspect(db_model) # primary_key = str(inspect(db_model).identity[0]) # # # Verificar alterações em cada atributo # for attr in inspector.attrs: # old_value = original_model.get(attr.key, None) # new_value = getattr(db_model, attr.key, None) # # # Confirmar se houve mudança nos valores # if old_value != new_value: # log_entry = Historico_Alteracoes( # tabela=db_model.__tablename__, # coluna=attr.key, # valor_antigo=str(old_value) if old_value else None, # valor_novo=str(new_value) if new_value else None, # data_modificacao=timestamp, # action='UPDATE', # usuario_id=kwargs.get('user_id'), # registro_id=primary_key # ) # log_entries.append(log_entry) # # # Verificar se é uma operação de DELETE # elif operation == "DELETE": # timestamp_naive = datetime.now(timezone.utc).replace(tzinfo=None) # # if isinstance(db_models, list): # Caso seja uma lista de modelos deletados (delete_many) # for db_model in db_models: # primary_key = str(inspect(db_model).identity[0]) # deletado = {attr: value for attr, value in db_model.__dict__.items() # if not isinstance(value, list) and not attr.startswith('_')} # # json_deletado = json.dumps(deletado, default=str) # # log_entry = Historico_Alteracoes( # tabela=db_model.__tablename__, # coluna='N/A', # Especificar que esta é uma operação de DELETE # valor_antigo=str(deletado), # Salvar o estado completo do modelo deletado # valor_novo=None, # Valor novo é None porque o registro foi deletado # data_modificacao=timestamp_naive, # action='DELETE', # usuario_id=kwargs.get('user_id'), # registro_id=primary_key # ) # log_entries.append(log_entry) # # else: # Caso seja apenas um modelo (delete_one) # primary_key = str(inspect(db_models).identity[0]) # deletado = {attr: value for attr, value in db_models.__dict__.items() # if not isinstance(value, list) and not attr.startswith('_')} # # log_entry = Historico_Alteracoes( # tabela=db_models.__tablename__, # coluna='N/A', # Especificar que esta é uma operação de DELETE # valor_antigo=str(deletado), # Salvar o estado completo do modelo deletado # valor_novo=None, # Valor novo é None porque o registro foi deletado # data_modificacao=timestamp_naive, # action='DELETE', # usuario_id=kwargs.get('user_id'), # registro_id=primary_key # ) # log_entries.append(log_entry) # # # Se houver log_entries a serem salvas (no caso de UPDATE ou DELETE) # if log_entries: # session.add_all(log_entries) # # # Realizar o commit no final, para ambos UPDATE e DELETE # await session.commit() # # # Se for um update, atualiza o db_model com refresh # if operation == "UPDATE" and db_models: # for db_model in db_models: # await session.refresh(db_model) # # # Retorna o que a rota espera: o db_model para update ou rowcount para delete # if operation == "UPDATE": # if len(db_models) == 1: # return db_models[0].__dict__ # Retorna o único modelo como dicionário # else: # return db_models # Retorna a lista de modelos # else: # return rowcount # Retorna rowcount no caso de DELETE # # return wrapper # def audit_log(func): # async def wrapper(*args, **kwargs): # self = args[0] # O primeiro argumento será 'self', que é a instância do repositório # session = self.session # # # Verificar se o modelo está habilitado para auditoria # if not getattr(self.model, 'log_auditoria_habilitado', False): # # Executa a função original sem auditoria # result = await func(*args, **kwargs) # # # Realiza o commit e refresh # await session.commit() # # # Verificar se é uma operação de UPDATE ou DELETE # db_models = result.get("db_models") # operation = result.get("operation") # rowcount = result.get("rowcount") # # # Atualiza o db_model com refresh em caso de UPDATE # if operation == "UPDATE" and db_models: # for db_model in db_models: # await session.refresh(db_model) # print("refreshing relaizado") # # # Retorna o que a rota espera: o db_model para update ou rowcount para delete # if operation == "UPDATE": # if len(db_models) == 1: # print(db_models[0].__dict__) # return db_models[0].__dict__ # Retorna o único modelo como dicionário # else: # return db_models # Retorna a lista de modelos # else: # return rowcount # Retorna rowcount no caso de DELETE # # # Caso a auditoria esteja habilitada, continuar com o processo de auditoria # # Tenta executar a função original e captura o retorno # result = await func(*args, **kwargs) # # # Captura o db_model e a operação realizada # db_models = result.get("db_models") # original_models = result.get("original_models") # operation = result.get("operation") # rowcount = result.get("rowcount") # # # Para operações de UPDATE # log_entries = [] # timestamp = datetime.now(timezone.utc).replace(tzinfo=None) # if operation == "UPDATE" and db_models: # # timestamp = datetime.now(timezone.utc).replace(tzinfo=None) # # # Iterar pelos modelos atualizados # for db_model, original_model in zip(db_models, original_models): # inspector = inspect(db_model) # primary_key = str(inspect(db_model).identity[0]) # # # Registro na tabela HistoricoAlteracoes # log_entry_update = HistoricoAlteracoes( # tabela=db_model.__tablename__, # data_modificacao=timestamp, # action='UPDATE', # usuario_id=kwargs.get('user_id'), # registro_id=primary_key # ) # session.add(log_entry_update) # # # Verificar alterações em cada atributo # for attr in inspector.attrs: # old_value = original_model.get(attr.key, None) # new_value = getattr(db_model, attr.key, None) # # # Confirmar se houve mudança nos valores # if old_value != new_value: # log_update = HistoricoUpdate( # coluna=attr.key, # valor_antigo=str(old_value) if old_value else None, # valor_novo=str(new_value) if new_value else None, # alteracao=log_entry_update # Relacionando ao log de alteração principal # ) # session.add(log_update) # # # Verificar se é uma operação de DELETE # elif operation == "DELETE": # _timestamp_naive = datetime.now(timezone.utc).replace(tzinfo=None) # # if isinstance(db_models, list): # Caso seja uma lista de modelos deletados (delete_many) # for db_model in db_models: # primary_key = str(inspect(db_model).identity[0]) # # # Filtra os campos que não são relationship # # def is_relationship(attr_name, model): # """ # Função que verifica se um atributo é do tipo relacionamento no SQLAlchemy # """ # # Inspeciona o modelo SQLAlchemy # mapper = inspect(model.__class__) # # # Acessa todas as relationships do modelo # relationships = mapper.relationships # # # Verifica se o atributo atual é uma relationship # return attr_name in relationships # # deletado = {attr: value for attr, value in db_model.__dict__.items() # if # not isinstance(value, list) and not attr.startswith('_') and not # is_relationship(attr, db_model)} # # # Registro na tabela HistoricoAlteracoes # log_entry_delete = HistoricoAlteracoes( # tabela=db_model.__tablename__, # data_modificacao=timestamp, # action='DELETE', # usuario_id=kwargs.get('user_id'), # registro_id=primary_key # ) # session.add(log_entry_delete) # # log_delete = HistoricoDelete( # registro_deletado=str(deletado), # Serializar o registro deletado # alteracao=log_entry_delete # Relacionando ao log de alteração principal # ) # session.add(log_delete) # # else: # Caso seja apenas um modelo (delete_one) # primary_key = str(inspect(db_models).identity[0]) # # def is_relationship(attr_name, model): # """ # Função que verifica se um atributo é do tipo relacionamento no SQLAlchemy # """ # # Inspeciona o modelo SQLAlchemy # mapper = inspect(model.__class__) # # # Acessa todas as relationships do modelo # relationships = mapper.relationships # # # Verifica se o atributo atual é uma relationship # return attr_name in relationships # # # Filtra os campos que não são relationship # deletado = {attr: value for attr, value in db_models.__dict__.items() # if not isinstance(value, list) and not attr.startswith('_') and not # is_relationship(attr, db_models)} # # # Registro na tabela HistoricoAlteracoes # log_entry_delete = HistoricoAlteracoes( # tabela=db_models.__tablename__, # data_modificacao=timestamp, # action='DELETE', # usuario_id=kwargs.get('user_id'), # registro_id=primary_key # ) # session.add(log_entry_delete) # # log_delete = HistoricoDelete( # registro_deletado=str(deletado), # Serializar o registro deletado # alteracao=log_entry_delete # Relacionando ao log de alteração principal # ) # session.add(log_delete) # # # Se houver log_entries a serem salvas (no caso de UPDATE ou DELETE) # if log_entries: # session.add_all(log_entries) # # # Realizar o commit no final, para ambos UPDATE e DELETE # await session.commit() # # # Se for um update, atualiza o db_model com refresh # if operation == "UPDATE" and db_models: # for db_model in db_models: # await session.refresh(db_model) # # # Retorna o que a rota espera: o db_model para update ou rowcount para delete # if operation == "UPDATE": # if len(db_models) == 1: # return db_models[0].__dict__ # Retorna o único modelo como dicionário # else: # return db_models # Retorna a lista de modelos # else: # return rowcount # Retorna rowcount no caso de DELETE # # return wrapper def audit_log(func): async def wrapper(*args, **kwargs): self = args[0] # 'self' é a instância do repositório session = self.session # Se auditoria não estiver habilitada, processa sem auditoria if not getattr(self.model, 'log_auditoria_habilitado', False): result = await func(*args, **kwargs) await session.commit() db_models = result.get("db_models") operation = result.get("operation") rowcount = result.get("rowcount") if operation == "UPDATE" and db_models: if isinstance(db_models, list): for db_model in db_models: await session.refresh(db_model) print("refreshing realizado") else: await session.refresh(db_models) # Retorna exatamente o que a função original produziu if operation == "UPDATE": return db_models else: return rowcount # Auditoria habilitada: chama a função original e captura o retorno result = await func(*args, **kwargs) db_models = result.get("db_models") original_models = result.get("original_models") operation = result.get("operation") rowcount = result.get("rowcount") # Variável de controle: se o retorno for um objeto único, single_update será True. single_update = False original_db_model = None if not isinstance(db_models, list): single_update = True original_db_model = db_models # Guarda o objeto único original db_models = [db_models] # Encapsula para processamento da auditoria original_models = [original_models] # Processamento da auditoria para UPDATE if operation == "UPDATE" and db_models: timestamp = datetime.now(timezone.utc).replace(tzinfo=None) for db_model, original_model in zip(db_models, original_models): inspector = inspect(db_model) primary_key = str(inspect(db_model).identity[0]) log_entry_update = HistoricoAlteracoes( tabela=db_model.__tablename__, data_modificacao=timestamp, action='UPDATE', usuario_id=kwargs.get('user_id'), registro_id=primary_key ) session.add(log_entry_update) # Itera pelos atributos mapeados e registra alterações for attr in inspector.attrs: old_value = original_model.get(attr.key, None) new_value = getattr(db_model, attr.key, None) if old_value != new_value: log_update = HistoricoUpdate( coluna=attr.key, valor_antigo=str(old_value) if old_value is not None else None, valor_novo=str(new_value) if new_value is not None else None, alteracao=log_entry_update ) session.add(log_update) # Processamento da auditoria para DELETE elif operation == "DELETE": _timestamp_naive = datetime.now(timezone.utc).replace(tzinfo=None) if isinstance(db_models, list): # Caso seja delete_many for db_model in db_models: primary_key = str(inspect(db_model).identity[0]) def is_relationship(attr_name, model): mapper = inspect(model.__class__) return attr_name in mapper.relationships deletado = { attr: value for attr, value in db_model.__dict__.items() if not isinstance(value, list) and not attr.startswith('_') and not is_relationship(attr, db_model) } log_entry_delete = HistoricoAlteracoes( tabela=db_model.__tablename__, data_modificacao=_timestamp_naive, action='DELETE', usuario_id=kwargs.get('user_id'), registro_id=primary_key ) session.add(log_entry_delete) log_delete = HistoricoDelete( registro_deletado=str(deletado), alteracao=log_entry_delete ) session.add(log_delete) else: # Caso delete_one primary_key = str(inspect(db_models).identity[0]) def is_relationship(attr_name, model): mapper = inspect(model.__class__) return attr_name in mapper.relationships deletado = { attr: value for attr, value in db_models.__dict__.items() if not isinstance(value, list) and not attr.startswith('_') and not is_relationship(attr, db_models) } log_entry_delete = HistoricoAlteracoes( tabela=db_models.__tablename__, data_modificacao=_timestamp_naive, action='DELETE', usuario_id=kwargs.get('user_id'), registro_id=primary_key ) session.add(log_entry_delete) log_delete = HistoricoDelete( registro_deletado=str(deletado), alteracao=log_entry_delete ) session.add(log_delete) # Realiza o commit final após registrar a auditoria await session.commit() # Para operações de UPDATE, faz refresh dos objetos if operation == "UPDATE" and db_models: for db_model in db_models: await session.refresh(db_model) # Retorno final: se for update e se for update_by_id (single_update=True), # retorna o objeto único; caso contrário, retorna a lista, mantendo o contrato original. if operation == "UPDATE": return original_db_model if single_update else db_models else: return rowcount return wrapper