Spaces:
Sleeping
Sleeping
| # app.py | |
| import gradio as gr | |
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| import seaborn as sns | |
| from scipy import stats | |
| import io | |
| # --- Funções do seu script original --- | |
| # (com uma pequena modificação na função de gráfico) | |
| def simular_sprints(backlog: int, lam: float, n_sim: int, rng) -> np.ndarray: | |
| """Devolve o n de sprints para zerar backlog em cada simulação.""" | |
| # Estima um número máximo de sprints razoável para evitar loops infinitos | |
| # Aumenta a folga para casos de lambda muito baixo | |
| if lam > 0: | |
| max_sprints = int(np.ceil(backlog / (lam * 0.2))) + 20 | |
| else: | |
| max_sprints = 200 # Um fallback caso lambda seja zero | |
| sprints_simuladas = rng.poisson(lam, size=(n_sim, max_sprints)) | |
| sprints_acumuladas = sprints_simuladas.cumsum(axis=1) | |
| # +1 porque o índice é 0-based e sprint é 1-based | |
| sprints_para_concluir = np.argmax(sprints_acumuladas >= backlog, axis=1) + 1 | |
| # Se alguma simulação não atingir o backlog, np.argmax retorna 0. | |
| # Corrigimos isso para indicar que não foi concluído no tempo simulado. | |
| nao_concluido = sprints_acumuladas[:, -1] < backlog | |
| sprints_para_concluir[nao_concluido] = max_sprints # Marcamos como o máximo | |
| return sprints_para_concluir | |
| def resumo_percentis(arr: np.ndarray, pcts: tuple = (50, 85, 95)) -> dict[str, int]: | |
| """Calcula os percentis dos resultados da simulação.""" | |
| return {f"{p}%": int(np.ceil(np.percentile(arr, p))) for p in pcts} | |
| def criar_grafico(arr: np.ndarray, nome_orgao: str, percentis: dict) -> plt.Figure: | |
| """Cria e retorna a figura do histograma.""" | |
| sns.set_theme(style="whitegrid") | |
| fig, ax = plt.subplots() # Cria figura e eixos | |
| sns.histplot(arr, bins="auto", stat="probability", ax=ax, color="#4C72B0", edgecolor=".2") | |
| for p_str, v in percentis.items(): | |
| ax.axvline(v, ls='--', lw=1.2, label=f"{p_str} de confiança: {v} sprints") | |
| ax.set(title=f"Distribuição de Sprints para Concluir o Backlog de {nome_orgao}", | |
| xlabel="Número de Sprints", ylabel="Probabilidade") | |
| ax.legend() | |
| plt.tight_layout() | |
| # IMPORTANTE: Em vez de plt.show(), retornamos a figura | |
| return fig | |
| # --- Função principal que integra tudo com a interface --- | |
| def rodar_simulacao(nome_orgao, backlog_str, historico_str, n_sim_str): | |
| """ | |
| Função chamada pela interface Gradio. Recebe os inputs, roda a simulação | |
| e retorna os outputs formatados. | |
| """ | |
| try: | |
| # 1. Validar e converter inputs | |
| backlog = int(backlog_str) | |
| n_sim = int(n_sim_str) | |
| historico_numeros = [float(x.strip()) for x in historico_str.split(',')] | |
| historico = np.array(historico_numeros) | |
| if backlog <= 0 or n_sim <= 0: | |
| raise ValueError("Backlog e Nº de Simulações devem ser positivos.") | |
| if historico.size == 0 or np.mean(historico) <= 0: | |
| raise ValueError("O histórico de produtividade deve ser válido e a média maior que zero.") | |
| # 2. Executar a simulação | |
| RNG = np.random.default_rng(42) # Para reprodutibilidade | |
| lam = historico.mean() | |
| sims = simular_sprints(backlog, lam, n_sim, RNG) | |
| # 3. Gerar resumo e gráfico | |
| percentis = resumo_percentis(sims) | |
| texto_percentis = f"Resultados para {nome_orgao} (Lambda = {lam:.2f}):\n" | |
| for p, v in percentis.items(): | |
| texto_percentis += f"- {p} de probabilidade de concluir em {v} sprints ou menos.\n" | |
| figura_plot = criar_grafico(sims, nome_orgao, percentis) | |
| # 4. Análise de Sensibilidade | |
| texto_sensibilidade = "--- Análise de Sensibilidade (Prazo para 85% de confiança) ---\n" | |
| for delta in [+0.10, -0.10]: | |
| lam_alt = lam * (1 + delta) | |
| sims_alt = simular_sprints(backlog, lam_alt, n_sim, RNG) | |
| p85 = int(np.ceil(np.percentile(sims_alt, 85))) | |
| texto_sensibilidade += f"Com variação de {delta:+.0%}, o prazo vai para {p85} sprints (λ={lam_alt:.2f}).\n" | |
| return texto_percentis, figura_plot, texto_sensibilidade | |
| except Exception as e: | |
| # Retorna uma mensagem de erro amigável se algo der errado | |
| error_message = f"Ocorreu um erro: {e}. Por favor, verifique os valores de entrada." | |
| # Cria um plot vazio para não quebrar a interface | |
| fig_vazia, ax = plt.subplots() | |
| ax.text(0.5, 0.5, 'Erro na Geração do Gráfico', ha='center', va='center') | |
| return error_message, fig_vazia, "" | |
| # --- Definição da Interface Gradio --- | |
| with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# 🎲 Simulador de Monte Carlo para Previsão de Projetos Ágeis") | |
| gr.Markdown( | |
| "Esta é uma demonstração interativa do modelo descrito no meu trabalho da matéria ANÁLISE ESTATÍSTICA DE DADOS E INFORMAÇÕES, no mestrado em computação aplicada. " | |
| ) | |
| gr.Markdown( | |
| "O funcionamento é relativamente simples, você insere o deparamento, a quantidade de Produtos de Itens de Backlog(PBI) e a quantidade de tarefas por sprints." | |
| "Feito isso, basta pedir para que o algoritmo preveja(Rodar Simulação) o tempo que será gasto para a conclusão do projeto." | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 1. Dados de Entrada") | |
| orgao = gr.Textbox(label="Nome do Projeto ou Equipe", value="PGCONT") | |
| backlog_input = gr.Textbox(label="Tamanho do Backlog (nº de tarefas)", value="176") | |
| historico_input = gr.Textbox( | |
| label="Produtividade Histórica (tarefas por sprint, separadas por vírgula)", | |
| value="7, 11, 9, 5, 13" | |
| ) | |
| nsim_input = gr.Textbox(label="Número de Simulações", value="10000") | |
| btn = gr.Button("Rodar Simulação", variant="primary") | |
| with gr.Column(scale=2): | |
| gr.Markdown("### 2. Resultados da Previsão") | |
| texto_output = gr.Textbox(label="Percentis de Conclusão", lines=5) | |
| plot_output = gr.Plot(label="Distribuição dos Prazos (em Sprints)") | |
| sensibilidade_output = gr.Textbox(label="Análise de Sensibilidade", lines=4) | |
| btn.click( | |
| fn=rodar_simulacao, | |
| inputs=[orgao, backlog_input, historico_input, nsim_input], | |
| outputs=[texto_output, plot_output, sensibilidade_output] | |
| ) | |
| gr.Examples( | |
| [["PGFAZ", "13", "3, 3, 5, 1, 2", "10000"]], | |
| inputs=[orgao, backlog_input, historico_input, nsim_input] | |
| ) | |
| demo.launch() | |