Spaces:
Running
Running
| from __future__ import annotations | |
| import os | |
| from typing import Any | |
| from fastapi import APIRouter, File, Form, Request, UploadFile | |
| from fastapi.responses import FileResponse | |
| from pydantic import BaseModel, Field | |
| from app.services import auth_service, elaboracao_service | |
| from app.services.audit_log_service import log_event | |
| from app.services.session_store import session_store | |
| router = APIRouter(prefix="/api/elaboracao", tags=["elaboracao"]) | |
| class SessionPayload(BaseModel): | |
| session_id: str | |
| class ConfirmSheetPayload(SessionPayload): | |
| sheet_name: str | |
| class MapCoordsPayload(SessionPayload): | |
| col_lat: str | |
| col_lon: str | |
| class GeocodePayload(SessionPayload): | |
| col_cdlog: str | |
| col_num: str | |
| auto_200: bool = False | |
| class CorrecaoGeo(BaseModel): | |
| linha: int | |
| cdlog_corrigido: str | None = None | |
| numero_corrigido: str | None = None | |
| class GeocodeCorrecaoPayload(SessionPayload): | |
| correcoes: list[CorrecaoGeo] = Field(default_factory=list) | |
| auto_200: bool = False | |
| class ApplySelectionPayload(SessionPayload): | |
| coluna_y: str | |
| colunas_x: list[str] | |
| tipo_y: str | None = None | |
| coluna_area: str | None = None | |
| dicotomicas: list[str] = Field(default_factory=list) | |
| codigo_alocado: list[str] = Field(default_factory=list) | |
| percentuais: list[str] = Field(default_factory=list) | |
| outliers_anteriores: list[int] = Field(default_factory=list) | |
| grau_min_coef: int = 0 | |
| grau_min_f: int = 0 | |
| class ClassificarXPayload(SessionPayload): | |
| colunas_x: list[str] = Field(default_factory=list) | |
| class SearchTransformPayload(SessionPayload): | |
| grau_min_coef: int = 0 | |
| grau_min_f: int = 0 | |
| transformacoes_fixas: dict[str, str] = Field(default_factory=dict) | |
| transformacao_y_fixa: str | None = None | |
| class AdoptSuggestionPayload(SessionPayload): | |
| indice: int | |
| class FitModelPayload(SessionPayload): | |
| transformacao_y: str | |
| transformacoes_x: dict[str, str] = Field(default_factory=dict) | |
| dicotomicas: list[str] = Field(default_factory=list) | |
| codigo_alocado: list[str] = Field(default_factory=list) | |
| percentuais: list[str] = Field(default_factory=list) | |
| class DispersaoPayload(SessionPayload): | |
| eixo_x: str = "transformado" | |
| eixo_y_tipo: str = "y_transformado" | |
| eixo_y_residuo: str | None = None | |
| eixo_y_coluna: str | None = None | |
| class DispersaoInterativoPayload(SessionPayload): | |
| alvo: str = "secao10" | |
| class DiagnosticoInterativoPayload(SessionPayload): | |
| grafico: str = "obs_calc" | |
| class TransformPreviewPayload(SessionPayload): | |
| transformacao_y: str = "(x)" | |
| transformacoes_x: dict[str, str] = Field(default_factory=dict) | |
| class OutlierFiltro(BaseModel): | |
| variavel: str | |
| operador: str | |
| valor: float | |
| class OutlierFilterPayload(SessionPayload): | |
| filtros: list[OutlierFiltro] = Field(default_factory=list) | |
| class OutlierRestartPayload(SessionPayload): | |
| outliers_texto: str | None = None | |
| reincluir_texto: str | None = None | |
| grau_min_coef: int = 3 | |
| grau_min_f: int = 3 | |
| class OutlierSummaryPayload(SessionPayload): | |
| outliers_texto: str | None = None | |
| reincluir_texto: str | None = None | |
| class AvaliacaoPayload(SessionPayload): | |
| valores_x: dict[str, Any] | |
| indice_base: str | None = None | |
| class AvaliacaoKnnDetalhesPayload(SessionPayload): | |
| valores_x: dict[str, Any] | |
| class AvaliacaoDeletePayload(SessionPayload): | |
| indice: str | None = None | |
| indice_base: str | None = None | |
| class AvaliacaoBasePayload(SessionPayload): | |
| indice_base: str | None = None | |
| class ExportModeloPayload(SessionPayload): | |
| nome_arquivo: str | |
| elaborador: dict[str, Any] | None = None | |
| observacao_modelo: str | None = None | |
| class SalvarModeloRepositorioPayload(ExportModeloPayload): | |
| confirmar_substituicao: bool = False | |
| class UpdateMapaPayload(SessionPayload): | |
| variavel_mapa: str | None = None | |
| modo_mapa: str | None = None | |
| escala_extremo_abs: float | None = None | |
| class ColunaDataMercadoPayload(SessionPayload): | |
| coluna_data: str | |
| class RepositorioModeloPayload(SessionPayload): | |
| modelo_id: str | |
| async def upload_file( | |
| session_id: str = Form(...), | |
| file: UploadFile = File(...), | |
| ) -> dict[str, Any]: | |
| session = session_store.get(session_id) | |
| conteudo = await file.read() | |
| elaboracao_service.save_uploaded_file(session, file.filename, conteudo) | |
| return elaboracao_service.load_uploaded_file(session) | |
| def repositorio_modelos(request: Request) -> dict[str, Any]: | |
| user = auth_service.require_user(request) | |
| resposta = elaboracao_service.listar_modelos_repositorio() | |
| log_event( | |
| "repositorio", | |
| "listar_modelos_elaboracao", | |
| user=user, | |
| request=request, | |
| details={"total_modelos": resposta.get("total_modelos")}, | |
| ) | |
| return resposta | |
| def repositorio_carregar(payload: RepositorioModeloPayload, request: Request) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| user = auth_service.require_user(request) | |
| resposta = elaboracao_service.carregar_modelo_repositorio(session, payload.modelo_id) | |
| log_event( | |
| "repositorio", | |
| "carregar_modelo_elaboracao", | |
| user=user, | |
| session_id=payload.session_id, | |
| request=request, | |
| details={"modelo_id": payload.modelo_id}, | |
| ) | |
| return resposta | |
| def confirm_sheet(payload: ConfirmSheetPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.load_uploaded_file(session, selected_sheet=payload.sheet_name) | |
| def map_coords(payload: MapCoordsPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.mapear_coordenadas_manualmente(session, payload.col_lat, payload.col_lon) | |
| def geocodificar(payload: GeocodePayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.geocodificar(session, payload.col_cdlog, payload.col_num, auto_200=payload.auto_200) | |
| def geocodificar_correcoes(payload: GeocodeCorrecaoPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| correcoes = [item.model_dump() for item in payload.correcoes] | |
| return elaboracao_service.aplicar_correcoes_geocodificacao(session, correcoes, auto_200=payload.auto_200) | |
| def geocodificar_reiniciar(payload: SessionPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.reiniciar_geocodificacao(session) | |
| def geocodificar_excluir_coords(payload: SessionPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.excluir_coordenadas_para_geocodificacao(session) | |
| def apply_selection(payload: ApplySelectionPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.apply_selection( | |
| session, | |
| coluna_y=payload.coluna_y, | |
| colunas_x=payload.colunas_x, | |
| tipo_y=payload.tipo_y, | |
| coluna_area=payload.coluna_area, | |
| dicotomicas=payload.dicotomicas, | |
| codigo_alocado=payload.codigo_alocado, | |
| percentuais=payload.percentuais, | |
| outliers_anteriores=payload.outliers_anteriores, | |
| grau_min_coef=payload.grau_min_coef, | |
| grau_min_f=payload.grau_min_f, | |
| ) | |
| def classify_x(payload: ClassificarXPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.classificar_tipos_variaveis_x(session, payload.colunas_x) | |
| def search_transformations(payload: SearchTransformPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.search_transformacoes( | |
| session, | |
| grau_min_coef=payload.grau_min_coef, | |
| grau_min_f=payload.grau_min_f, | |
| transformacoes_fixas_usuario=payload.transformacoes_fixas, | |
| transformacao_y_fixa_usuario=payload.transformacao_y_fixa, | |
| ) | |
| def adopt_suggestion(payload: AdoptSuggestionPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.adotar_sugestao(session, payload.indice) | |
| def fit_model(payload: FitModelPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.fit_model( | |
| session, | |
| transformacao_y=payload.transformacao_y, | |
| transformacoes_x=payload.transformacoes_x, | |
| dicotomicas=payload.dicotomicas, | |
| codigo_alocado=payload.codigo_alocado, | |
| percentuais=payload.percentuais, | |
| ) | |
| def model_dispersao(payload: DispersaoPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.gerar_grafico_dispersao_modelo( | |
| session, | |
| eixo_x=payload.eixo_x, | |
| eixo_y_tipo=payload.eixo_y_tipo, | |
| eixo_y_residuo=payload.eixo_y_residuo, | |
| eixo_y_coluna=payload.eixo_y_coluna, | |
| ) | |
| def dispersao_interativo(payload: DispersaoInterativoPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.obter_grafico_dispersao_interativo(session, payload.alvo) | |
| def diagnostico_interativo(payload: DiagnosticoInterativoPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.obter_grafico_diagnostico_interativo(session, payload.grafico) | |
| def transform_preview(payload: TransformPreviewPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.preview_transformacao_manual( | |
| session, | |
| transformacao_y=payload.transformacao_y, | |
| transformacoes_x=payload.transformacoes_x, | |
| ) | |
| def outliers_apply_filters(payload: OutlierFilterPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| filtros = [item.model_dump() for item in payload.filtros] | |
| return elaboracao_service.apply_outlier_filters(session, filtros) | |
| def outliers_apply_filters_recursive(payload: OutlierFilterPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| filtros = [item.model_dump() for item in payload.filtros] | |
| return elaboracao_service.apply_outlier_filters_recursive(session, filtros) | |
| def outliers_restart(payload: OutlierRestartPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.reiniciar_iteracao( | |
| session, | |
| outliers_texto=payload.outliers_texto, | |
| reincluir_texto=payload.reincluir_texto, | |
| grau_min_coef=payload.grau_min_coef, | |
| grau_min_f=payload.grau_min_f, | |
| ) | |
| def outliers_summary(payload: OutlierSummaryPayload) -> dict[str, str]: | |
| session = session_store.get(payload.session_id) | |
| texto = elaboracao_service.resumir_outliers( | |
| session.outliers_anteriores, | |
| payload.outliers_texto, | |
| payload.reincluir_texto, | |
| ) | |
| return {"resumo": texto} | |
| def outliers_clear_history(payload: SessionPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.limpar_historico_outliers(session) | |
| def evaluation_fields(payload: SessionPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return {"campos": elaboracao_service.build_campos_avaliacao(session)} | |
| def evaluation_calculate(payload: AvaliacaoPayload, request: Request) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| user = auth_service.require_user(request) | |
| resposta = elaboracao_service.calcular_avaliacao_elaboracao(session, payload.valores_x, payload.indice_base) | |
| log_event( | |
| "elaboracao", | |
| "avaliacao_calcular", | |
| user=user, | |
| session_id=payload.session_id, | |
| request=request, | |
| details={"total_avaliacoes": len(resposta.get("avaliacoes") or [])}, | |
| ) | |
| return resposta | |
| def evaluation_knn_details(payload: AvaliacaoKnnDetalhesPayload, request: Request) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| user = auth_service.require_user(request) | |
| resposta = elaboracao_service.detalhes_knn_avaliacao_elaboracao(session, payload.valores_x) | |
| log_event( | |
| "elaboracao", | |
| "avaliacao_knn_detalhes", | |
| user=user, | |
| session_id=payload.session_id, | |
| request=request, | |
| ) | |
| return resposta | |
| def evaluation_clear(payload: SessionPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.limpar_avaliacoes_elaboracao(session) | |
| def evaluation_delete(payload: AvaliacaoDeletePayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.excluir_avaliacao_elaboracao(session, payload.indice, payload.indice_base) | |
| def evaluation_base(payload: AvaliacaoBasePayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.atualizar_base_avaliacao_elaboracao(session, payload.indice_base) | |
| def evaluation_export(session_id: str) -> FileResponse: | |
| session = session_store.get(session_id) | |
| caminho = elaboracao_service.exportar_avaliacoes_elaboracao(session) | |
| return FileResponse( | |
| path=caminho, | |
| media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| filename=os.path.basename(caminho), | |
| ) | |
| def equation_export(session_id: str, mode: str = "excel") -> FileResponse: | |
| session = session_store.get(session_id) | |
| caminho, nome = elaboracao_service.exportar_equacao(session, mode) | |
| return FileResponse( | |
| path=caminho, | |
| media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| filename=nome, | |
| ) | |
| def listar_avaliadores() -> dict[str, Any]: | |
| return {"avaliadores": elaboracao_service.list_avaliadores()} | |
| def export_model(payload: ExportModeloPayload, request: Request) -> FileResponse: | |
| session = session_store.get(payload.session_id) | |
| caminho, _ = elaboracao_service.exportar_modelo( | |
| session, | |
| payload.nome_arquivo, | |
| elaborador=payload.elaborador, | |
| observacao_modelo=payload.observacao_modelo, | |
| ) | |
| user = auth_service.require_user(request) | |
| log_event( | |
| "elaboracao", | |
| "exportar_modelo", | |
| user=user, | |
| session_id=payload.session_id, | |
| request=request, | |
| details={"nome_arquivo": os.path.basename(caminho)}, | |
| ) | |
| return FileResponse( | |
| path=caminho, | |
| media_type="application/octet-stream", | |
| filename=os.path.basename(caminho), | |
| ) | |
| def save_model_repository(payload: SalvarModeloRepositorioPayload, request: Request) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| user = auth_service.require_admin(request) | |
| resposta = elaboracao_service.salvar_modelo_repositorio( | |
| session, | |
| payload.nome_arquivo, | |
| elaborador=payload.elaborador, | |
| observacao_modelo=payload.observacao_modelo, | |
| actor=user.get("usuario"), | |
| confirmar_substituicao=payload.confirmar_substituicao, | |
| ) | |
| log_event( | |
| "repositorio", | |
| "salvar_modelo_repositorio_elaboracao", | |
| user=user, | |
| session_id=payload.session_id, | |
| request=request, | |
| details={ | |
| "nome_arquivo": payload.nome_arquivo, | |
| "confirmar_substituicao": bool(payload.confirmar_substituicao), | |
| "substituidos": resposta.get("resultado_upload", {}).get("substituidos", []), | |
| }, | |
| ) | |
| return resposta | |
| def export_base(session_id: str, filtered: bool = True) -> FileResponse: | |
| session = session_store.get(session_id) | |
| caminho = elaboracao_service.exportar_base(session, usar_filtrado=filtered) | |
| return FileResponse( | |
| path=caminho, | |
| media_type="text/csv", | |
| filename=os.path.basename(caminho), | |
| ) | |
| def map_update(payload: UpdateMapaPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.atualizar_mapa(session, payload.variavel_mapa, payload.modo_mapa) | |
| def residuos_map_update(payload: UpdateMapaPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.atualizar_mapa_residuos( | |
| session, | |
| payload.variavel_mapa, | |
| payload.modo_mapa, | |
| payload.escala_extremo_abs, | |
| ) | |
| def market_date_preview(payload: ColunaDataMercadoPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.previsualizar_coluna_data_mercado(session, payload.coluna_data) | |
| def market_date_apply(payload: ColunaDataMercadoPayload) -> dict[str, Any]: | |
| session = session_store.get(payload.session_id) | |
| return elaboracao_service.aplicar_coluna_data_mercado(session, payload.coluna_data) | |
| def context(session_id: str) -> dict[str, Any]: | |
| session = session_store.get(session_id) | |
| return { | |
| "coluna_y": session.coluna_y, | |
| "tipo_y": session.tipo_y, | |
| "coluna_area": session.coluna_area, | |
| "colunas_area_candidatas": elaboracao_service.detectar_colunas_area(session.df_original) if session.df_original is not None else [], | |
| "colunas_x": session.colunas_x, | |
| "dicotomicas": session.dicotomicas, | |
| "codigo_alocado": session.codigo_alocado, | |
| "percentuais": session.percentuais, | |
| "outliers_anteriores": session.outliers_anteriores, | |
| "iteracao": session.iteracao, | |
| "coluna_data_mercado": session.coluna_data_mercado, | |
| "periodo_dados_mercado": { | |
| "coluna_data": session.coluna_data_mercado, | |
| "data_inicial": session.periodo_dados_mercado_inicio, | |
| "data_final": session.periodo_dados_mercado_fim, | |
| }, | |
| } | |