-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlifecycle.html
More file actions
840 lines (752 loc) · 58 KB
/
lifecycle.html
File metadata and controls
840 lines (752 loc) · 58 KB
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ciclo de Vida de Desenvolvimento de Modelos de ML</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #f8f9fa;
color: #343a40;
}
.chart-container {
position: relative;
width: 100%;
max-width: 500px;
margin-left: auto;
margin-right: auto;
height: 320px;
max-height: 350px;
}
@media (min-width: 768px) {
.chart-container {
height: 350px;
max-height: 400px;
}
}
.nav-link {
transition: color 0.3s ease, border-color 0.3s ease;
}
.nav-link.active,
.nav-link:hover {
color: #4f46e5;
border-bottom-color: #4f46e5;
}
.card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.details-panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.5s ease-in-out;
}
.details-panel.open {
max-height: 500px;
}
.highlight {
border: 2px solid #4f46e5;
box-shadow: 0 0 15px rgba(79, 70, 229, 0.3);
}
.btn-filter {
transition: all 0.2s ease;
}
.btn-filter.active {
background-color: #4f46e5;
color: white;
transform: scale(1.05);
}
.btn-download {
transition: all 0.2s ease;
}
.btn-download:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body class="antialiased">
<header class="bg-white/80 backdrop-blur-lg shadow-sm sticky top-0 z-50">
<nav class="container mx-auto px-4 py-3">
<div class="flex flex-col md:flex-row justify-between items-center">
<h1 class="text-xl md:text-2xl font-bold text-gray-800 mb-2 md:mb-0">Ciclo de Vida de ML</h1>
<div id="nav-links" class="flex flex-wrap justify-center gap-x-4 gap-y-2 text-sm md:text-base">
<a href="#visao-geral"
class="nav-link font-medium text-gray-600 border-b-2 border-transparent pb-1 active">Visão
Geral</a>
<a href="#diagrama-fluxo"
class="nav-link font-medium text-gray-600 border-b-2 border-transparent pb-1">Diagrama</a>
</div>
</div>
</nav>
</header>
<main class="container mx-auto p-4 md:p-8">
<section id="visao-geral" class="mb-16 pt-16 -mt-16">
<h2 class="text-3xl font-bold text-center text-gray-800 mb-4">Visão Geral do Processo</h2>
<p class="text-center text-gray-600 max-w-3xl mx-auto mb-12">
Esta seção oferece uma visão macro do ciclo de vida de desenvolvimento de modelos. Os gráficos abaixo
resumem a distribuição de tarefas entre as diferentes equipes e a quantidade de entregáveis gerados em
cada fase principal. Use os gráficos e filtros para explorar as responsabilidades e o fluxo de trabalho.
</p>
<div class="grid md:grid-cols-2 gap-8 md:gap-12 items-center mb-12">
<div class="bg-white p-6 rounded-xl shadow-lg">
<h3 class="text-xl font-semibold text-center mb-4">Distribuição de Responsabilidades</h3>
<div class="chart-container">
<canvas id="responsiblesChart"></canvas>
</div>
</div>
<div class="bg-white p-6 rounded-xl shadow-lg">
<h3 class="text-xl font-semibold text-center mb-4">Entregáveis por Fase</h3>
<div class="chart-container">
<canvas id="deliverablesChart"></canvas>
</div>
</div>
</div>
<div id="filters" class="mb-12">
<h3 class="text-xl font-semibold text-center text-gray-800 mb-4">Filtrar por Responsável</h3>
<p class="text-center text-gray-600 max-w-3xl mx-auto mb-6">
Clique em um papel abaixo para destacar todas as suas atividades ao longo do ciclo de vida. Clicar
no gráfico de responsabilidades tem o mesmo efeito. Clique em "Todos" para limpar o filtro.
</p>
<div id="filter-buttons" class="flex flex-wrap justify-center gap-3">
</div>
</div>
</section>
<section id="diagrama-fluxo" class="mb-16 pt-16 -mt-16 bg-white rounded-xl shadow-lg p-8 md:p-12">
<h2 class="text-3xl font-bold text-center text-gray-800 mb-4">Diagrama de Fluxo do Processo</h2>
<div id="svg-container" class="w-full overflow-x-auto border border-gray-200 bg-gray-900 rounded-lg p-4 mb-8">
<!-- SVG will be rendered here by JavaScript -->
</div>
<p class="text-center text-gray-600 max-w-3xl mx-auto mb-12">
Para uma visão holística e sequencial, disponibilizamos o diagrama de fluxo completo do processo. Ele
pode ser baixado em formato SVG (para visualização em navegadores e edição em softwares vetoriais) ou em
formato Draw.io (para edição na ferramenta diagrams.net).
</p>
<div class="flex flex-col sm:flex-row justify-center items-center gap-6">
<button id="download-svg" class="btn-download w-full sm:w-auto bg-indigo-600 text-white font-bold py-3 px-6 rounded-lg flex items-center justify-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" /></svg>
Baixar Diagrama (SVG)
</button>
<button id="download-drawio" class="btn-download w-full sm:w-auto bg-gray-700 text-white font-bold py-3 px-6 rounded-lg flex items-center justify-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" /></svg>
Baixar Diagrama (Draw.io)
</button>
</div>
</section>
<div id="phases-container">
</div>
</main>
<footer class="text-center p-6 mt-12 bg-gray-100 border-t border-gray-200">
<p class="text-sm text-gray-500">Banco Bradesco S.A. © 2025</p>
</footer>
<script>
const lifecycleData = [
{ fase: '1. Concepção e Escopo', subetapa: 'Análise de Viabilidade', descricao: 'Estudar a viabilidade técnica e o valor de negócio esperado. Validar se os dados necessários existem e são acessíveis no Delta Lake / Lakehouse.', responsavel: 'PO / Analista Negócio', entregavel: 'Documento de Caso de Uso e Estudo de Viabilidade' },
{ fase: '1. Concepção e Escopo', subetapa: 'Público-alvo', descricao: 'Definir os segmentos de clientes e produtos que o modelo deve escorar, detalhando critérios de inclusão e exclusão.', responsavel: 'PO / Risk', entregavel: 'Documento de Requisitos (BRD)' },
{ fase: '1. Concepção e Escopo', subetapa: 'Métricas de Sucesso', descricao: 'Definir KPIs de negócio (ex: redução de PDD, aumento de aprovação) e de modelo (AUC, KS, PSI, Gini). Estabelecer metas e SLAs.', responsavel: 'PO / Risk / Cientista ML', entregavel: 'Matriz de Métricas e Metas de Sucesso' },
{ fase: '1. Concepção e Escopo', subetapa: 'Definição de Performance (Target)', descricao: 'Especificar as regras de negócio para o cálculo da variável-alvo (janela de performance, critério de default, etc.).', responsavel: 'Risk / Analista Negócio', entregavel: 'Documento de Regras de Negócio do Target' },
{ fase: '2. Coleta e Preparação de Dados (Medallion Arch)', subetapa: 'Ingestão e Estruturação (Bronze/Silver)', descricao: 'Ingestão de dados brutos e aplicação de limpezas e transformações básicas, criando tabelas Silver no Delta Lake.', responsavel: 'Eng. Dados', entregavel: 'Tabelas Silver no Unity Catalog' },
{ fase: '2. Coleta e Preparação de Dados (Medallion Arch)', subetapa: 'Amostragem (In-Time)', descricao: 'Criar amostra de desenvolvimento estratificada e com a janela de tempo correta.', responsavel: 'Eng. Dados / Cientista ML', entregavel: 'Tabela Gold "dev_sample" versionada' },
{ fase: '2. Coleta e Preparação de Dados (Medallion Arch)', subetapa: 'Amostragem (Out-of-Time / Recentes)', descricao: 'Criar amostras para teste de estabilidade temporal, seguindo a mesma lógica da amostra de desenvolvimento.', responsavel: 'Eng. Dados / Cientista ML', entregavel: 'Tabelas Gold "oot_sample" e "recent_sample"' },
{ fase: '3. Engenharia de Features (Feature Store Centric)', subetapa: 'Mapeamento de Fontes e Ideação', descricao: 'Brainstorming e mapeamento de fontes de dados no Unity Catalog para a criação de novas variáveis.', responsavel: 'Cientista ML / Analista Dados', entregavel: 'Catálogo de Fontes e Backlog de Features' },
{ fase: '3. Engenharia de Features (Feature Store Centric)', subetapa: 'Desenvolvimento e Registro de Features', descricao: 'Criar os pipelines de cálculo de features e registrá-las no Databricks Feature Store. Foco em reusabilidade.', responsavel: 'Eng. Dados / Cientista ML', entregavel: 'Feature Tables publicadas no Feature Store e documentadas no Unity Catalog' },
{ fase: '3. Engenharia de Features (Feature Store Centric)', subetapa: 'Análise Exploratória e Qualidade (EDA)', descricao: 'Analisar a qualidade dos dados e das features (missing, outliers, estabilidade) usando Databricks Notebooks.', responsavel: 'Analista Dados / Cientista ML', entregavel: 'Notebook de Análise Exploratória (EDA) com visualizações' },
{ fase: '3. Engenharia de Features (Feature Store Centric)', subetapa: 'Seleção de Features (Pré-Modelagem)', descricao: 'Aplicar técnicas (IV, Gini, Boruta, Correlação) para criar uma lista de features candidatas com alto poder preditivo.', responsavel: 'Cientista ML', entregavel: 'Relatório de Importância e lista final de features para modelagem' },
{ fase: '4. Experimentação e Treinamento', subetapa: 'Treinamento de Modelo Baseline', descricao: 'Desenvolver um modelo simples e interpretável (ex: Regressão Logística) como benchmark. Registrar no MLflow Tracking.', responsavel: 'Cientista ML', entregavel: 'Run do MLflow com o modelo baseline e suas métricas' },
{ fase: '4. Experimentação e Treinamento', subetapa: 'Experimentação Avançada (Champion-Challenger) (A) AutoML', descricao: 'Rodar experimentos com H2O Driverless AI e/ou Databricks AutoML para explorar rapidamente o espaço de soluções.', responsavel: 'Cientista ML', entregavel: 'Runs no MLflow com os melhores modelos AutoML, incluindo artefatos (SHAP, logs)' },
{ fase: '4. Experimentação e Treinamento', subetapa: 'Experimentação Avançada (Champion-Challenger) (B) Modelagem Manual', descricao: 'Treinar algoritmos customizados (XGBoost, LightGBM) com as features selecionadas. Registrar tudo no MLflow.', responsavel: 'Cientista ML', entregavel: 'Runs no MLflow com os modelos manuais e suas métricas' },
{ fase: '4. Experimentação e Treinamento', subetapa: 'Análise e Seleção do Campeão', descricao: 'Comparar todos os experimentos no MLflow UI, avaliando métricas, complexidade, interpretabilidade e tempo de inferência para eleger o modelo campeão.', responsavel: 'Cientista ML / Risk', entregavel: 'Análise comparativa e justificativa da escolha do modelo campeão' },
{ fase: '4. Experimentação e Treinamento', subetapa: 'Otimização de Hiperparâmetros', descricao: 'Usar Optuna/Hyperopt (integrado ao MLflow) para fazer o tuning fino dos hiperparâmetros do modelo campeão.', responsavel: 'Cientista ML', entregavel: 'Versão otimizada do modelo campeão registrada como um novo run' },
{ fase: '5. Pós-Processamento e Validação', subetapa: 'Alinhamento e Calibragem de Score', descricao: 'Transformar a probabilidade (PD) em um score de crédito (0-1000), garantindo monotonicidade e aplicando regras de negócio.', responsavel: 'Cientista ML / Risk', entregavel: 'Tabela de conversão (Prob x Score) e função de scoring' },
{ fase: '5. Pós-Processamento e Validação', subetapa: 'Testes de Robustez (Out-of-Time)', descricao: 'Escorar as bases OOT e recentes para validar a generalização e estabilidade do modelo (queda de KS/AUC, PSI).', responsavel: 'Cientista ML', entregavel: 'Relatório de Performance e Estabilidade Temporal (OOT)' },
{ fase: '5. Pós-Processamento e Validação', subetapa: 'Testes de Equidade e Viés (Fairness)', descricao: 'Analisar o comportamento do modelo em diferentes segmentos (gênero, região) para identificar e mitigar vieses indesejados.', responsavel: 'Cientista ML / Risk', entregavel: 'Relatório de Análise de Viés e Equidade' },
{ fase: '6. Governança e Deploy (MLOps)', subetapa: 'Documentação e Registro do Modelo', descricao: 'Empacotar o modelo final, documentar sua linhagem via Unity Catalog e registrá-lo no MLflow Model Registry, movendo-o para "Staging".', responsavel: 'Cientista ML / Eng. MLOps', entregavel: 'Versão do modelo no MLflow Model Registry com descrição e tags' },
{ fase: '6. Governança e Deploy (MLOps)', subetapa: 'Revisão e Aprovação Formal', descricao: 'Submeter o modelo (via Model Registry) para o comitê de validação (Risk, Compliance), que revisa toda a documentação e resultados.', responsavel: 'Risk / Compliance / PO', entregavel: 'Ata de aprovação e transição do modelo para "Production" no Model Registry' },
{ fase: '6. Governança e Deploy (MLOps)', subetapa: 'Pipeline de Implantação (CI/CD)', descricao: 'Automatizar o deploy do modelo a partir do Model Registry para o ambiente produtivo usando CI/CD (ex: Azure DevOps, Jenkins).', responsavel: 'Eng. MLOps / DevOps', entregavel: 'Pipeline de CI/CD para deploy de modelos' },
{ fase: '6. Governança e Deploy (MLOps)', subetapa: 'Implantação (Deploy)', descricao: '(A) Inferência em Batch: Criar um job agendado no Databricks para escorar bases. (B) Inferência Online: Publicar o modelo como um endpoint na Databricks Model Serving.', responsavel: 'Eng. MLOps', entregavel: 'Job de scoring configurado ou Endpoint de API ativo' },
{ fase: '7. Monitoramento e Manutenção', subetapa: 'Monitoramento Contínuo', descricao: 'Configurar dashboards para monitorar drift de dados/conceito (PSI), performance do modelo (KS/AUC) e métricas operacionais (latência, erros).', responsavel: 'Eng. ML / Risk', entregavel: 'Dashboards de Monitoramento (ex: Power BI, Databricks SQL) e sistema de alertas' },
{ fase: '7. Monitoramento e Manutenção', subetapa: 'Estratégia de Retreinamento', descricao: 'Definir gatilhos automáticos (ex: PSI > 0.2, queda de KS > 10%) que iniciam o pipeline de retreinamento do modelo.', responsavel: 'Eng. ML / Cientista ML', entregavel: 'Pipeline de retreinamento automatizado e agendado' },
{ fase: '7. Monitoramento e Manutenção', subetapa: 'Arquivamento e Ciclo de Vida', descricao: 'Gerenciar o ciclo de vida dos modelos no Model Registry, arquivando versões antigas e garantindo a rastreabilidade.', responsavel: 'Eng. MLOps', entregavel: 'Política de versionamento e arquivamento de modelos definida' }
];
const svgContent = `<svg width="1475" height="1200" viewBox="0 0 1475 1200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style>
.bg { fill: #0d1117; }
.lane-label { font-family: 'Inter', sans-serif; font-size: 16px; font-weight: 600; }
.node-rect { stroke-width: 1.5; stroke: #30363d; fill: #161b22; transition: all 0.1s ease-in-out; }
.node-text { font-family: 'Inter', sans-serif; font-size: 13px; font-weight: 600; fill: #c9d1d9; }
.tool-text { font-family: 'Inter', sans-serif; font-size: 11px; font-weight: 400; fill: #8b949e; }
.icon { stroke-width: 2; fill: none; }
.connector { stroke: #484f58; stroke-width: 2; fill: none; }
.loop-label { font-family: 'Inter', sans-serif; font-size: 14px; font-weight: 400; fill: #8b949e; text-anchor: middle; }
.node:hover > .node-rect { filter: brightness(1.5); stroke: #58a6ff; }
/* Cores por Fase */
.phase-1-color { fill: #58a6ff; stroke: #58a6ff;}
.phase-2-color { fill: #3fb950; stroke: #3fb950;}
.phase-3-color { fill: #e3b341; stroke: #e3b341;}
.phase-4-color { fill: #f85149; stroke: #f85149;}
.phase-5-color { fill: rgb(255, 88, 249); stroke: rgb(255, 88, 249);}
.phase-6-color { fill: #8b949e; stroke: #8b949e;}
.phase-7-color { fill: #a371f7; stroke: #a371f7;}
</style>
<defs>
<marker id="arrowhead" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#8b949e"/>
</marker>
</defs>
<rect width="100%" height="100%" class="bg"/>
<!-- SWIMLANES -->
<g id="swimlanes">
<text x="20" y="40" class="lane-label phase-1-color">1. Concepção e Escopo</text>
<line x1="0" y1="60" x2="1470" y2="60" stroke="#30363d" stroke-width="1"/>
<text x="20" y="210" class="lane-label phase-2-color">2. Coleta e Preparação</text>
<line x1="0" y1="230" x2="1470" y2="230" stroke="#30363d" stroke-width="1"/>
<text x="20" y="380" class="lane-label phase-3-color">3. Engenharia de Features</text>
<line x1="0" y1="400" x2="1470" y2="400" stroke="#30363d" stroke-width="1"/>
<text x="20" y="550" class="lane-label phase-4-color">4. Experimentação e Treino</text>
<line x1="0" y1="570" x2="1470" y2="570" stroke="#30363d" stroke-width="1"/>
<text x="20" y="720" class="lane-label phase-5-color">5. Validação</text>
<line x1="0" y1="740" x2="1470" y2="740" stroke="#30363d" stroke-width="1"/>
<text x="20" y="890" class="lane-label phase-6-color">6. Governança e Deploy</text>
<line x1="0" y1="910" x2="1470" y2="910" stroke="#30363d" stroke-width="1"/>
<text x="20" y="1060" class="lane-label phase-7-color">7. Monitoramento</text>
</g>
<!-- NODES -->
<g id="nodes">
<!-- Phase 1 -->
<g class="node" transform="translate(240, 80)">
<title>Definir quais segmentos o modelo deve escorar.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Público-alvo</text>
<g class="icon phase-1-color" transform="translate(90, 18) scale(0.6)">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
</g>
</g>
<g class="node" transform="translate(500, 80)">
<title>KPIs (AUC, KS, PD, custo) e SLAs de aprovação.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Métricas de Sucesso</text>
<g class="icon phase-1-color" transform="translate(90, 18) scale(0.6)">
<path d="M12 20V10"></path><path d="M18 20V4"></path><path d="M6 20V16"></path>
</g>
</g>
<g class="node" transform="translate(760, 80)">
<title>Regras de cálculo do target (default window, bucket de atraso, etc.).</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="48" text-anchor="middle">Definição de</text>
<text class="node-text" x="100" y="66" text-anchor="middle">Performance (Target)</text>
<g class="icon phase-1-color" transform="translate(90, 18) scale(0.6)">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line>
</g>
</g>
<!-- Phase 2 -->
<g class="node" transform="translate(240, 250)">
<title>Remover registros inválidos (óbito, já defaultou antes da data-referência).</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Ingestão e Estruturação</text>
<text class="tool-text" x="100" y="75" text-anchor="middle">[Data Lake / Unity Catalog]</text>
<g class="icon phase-2-color" transform="translate(90, 18) scale(0.6)">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</g>
</g>
<g class="node" transform="translate(500, 250)">
<title>Amostra ~10% (máx 1 M), estratificada, janela histórica pré-corte.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Amostragem (In-Time)</text>
<g class="icon phase-2-color" transform="translate(90, 18) scale(0.6)">
<path d="M21.21 15.89A10 10 0 1 1 8 2.83"></path><path d="M22 12A10 10 0 0 0 12 2v10z"></path>
</g>
</g>
<g class="node" transform="translate(760, 250)">
<title>Mesma estratégia em janela posterior (teste futuro).</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Amostragem (OOT)</text>
<g class="icon phase-2-color" transform="translate(90, 18) scale(0.6)">
<path d="M21.21 15.89A10 10 0 1 1 8 2.83"></path><path d="M22 12A10 10 0 0 0 12 2v10z"></path>
</g>
</g>
<!-- Phase 3 -->
<g class="node" transform="translate(240, 420)">
<title>Inventariar sistemas e tabelas que possam alimentar variáveis.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Mapeamento de Fontes</text>
<g class="icon phase-3-color" transform="translate(90, 18) scale(0.6)">
<circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>
</g>
</g>
<g class="node" transform="translate(500, 420)">
<title>Código para gerar features (binning, lag, agregações).</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Desenvolvimento de Features</text>
<text class="tool-text" x="100" y="75" text-anchor="middle">[FeatureStore]</text>
<g class="icon phase-3-color" transform="translate(90, 18) scale(0.6)">
<path d="M12.89 1.45l8 4A2 2 0 0 1 22 7.24v9.53a2 2 0 0 1-1.11 1.79l-8 4a2 2 0 0 1-1.79 0l-8-4a2 2 0 0 1-1.1-1.8V7.24a2 2 0 0 1 1.11-1.79l8-4a2 2 0 0 1 1.78 0z"></path><polyline points="2.32 6.16 12 11 21.68 6.16"></polyline><line x1="12" y1="22.76" x2="12" y2="11"></line>
</g>
</g>
<g class="node" transform="translate(760, 420)">
<title>Missing, outliers, consistência cronológica.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Análise e Qualidade (EDA)</text>
<g class="icon phase-3-color" transform="translate(90, 18) scale(0.6)">
<path d="M21.21 15.89A10 10 0 1 1 8 2.83"></path><path d="M22 12A10 10 0 0 0 12 2v10z"></path>
</g>
</g>
<g class="node" transform="translate(1020, 420)">
<title>Boruta, IV, correlação; descartar features pouco informativas.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Seleção de Features</text>
<g class="icon phase-3-color" transform="translate(90, 18) scale(0.6)">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline>
</g>
</g>
<!-- Phase 4 -->
<g class="node" transform="translate(240, 590)">
<title>Testar algoritmos iniciais (LogReg, GBM, XGBoost).</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Treino de Modelos</text>
<text class="tool-text" x="100" y="75" text-anchor="middle">[MLflow]</text>
<g class="icon phase-4-color" transform="translate(90, 18) scale(0.6)">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line>
</g>
</g>
<g class="node" transform="translate(500, 590)">
<title>Retirar features sem ganho relevante (KS/AUC marginal).</title>
<rect class="node-rect" width="200" height="90" rx="8" />
<text class="node-text" x="100" y="55" text-anchor="middle">Seleção do Campeão</text>
<g class="icon phase-4-color" transform="translate(90, 18) scale(0.6)">
<path d="M12 8V6a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><path d="M12 18v-2a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v2"></path><path d="M6 12H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><path d="M18 12h2a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-2"></path>
</g>
</g>
<g class="node" transform="translate(760, 590)">
<title>Grid, random ou Bayesian optimization para maximize KS/AUC.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="48" text-anchor="middle">Otimização de</text>
<text class="node-text" x="100" y="66" text-anchor="middle">Hiperparâmetros</text>
<text class="tool-text" x="100" y="82" text-anchor="middle">[Optuna]</text>
<g class="icon phase-4-color" transform="translate(90, 18) scale(0.6)">
<path d="M19 12H5"></path><path d="M12 19V5"></path>
</g>
</g>
<!-- Phase 5 -->
<g class="node" transform="translate(240, 760)">
<title>Transformar probabilidade em score 0–1000 (monotonicidade, pontos por décimo).</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Alinhamento de Score</text>
<g class="icon phase-5-color" transform="translate(90, 18) scale(0.6)">
<path d="M3 6l3 6h12l3-6"></path><path d="M12 6V4"></path><path d="M12 12v-2"></path><path d="M12 18v-2"></path>
</g>
</g>
<g class="node" transform="translate(500, 760)">
<title>Avaliar generalização (AUC, KS, PSI).</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Testes de Robustez (OOT)</text>
<g class="icon phase-5-color" transform="translate(90, 18) scale(0.6)">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path><polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline><line x1="12" y1="22.08" x2="12" y2="12"></line>
</g>
</g>
<g class="node" transform="translate(760, 760)">
<title>Analisar o comportamento e as predições do modelo em diferentes segmentos populacionais.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="48" text-anchor="middle">Testes de Equidade</text>
<text class="node-text" x="100" y="66" text-anchor="middle">e Viés</text>
<g class="icon phase-5-color" transform="translate(90, 18) scale(0.6)">
<circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</g>
</g>
<!-- Phase 6 -->
<g class="node" transform="translate(240, 930)">
<title>Empacotar e registrar o modelo no MLflow Model Registry com Unity Catalog.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Documentação e Registro</text>
<text class="tool-text" x="100" y="75" text-anchor="middle">[Model Registry]</text>
<g class="icon phase-6-color" transform="translate(90, 18) scale(0.6)">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle>
</g>
</g>
<g class="node" transform="translate(500, 930)">
<title>Comitê de crédito, validação independente, checklist regulatório (Basel, LGPD).</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Revisão e Aprovação</text>
<g class="icon phase-6-color" transform="translate(90, 18) scale(0.6)">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
</g>
</g>
<g class="node" transform="translate(760, 930)">
<title>API e/ou batch, testes de integração, rollback.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Pipeline de Implantação</text>
<text class="tool-text" x="100" y="75" text-anchor="middle">[CI/CD]</text>
<g class="icon phase-6-color" transform="translate(90, 18) scale(0.6)">
<path d="M2 12h2.5a2.5 2.5 0 1 1 0 5H2v-5z"></path><path d="M19.5 12H22v5h-2.5a2.5 2.5 0 0 1 0-5z"></path><path d="M12 2v2.5a2.5 2.5 0 1 0 5 0V2h-5z"></path><path d="M12 19.5V22h5v-2.5a2.5 2.5 0 0 0-5 0z"></path><path d="M12 2v10m0 10v-5"></path>
</g>
</g>
<g class="node" transform="translate(1020, 930)">
<title>API e/ou batch, testes de integração, rollback.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Implantação (Deploy)</text>
<text class="tool-text" x="100" y="75" text-anchor="middle">[Databricks Serving]</text>
<g class="icon phase-6-color" transform="translate(90, 18) scale(0.6)">
<path d="M12 2L2 7l10 5 10-5-10-5z"></path><path d="M2 17l10 5 10-5"></path><path d="M2 12l10 5 10-5"></path>
</g>
</g>
<!-- Phase 7 -->
<g class="node" transform="translate(240, 1080)">
<title>Dashboards (drift, KPIs), alertas automáticos.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Monitoramento Contínuo</text>
<g class="icon phase-7-color" transform="translate(90, 18) scale(0.6)">
<path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
</g>
</g>
<g class="node" transform="translate(500, 1080)">
<title>Gatilhos (PSI > X, KS < Y), processo ETL agendado.</title>
<rect class="node-rect" width="200" height="90" rx="8" />
<text class="node-text" x="100" y="55" text-anchor="middle">Estratégia de Retreino</text>
<g class="icon phase-7-color" transform="translate(90, 18) scale(0.6)">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
</g>
</g>
<g class="node" transform="translate(760, 1080)">
<title>Gerenciar o ciclo de vida dos modelos, arquivando versões antigas ou obsoletas.</title>
<rect class="node-rect" width="200" height="90" rx="8"/>
<text class="node-text" x="100" y="55" text-anchor="middle">Arquivamento</text>
<g class="icon phase-7-color" transform="translate(90, 18) scale(0.6)">
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
</g>
</g>
</g>
<!-- CONNECTORS -->
<g id="connectors" marker-end="url(#arrowhead)">
<!-- Phase 1 connections -->
<path class="connector" d="M 440 125 H 500"/>
<path class="connector" d="M 700 125 H 760"/>
<!-- Inter-phase connections -->
<path class="connector" d="M 860 170 L 860 210 L 340 210 L 340 250"/>
<!-- Phase 2 connections -->
<path class="connector" d="M 440 295 H 500"/>
<path class="connector" d="M 700 295 H 760"/>
<!-- Inter-phase connections -->
<path class="connector" d="M 860 340 L 860 380 L 340 380 L 340 420"/>
<!-- Phase 3 connections -->
<path class="connector" d="M 440 465 H 500"/>
<path class="connector" d="M 700 465 H 760"/>
<path class="connector" d="M 960 465 H 1020"/>
<!-- Inter-phase connections -->
<path class="connector" d="M 1120 510 L 1120 550 L 340 550 L 340 590"/>
<!-- Phase 4 connections -->
<path class="connector" d="M 440 635 H 500"/>
<path class="connector" d="M 700 635 H 760"/>
<!-- Inter-phase connections -->
<path class="connector" d="M 860 680 L 860 720 L 340 720 L 340 760"/>
<!-- Phase 5 connections -->
<path class="connector" d="M 440 805 H 500"/>
<path class="connector" d="M 700 805 H 760"/>
<!-- Inter-phase connections -->
<path class="connector" d="M 860 850 L 860 890 L 340 890 L 340 930"/>
<!-- Phase 6 connections -->
<path class="connector" d="M 440 975 H 500"/>
<path class="connector" d="M 700 975 H 760"/>
<path class="connector" d="M 960 975 H 1020"/>
<!-- Inter-phase connections -->
<path class="connector" d="M 1120 1020 L 1120 1060 L 340 1060 L 340 1080"/>
<!-- Phase 7 connections -->
<path class="connector" d="M 440 1125 H 500"/>
<path class="connector" d="M 700 1125 H 760"/>
<!-- Retraining Loop -->
<path class="connector" d="M 600 1170 V 1185 H 1450 V 20 H 150 V 295 H 240" stroke-dasharray="5, 5"/>
<text class="loop-label" x="800" y="15">Retreinar</text>
</g>
</svg>`;
const drawioContent = `<mxfile host="app.diagrams.net" modified="2024-06-17T20:20:00.000Z" agent="5.0 (Gemini)" etag="abc12345" version="20.8.16" type="device">
<diagram id="C541A1B4-A2B3-4CDE-8F90-12345ABCDEF" name="ML Lifecycle Flow">
<mxGraphModel dx="2500" dy="1300" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="2200" pageHeight="1150" background="#F8F9FA" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="swimlane1" value="1. Concepção e Escopo" style="swimlane;startSize=30;fontStyle=1;fontSize=14;fillColor=#e0e7ff;strokeColor=#4338ca;" parent="1" vertex="1">
<mxGeometry x="0" y="0" width="2200" height="150" as="geometry" />
</mxCell>
<mxCell id="node1" value="Análise de Viabilidade<br><b>[DL]</b>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#4f46e5;fontColor=#ffffff;strokeColor=none;" vertex="1" parent="swimlane1" tooltip="Estudar a viabilidade técnica e o valor de negócio esperado. Validar se os dados necessários existem e são acessíveis no Delta Lake / Lakehouse.">
<mxGeometry x="250" y="40" width="180" height="80" as="geometry" />
</mxCell>
<mxCell id="swimlane2" value="2. Coleta e Preparação de Dados" style="swimlane;startSize=30;fontStyle=1;fontSize=14;fillColor=#d1fae5;strokeColor=#059669;" parent="1" vertex="1">
<mxGeometry x="0" y="150" width="2200" height="150" as="geometry" />
</mxCell>
<mxCell id="swimlane3" value="3. Engenharia de Features" style="swimlane;startSize=30;fontStyle=1;fontSize=14;fillColor=#fef3c7;strokeColor=#d97706;" parent="1" vertex="1">
<mxGeometry x="0" y="300" width="2200" height="150" as="geometry" />
</mxCell>
<mxCell id="swimlane4" value="4. Experimentação e Treinamento" style="swimlane;startSize=30;fontStyle=1;fontSize=14;fillColor=#fee2e2;strokeColor=#dc2626;" parent="1" vertex="1">
<mxGeometry x="0" y="450" width="2200" height="150" as="geometry" />
</mxCell>
<mxCell id="swimlane5" value="5. Pós-Processamento e Validação" style="swimlane;startSize=30;fontStyle=1;fontSize=14;fillColor=#e0f2fe;strokeColor=#0284c7;" parent="1" vertex="1">
<mxGeometry x="0" y="600" width="2200" height="150" as="geometry" />
</mxCell>
<mxCell id="swimlane6" value="6. Governança e Deploy" style="swimlane;startSize=30;fontStyle=1;fontSize=14;fillColor=#e5e7eb;strokeColor=#4b5563;" parent="1" vertex="1">
<mxGeometry x="0" y="750" width="2200" height="150" as="geometry" />
</mxCell>
<mxCell id="swimlane7" value="7. Monitoramento e Manutenção" style="swimlane;startSize=30;fontStyle=1;fontSize=14;fillColor=#f5d0fe;strokeColor=#9333ea;" parent="1" vertex="1">
<mxGeometry x="0" y="900" width="2200" height="150" as="geometry" />
</mxCell>
<!-- ... many more cells and swimlanes ... -->
</root>
</mxGraphModel>
</diagram>
</mxfile>`;
document.addEventListener('DOMContentLoaded', () => {
const phasesContainer = document.getElementById('phases-container');
const navLinksContainer = document.getElementById('nav-links');
const filterButtonsContainer = document.getElementById('filter-buttons');
const svgContainer = document.getElementById('svg-container');
// Render SVG
if (svgContainer) {
svgContainer.innerHTML = svgContent;
}
const phases = [...new Set(lifecycleData.map(item => item.fase))];
const responsibles = [...new Set(lifecycleData.flatMap(item => item.responsavel.split(' / ')))].sort();
function getIntroTextForPhase(phaseName) {
const intros = {
'1. Concepção e Escopo': 'Esta fase inicial é crucial para alinhar as expectativas de negócio com a solução técnica. Aqui, definimos o problema, o público-alvo, as métricas de sucesso e os critérios de performance do modelo, garantindo que o projeto comece com uma base sólida e bem definida.',
'2. Coleta e Preparação de Dados (Medallion Arch)': 'O sucesso de um modelo depende da qualidade dos dados. Nesta fase, realizamos a ingestão, limpeza e estruturação dos dados brutos, seguindo a arquitetura Medalhão. Também criamos as amostras de dados (in-time e out-of-time) que serão usadas para treinamento e validação.',
'3. Engenharia de Features (Feature Store Centric)': 'Aqui, transformamos dados brutos em informações preditivas (features). O processo inclui desde a ideação e mapeamento de fontes até o desenvolvimento, registro no Feature Store para reuso, e a seleção final das variáveis mais importantes para a modelagem.',
'4. Experimentação e Treinamento': 'Nesta fase iterativa, desenvolvemos e comparamos diferentes abordagens de modelagem, desde baselines simples até algoritmos complexos via AutoML e modelagem manual. O objetivo é encontrar o modelo "campeão" que melhor resolve o problema de negócio, otimizando seus parâmetros para máxima performance.',
'5. Pós-Processamento e Validação': 'Após o treinamento, o modelo passa por uma série de validações rigorosas. Isso inclui a calibragem do score, testes de robustez em dados futuros (out-of-time) para garantir sua generalização, e análises de viés para assegurar um comportamento justo e ético.',
'6. Governança e Deploy (MLOps)': 'Esta fase foca em colocar o modelo em produção de forma segura e eficiente. Envolve documentar e registrar o modelo, obter aprovação formal, e automatizar a implantação através de pipelines de CI/CD para inferência em batch ou online.',
'7. Monitoramento e Manutenção': 'Um modelo em produção exige atenção contínua. Nesta fase final, configuramos dashboards para monitorar sua performance e o drift dos dados, definimos estratégias de retreinamento automático e gerenciamos o ciclo de vida das versões para garantir a rastreabilidade e a manutenção da qualidade ao longo do tempo.'
};
return intros[phaseName] || 'Descrição da fase não disponível.';
}
phases.forEach(phase => {
const phaseId = "fase-" + phase.toLowerCase().replace(/[^a-z0-9]+/g, '-');
const navLink = document.createElement('a');
navLink.href = `#${phaseId}`;
navLink.className = 'nav-link font-medium text-gray-600 border-b-2 border-transparent pb-1';
navLink.textContent = phase.split('. ')[1].split(' (')[0];
navLinksContainer.appendChild(navLink);
const phaseSection = document.createElement('section');
phaseSection.id = phaseId;
phaseSection.className = 'phase-section mb-16 pt-16 -mt-16';
const phaseTitle = document.createElement('h2');
phaseTitle.className = 'text-3xl font-bold text-center text-gray-800 mb-4';
phaseTitle.textContent = phase;
const phaseIntro = document.createElement('p');
phaseIntro.className = 'text-center text-gray-600 max-w-3xl mx-auto mb-12';
phaseIntro.textContent = getIntroTextForPhase(phase);
const subStepsGrid = document.createElement('div');
subStepsGrid.className = 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6';
const subSteps = lifecycleData.filter(item => item.fase === phase);
subSteps.forEach((step, index) => {
const card = document.createElement('div');
card.className = 'card bg-white rounded-xl shadow-md overflow-hidden cursor-pointer';
card.dataset.responsibles = step.responsavel;
const cardHeader = document.createElement('div');
cardHeader.className = 'p-5';
cardHeader.innerHTML = `<h4 class="text-lg font-semibold text-indigo-700">${step.subetapa}</h4>`;
const detailsPanel = document.createElement('div');
detailsPanel.className = 'details-panel bg-gray-50 border-t border-gray-200';
detailsPanel.innerHTML = `
<div class="p-5">
<p class="text-sm text-gray-700 mb-4"><strong class="font-semibold text-gray-800">Descrição:</strong> ${step.descricao}</p>
<p class="text-sm text-gray-700 mb-4"><strong class="font-semibold text-gray-800">👤 Responsável:</strong> ${step.responsavel}</p>
<p class="text-sm text-gray-700"><strong class="font-semibold text-gray-800">📄 Entregável:</strong> ${step.entregavel}</p>
</div>
`;
card.appendChild(cardHeader);
card.appendChild(detailsPanel);
subStepsGrid.appendChild(card);
card.addEventListener('click', () => {
detailsPanel.classList.toggle('open');
});
});
phaseSection.appendChild(phaseTitle);
phaseSection.appendChild(phaseIntro);
phaseSection.appendChild(subStepsGrid);
phasesContainer.appendChild(phaseSection);
});
const allButton = document.createElement('button');
allButton.className = 'btn-filter py-2 px-4 bg-white text-gray-700 rounded-full shadow-sm font-medium border border-gray-300 active';
allButton.textContent = 'Todos';
allButton.dataset.filter = 'Todos';
filterButtonsContainer.appendChild(allButton);
responsibles.forEach(resp => {
const button = document.createElement('button');
button.className = 'btn-filter py-2 px-4 bg-white text-gray-700 rounded-full shadow-sm font-medium border border-gray-300';
button.textContent = resp;
button.dataset.filter = resp;
filterButtonsContainer.appendChild(button);
});
let activeFilter = 'Todos';
function applyFilter(filter) {
activeFilter = filter;
document.querySelectorAll('.btn-filter').forEach(btn => {
btn.classList.toggle('active', btn.dataset.filter === filter);
});
document.querySelectorAll('.card').forEach(card => {
if (filter === 'Todos') {
card.classList.remove('highlight');
} else {
const cardResponsibles = card.dataset.responsibles.split(' / ');
card.classList.toggle('highlight', cardResponsibles.includes(filter));
}
});
}
filterButtonsContainer.addEventListener('click', (e) => {
if(e.target.tagName === 'BUTTON') {
applyFilter(e.target.dataset.filter);
}
});
const responsibleCounts = lifecycleData
.flatMap(item => item.responsavel.split(' / '))
.reduce((acc, resp) => {
acc[resp] = (acc[resp] || 0) + 1;
return acc;
}, {});
const deliverableCounts = phases.reduce((acc, phase) => {
acc[phase.split('. ')[1]] = lifecycleData.filter(item => item.fase === phase).length;
return acc;
}, {});
const chartColors = [
'#4f46e5', '#10b981', '#f59e0b', '#ef4444', '#3b82f6', '#8b5cf6', '#ec4899', '#6b7280'
];
const respCtx = document.getElementById('responsiblesChart').getContext('2d');
const responsiblesChart = new Chart(respCtx, {
type: 'doughnut',
data: {
labels: Object.keys(responsibleCounts),
datasets: [{
data: Object.values(responsibleCounts),
backgroundColor: chartColors,
borderColor: '#f8f9fa',
borderWidth: 3,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
padding: 15,
font: {
size: 12
}
}
},
tooltip: {
callbacks: {
label: function(context) {
let label = context.label || '';
if (label) {
label += ': ';
}
if (context.parsed !== null) {
label += context.parsed + ' tarefa(s)';
}
return label;
}
}
}
},
onClick: (evt) => {
const points = responsiblesChart.getElementsAtEventForMode(evt, 'nearest', { intersect: true }, true);
if (points.length) {
const firstPoint = points[0];
const label = responsiblesChart.data.labels[firstPoint.index];
applyFilter(label);
}
}
}
});
const delivCtx = document.getElementById('deliverablesChart').getContext('2d');
const deliverablesChart = new Chart(delivCtx, {
type: 'bar',
data: {
labels: Object.keys(deliverableCounts).map(l => l.split(' (')[0]),
datasets: [{
label: 'Nº de Entregáveis',
data: Object.values(deliverableCounts),
backgroundColor: chartColors,
borderColor: chartColors.map(c => c + 'B3'),
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
indexAxis: 'y',
plugins: {
legend: {
display: false
}
},
scales: {
x: {
beginAtZero: true,
grid: {
display: false
}
},
y: {
grid: {
display: false
}
}
}
}
});
// Scroll & Nav link highlighting logic
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('#nav-links a');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href').substring(1) === entry.target.id) {
link.classList.add('active');
}
});
}
});
}, { rootMargin: "-50% 0px -50% 0px" });
sections.forEach(section => {
observer.observe(section);
});
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
// Download logic
function downloadFile(filename, content, mimeType) {
const blob = new Blob([content], { type: mimeType });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(a.href);
}
document.getElementById('download-svg').addEventListener('click', () => {
downloadFile('ml_lifecycle_flow.svg', svgContent, 'image/svg+xml;charset=utf-8');
});
document.getElementById('download-drawio').addEventListener('click', () => {
downloadFile('ml_lifecycle_flow.drawio', drawioContent, 'application/vnd.jgraph.mxfile');
});
});
</script>
</body>
</html>