File size: 5,407 Bytes
26a163e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import gradio as gr
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# Função de simulação, ajustada para Gradio
def run_monte_carlo_simulation(tasks_list_of_tuples, n_simulations):
    task_names_original = [
        '1. Requisitos', '2. Arquitetura', '3. Back-end', 
        '4. Front-end', '5. Testes', '6. Implantação'
    ]
    tasks = [{'tarefa': name, 'a': a, 'm': m, 'b': b} for name, (a, m, b) in zip(task_names_original, tasks_list_of_tuples)]
    task_names = [task['tarefa'] for task in tasks]
    n_simulations = int(n_simulations)
    simulation_results = {name: np.zeros(n_simulations) for name in task_names}

    for i in range(n_simulations):
        for task in tasks:
            a, m, b = task['a'], task['m'], task['b']
            if not (a > 0 and a <= m and m <= b):
                raise gr.Error(f"Parâmetros inválidos na tarefa: {task['tarefa']}. Garanta que Otimista <= Provável <= Pessimista.")
            mu = (a + 4 * m + b) / 6
            if b - a == 0:
                task_duration = a
            else:
                gamma = 4.0
                alpha = 1 + gamma * (mu - a) / (b - a)
                beta = 1 + gamma * (b - mu) / (b - a)
                sample = np.random.beta(alpha, beta)
                task_duration = a + sample * (b - a)
            simulation_results[task['tarefa']][i] = task_duration
    
    results_df = pd.DataFrame(simulation_results)
    results_df['Duração Total'] = results_df.sum(axis=1)
    return results_df

# Função principal da interface
def generate_analysis(*args):
    n_simulations = args[-1]
    task_values = args[:-1]
    tasks_list_of_tuples = [tuple(task_values[i:i+3]) for i in range(0, len(task_values), 3)]
    
    results_df = run_monte_carlo_simulation(tasks_list_of_tuples, n_simulations)
    durations = results_df['Duração Total']
    
    summary_df = pd.DataFrame({
        'Métrica': ['Prazo Realista (50%)', 'Prazo de Segurança (95%)', 'Nível de Incerteza'],
        'Valor (dias)': [f"{np.median(durations):.1f}", f"{np.percentile(durations, 95):.1f}", f"{np.std(durations):.1f}"]
    }).set_index('Métrica')
    
    fig_hist, ax_hist = plt.subplots(figsize=(10, 6))
    sns.histplot(durations, kde=True, bins=50, ax=ax_hist, color="skyblue")
    ax_hist.axvline(np.median(durations), color='green', linestyle='-', label=f'Prazo Realista: {np.median(durations):.1f} dias')
    ax_hist.axvline(np.percentile(durations, 95), color='purple', linestyle=':', lw=2, label=f'Prazo de Segurança: {np.percentile(durations, 95):.1f} dias')
    ax_hist.set_title('Distribuição de Resultados', fontsize=16)
    ax_hist.set_xlabel('Duração Total (dias)')
    ax_hist.set_ylabel('Frequência')
    ax_hist.legend()
    
    task_names_original = ['1. Requisitos', '2. Arquitetura', '3. Back-end', '4. Front-end', '5. Testes', '6. Implantação']
    correlations = results_df.corr(numeric_only=True)['Duração Total'].drop('Duração Total')
    correlations.index = task_names_original
    sorted_correlations = correlations.abs().sort_values(ascending=False)
    fig_tornado, ax_tornado = plt.subplots(figsize=(10, 6))
    sns.barplot(x=sorted_correlations.values, y=sorted_correlations.index, orient='h', ax=ax_tornado, palette='viridis')
    ax_tornado.set_title('Análise de Sensibilidade', fontsize=16)
    ax_tornado.set_xlabel('Força de Impacto no Cronograma')
    
    return summary_df, fig_hist, fig_tornado

# Construção da Interface com Gradio
with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("# 🗺️ Simulador Interativo de Risco de Projetos")
    gr.Markdown("Altere as estimativas de cada tarefa para analisar diferentes cenários e seus impactos.")
    
    task_inputs = []
    task_names = ['1. Requisitos', '2. Arquitetura', '3. Back-end', '4. Front-end', '5. Testes', '6. Implantação']
    initial_values = [(5, 7, 15), (8, 10, 18), (20, 25, 45), (15, 20, 30), (10, 12, 20), (1, 2, 4)]

    with gr.Row():
        with gr.Column(scale=1):
            gr.Markdown("### ⚙️ Parâmetros de Entrada")
            for i, name in enumerate(task_names):
                with gr.Accordion(name, open=(i < 2)):
                    a = gr.Slider(1, 100, value=initial_values[i][0], step=1, label="Otimista (a)")
                    m = gr.Slider(1, 100, value=initial_values[i][1], step=1, label="Provável (m)")
                    b = gr.Slider(1, 100, value=initial_values[i][2], step=1, label="Pessimista (b)")
                    task_inputs.extend([a, m, b])
            
            n_sim = gr.Slider(1000, 100000, value=50000, step=1000, label="Nº de Simulações")
            task_inputs.append(n_sim)
            run_button = gr.Button("Analisar Cenário", variant="primary")
            
        with gr.Column(scale=2):
            gr.Markdown("### 🔍 Resultados da Análise")
            summary_output = gr.DataFrame(headers=["Métrica", "Valor (dias)"], row_count=3, col_count=2)
            with gr.Tabs():
                with gr.TabItem("Distribuição de Resultados"):
                    hist_output = gr.Plot()
                with gr.TabItem("Análise de Sensibilidade"):
                    tornado_output = gr.Plot()

    run_button.click(
        fn=generate_analysis, 
        inputs=task_inputs, 
        outputs=[summary_output, hist_output, tornado_output]
    )

demo.launch()