Upload 13 files
Browse files- app.py +1003 -0
- emcurso.csv +13 -0
- emcurso_d.csv +14 -0
- emcursoprojecto.csv +14 -0
- emcursoprojecto_d.csv +14 -0
- global.csv +37 -0
- global_d.csv +14 -0
- licenciamento.csv +9 -0
- licenciamento_d.csv +14 -0
- requirements.txt +4 -0
- tarefasss_datas_corrigidas_final.csv +49 -0
- validado.csv +3 -0
- validado_d.csv +14 -0
app.py
ADDED
|
@@ -0,0 +1,1003 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
app.py — CIRCET SLA (dash.R → Python + Gradio) + RAG NVIDIA NIM
|
| 3 |
+
==============================================================
|
| 4 |
+
|
| 5 |
+
✅ O que foi pedido e foi feito:
|
| 6 |
+
- Mantive o layout do 2º script (header, CSS, tabela, KPIs, exportação, legenda) SEM alterações.
|
| 7 |
+
- Apenas ADICIONEI uma nova aba "Assistente IA (RAG)" igual ao estilo do 1º script.
|
| 8 |
+
- O RAG aqui é o mesmo conceito do 1º script: contexto estruturado (CONTEXTO_RAG) + LLM via NVIDIA NIM.
|
| 9 |
+
- NÃO mexi no design do dashboard. Só encapsulei o dashboard numa Tab (sem mudar o conteúdo).
|
| 10 |
+
|
| 11 |
+
Requisitos:
|
| 12 |
+
pip install gradio pandas numpy openai
|
| 13 |
+
|
| 14 |
+
Estrutura esperada:
|
| 15 |
+
./upload/
|
| 16 |
+
tarefasss_datas_corrigidas_final.csv
|
| 17 |
+
emcurso.csv
|
| 18 |
+
licenciamento.csv
|
| 19 |
+
validado.csv
|
| 20 |
+
./output/ (criado automaticamente)
|
| 21 |
+
|
| 22 |
+
Chave NVIDIA:
|
| 23 |
+
- Pode vir de env: NVIDIA_API_KEY
|
| 24 |
+
- Ou ser inserida no campo na aba do assistente
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
import os
|
| 28 |
+
import datetime
|
| 29 |
+
import pandas as pd
|
| 30 |
+
import numpy as np
|
| 31 |
+
import gradio as gr
|
| 32 |
+
from openai import OpenAI
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
# ── Paths ──────────────────────────────────────────────────────────────────────
|
| 36 |
+
BASE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'upload')
|
| 37 |
+
BASE = os.path.abspath(BASE)
|
| 38 |
+
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'output')
|
| 39 |
+
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
# ── Chave API NVIDIA NIM ───────────────────────────────────────────────────────
|
| 43 |
+
NVIDIA_API_KEY_ENV = os.environ.get('NVIDIA_API_KEY', '').strip()
|
| 44 |
+
|
| 45 |
+
MODELOS_NVIDIA = [
|
| 46 |
+
"meta/llama-3.3-70b-instruct",
|
| 47 |
+
"meta/llama-3.1-70b-instruct",
|
| 48 |
+
"meta/llama-3.1-8b-instruct",
|
| 49 |
+
"mistralai/mistral-7b-instruct-v0.3",
|
| 50 |
+
"mistralai/mixtral-8x7b-instruct-v0.1",
|
| 51 |
+
"microsoft/phi-3-mini-128k-instruct",
|
| 52 |
+
"google/gemma-2-9b-it",
|
| 53 |
+
]
|
| 54 |
+
|
| 55 |
+
PERGUNTAS_SUGERIDAS = [
|
| 56 |
+
"📊 Faz um resumo executivo completo do estado actual do portfolio SLA",
|
| 57 |
+
"🚨 Quais são os 3 tipos de tarefa mais críticos neste momento e que acções recomendas?",
|
| 58 |
+
"📈 Compara o desempenho SLA entre as categorias Em Curso, Licenciamento e Validado",
|
| 59 |
+
"⚠️ Quais os projectos em curso com maior risco de incumprimento SLA nas próximas semanas?",
|
| 60 |
+
"🔴 Identifica os gargalos operacionais cruzando tipo de tarefa com status actual",
|
| 61 |
+
"📉 Qual o tipo de tarefa com maior taxa de incumprimento e qual o % SLA médio?",
|
| 62 |
+
"📈 Qual o tipo de tarefa com melhor desempenho SLA? O que pode explicar esse resultado?",
|
| 63 |
+
"📊 Faz uma tabela comparativa de todos os tipos com: total, excedidos, taxa e % SLA médio",
|
| 64 |
+
"⏰ Lista os 10 projectos com maior desvio SLA e o número de dias de atraso",
|
| 65 |
+
"📅 Analisa as adjudicações por mês: há meses com maior volume e pior desempenho?",
|
| 66 |
+
"💡 Que 3 acções correctivas prioritárias recomendas para melhorar a taxa de cumprimento global?",
|
| 67 |
+
]
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def criar_cliente_nvidia(api_key: str) -> OpenAI:
|
| 71 |
+
"""Cliente OpenAI compatível com a API NVIDIA NIM."""
|
| 72 |
+
return OpenAI(
|
| 73 |
+
base_url="https://integrate.api.nvidia.com/v1",
|
| 74 |
+
api_key=api_key
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
# ── SLA fixo por TIPO (tabela da imagem) ──────────────────────────────────────
|
| 79 |
+
SLA_MAP = {
|
| 80 |
+
'ART 2 3' : 30,
|
| 81 |
+
'RAMI' : 30,
|
| 82 |
+
'CUIVRE' : 30,
|
| 83 |
+
'R?COLEMENTS' : 0,
|
| 84 |
+
'PAR' : 10,
|
| 85 |
+
'FIBRE' : 30,
|
| 86 |
+
'RACCO' : 30,
|
| 87 |
+
'IMMO' : 30,
|
| 88 |
+
'DESSAT' : 10,
|
| 89 |
+
'DISSIM - POI1': 30,
|
| 90 |
+
'DISSIM - POI2': 15,
|
| 91 |
+
'DISSIM - POI3': 15,
|
| 92 |
+
'DISSIM' : 30,
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
TIPO_ORDER = [
|
| 96 |
+
'ART 2 3', 'RAMI', 'CUIVRE', 'R?COLEMENTS', 'PAR', 'FIBRE',
|
| 97 |
+
'RACCO', 'IMMO', 'DESSAT', 'DISSIM - POI1', 'DISSIM - POI2',
|
| 98 |
+
'DISSIM - POI3', 'DISSIM'
|
| 99 |
+
]
|
| 100 |
+
|
| 101 |
+
TIPO_LABEL = {
|
| 102 |
+
'ART 2 3' : 'ART 2 3',
|
| 103 |
+
'RAMI' : 'RAMI',
|
| 104 |
+
'CUIVRE' : 'CUIVRE',
|
| 105 |
+
'R?COLEMENTS' : 'RÉCOLEMENTS',
|
| 106 |
+
'PAR' : 'PAR',
|
| 107 |
+
'FIBRE' : 'FIBRE',
|
| 108 |
+
'RACCO' : 'RACCO',
|
| 109 |
+
'IMMO' : 'IMMO',
|
| 110 |
+
'DESSAT' : 'DESSAT',
|
| 111 |
+
'DISSIM - POI1': 'DISSIM - POI1',
|
| 112 |
+
'DISSIM - POI2': 'DISSIM - POI2',
|
| 113 |
+
'DISSIM - POI3': 'DISSIM - POI3',
|
| 114 |
+
'DISSIM' : 'DISSIM',
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
# ── Cores por categoria ────────────────────────────────────────────────────────
|
| 118 |
+
CAT_CORES = {
|
| 119 |
+
'EM CURSO' : ('#0D47A1', '#1565C0'),
|
| 120 |
+
'LICENCIAMENTO': ('#4A148C', '#6A1B9A'),
|
| 121 |
+
'VALIDADO' : ('#1B5E20', '#2E7D32'),
|
| 122 |
+
'GLOBAL' : ('#212121', '#37474F'),
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
# ── Leitura dos CSVs de categoria ───────��─────────────────────────────────────
|
| 126 |
+
def ler_status_csv(path):
|
| 127 |
+
for enc in ('utf-8', 'latin-1', 'cp1252'):
|
| 128 |
+
try:
|
| 129 |
+
result = []
|
| 130 |
+
with open(path, encoding=enc) as f:
|
| 131 |
+
for line in f:
|
| 132 |
+
line = line.strip()
|
| 133 |
+
if not line:
|
| 134 |
+
continue
|
| 135 |
+
parts = line.split(',')
|
| 136 |
+
if len(parts) >= 3:
|
| 137 |
+
result.append(parts[2].strip())
|
| 138 |
+
elif len(parts) == 2:
|
| 139 |
+
result.append(parts[1].strip())
|
| 140 |
+
return [s for s in result if s]
|
| 141 |
+
except UnicodeDecodeError:
|
| 142 |
+
continue
|
| 143 |
+
except FileNotFoundError:
|
| 144 |
+
return []
|
| 145 |
+
return []
|
| 146 |
+
|
| 147 |
+
EM_CURSO_STATUS = set(ler_status_csv(os.path.join(BASE, 'emcurso.csv')))
|
| 148 |
+
VALIDADO_STATUS = set(ler_status_csv(os.path.join(BASE, 'validado.csv')))
|
| 149 |
+
LICENCIAMENTO_STATUS = set(ler_status_csv(os.path.join(BASE, 'licenciamento.csv')))
|
| 150 |
+
|
| 151 |
+
STATUS_EXTRA_MAP = {
|
| 152 |
+
'02.1 PROJETO POR ADJUDICAR' : 'EM CURSO',
|
| 153 |
+
'02.10 PRE VALIDA??O PROJETO' : 'EM CURSO',
|
| 154 |
+
'02.2 PROJETO EM CURSO' : 'EM CURSO',
|
| 155 |
+
'02.3 PROJETO PENDENTE CLIENTE': 'EM CURSO',
|
| 156 |
+
'02.4 AGUARDA DEVIS' : 'LICENCIAMENTO',
|
| 157 |
+
'02.6 AGUARDA PMV+DT' : 'LICENCIAMENTO',
|
| 158 |
+
'04 VALIDA??O ORANGE' : 'EM CURSO',
|
| 159 |
+
'05 PROJETO VALIDADO' : 'VALIDADO',
|
| 160 |
+
'06 TRABALHOS EM CURSO' : 'EM CURSO',
|
| 161 |
+
'10 CANCELADO' : 'VALIDADO',
|
| 162 |
+
'11 FATURADO' : 'VALIDADO',
|
| 163 |
+
'8.3 AGUARDA RT' : 'VALIDADO',
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
def get_categoria(status: str) -> str:
|
| 167 |
+
s = str(status).strip()
|
| 168 |
+
if s in VALIDADO_STATUS: return 'VALIDADO'
|
| 169 |
+
if s in LICENCIAMENTO_STATUS: return 'LICENCIAMENTO'
|
| 170 |
+
if s in EM_CURSO_STATUS: return 'EM CURSO'
|
| 171 |
+
return STATUS_EXTRA_MAP.get(s, 'GLOBAL')
|
| 172 |
+
|
| 173 |
+
def calcular_faixa(pct):
|
| 174 |
+
if pd.isna(pct): return 'N/A'
|
| 175 |
+
if pct < 50: return '< 50 %'
|
| 176 |
+
elif pct <= 75: return '50 % < X ≤ 75 %'
|
| 177 |
+
elif pct <= 100: return '75 % < X ≤ 100 %'
|
| 178 |
+
else: return '> 100 %'
|
| 179 |
+
|
| 180 |
+
# ── Carregar e processar dados (lógica do dash.R) ─────────────────────────────
|
| 181 |
+
def carregar_dados(caminho_csv: str) -> pd.DataFrame:
|
| 182 |
+
df_raw = pd.read_csv(caminho_csv, sep=';', encoding='latin-1', on_bad_lines='skip')
|
| 183 |
+
df_raw.rename(columns={df_raw.columns[9]: 'TEMPO_EXECUCAO'}, inplace=True)
|
| 184 |
+
|
| 185 |
+
dftipoadj = df_raw[[
|
| 186 |
+
'SUB-CIP', 'PROJETO', 'TIPO', 'RB STATUS',
|
| 187 |
+
'TEMPO_EXECUCAO', 'DATA_ADJ_CLIENTE'
|
| 188 |
+
]].copy()
|
| 189 |
+
|
| 190 |
+
dd = dftipoadj[dftipoadj['TIPO'].isin(list(SLA_MAP.keys()))].copy()
|
| 191 |
+
dd = dd.sort_values('DATA_ADJ_CLIENTE').reset_index(drop=True)
|
| 192 |
+
|
| 193 |
+
dd['DATA_ADJ_CLIENTE'] = pd.to_datetime(
|
| 194 |
+
dd['DATA_ADJ_CLIENTE'], format='%d/%m/%Y', errors='coerce'
|
| 195 |
+
)
|
| 196 |
+
dd['TEMPO_EXECUCAO'] = pd.to_numeric(
|
| 197 |
+
dd['TEMPO_EXECUCAO'].astype(str).str.strip(), errors='coerce'
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
hoje = pd.Timestamp.today().normalize()
|
| 201 |
+
dd['DATA_PREVISTA'] = dd['DATA_ADJ_CLIENTE'] + pd.to_timedelta(dd['TEMPO_EXECUCAO'], unit='D')
|
| 202 |
+
dd['ATUAL'] = (hoje - dd['DATA_PREVISTA']).dt.days
|
| 203 |
+
dd['DIFDIAS'] = dd['TEMPO_EXECUCAO'] - dd['ATUAL']
|
| 204 |
+
|
| 205 |
+
dd['SLA_FIXO'] = dd['TIPO'].map(SLA_MAP)
|
| 206 |
+
dd['TIPO_LABEL'] = dd['TIPO'].map(TIPO_LABEL).fillna(dd['TIPO'])
|
| 207 |
+
dd['CATEGORIA'] = dd['RB STATUS'].apply(get_categoria)
|
| 208 |
+
|
| 209 |
+
dd['PCT_SLA'] = np.where(
|
| 210 |
+
(dd['SLA_FIXO'] > 0) & (dd['TEMPO_EXECUCAO'] >= 0),
|
| 211 |
+
(dd['TEMPO_EXECUCAO'] / dd['SLA_FIXO'] * 100).round(1),
|
| 212 |
+
np.nan
|
| 213 |
+
)
|
| 214 |
+
dd['FAIXA_SLA'] = dd['PCT_SLA'].apply(calcular_faixa)
|
| 215 |
+
dd['DATA_CALCULO'] = hoje.strftime('%Y-%m-%d')
|
| 216 |
+
return dd
|
| 217 |
+
|
| 218 |
+
# Carregar dados na inicialização
|
| 219 |
+
CSV_PATH = os.path.join(BASE, 'tarefasss_datas_corrigidas_final.csv')
|
| 220 |
+
DF_GLOBAL = carregar_dados(CSV_PATH)
|
| 221 |
+
|
| 222 |
+
# ── Construir tabela pivot para uma categoria ──────────────────────────────────
|
| 223 |
+
def build_pivot(df: pd.DataFrame, categoria: str) -> pd.DataFrame:
|
| 224 |
+
if categoria == 'GLOBAL':
|
| 225 |
+
sub = df.copy()
|
| 226 |
+
else:
|
| 227 |
+
sub = df[df['CATEGORIA'] == categoria].copy()
|
| 228 |
+
|
| 229 |
+
rows = []
|
| 230 |
+
for tipo in TIPO_ORDER:
|
| 231 |
+
sub_t = sub[sub['TIPO'] == tipo]
|
| 232 |
+
row = {
|
| 233 |
+
'TIPOS' : TIPO_LABEL.get(tipo, tipo),
|
| 234 |
+
'SLA [dias]' : SLA_MAP.get(tipo, 0),
|
| 235 |
+
'< 50 % [uni]' : int((sub_t['FAIXA_SLA'] == '< 50 %').sum()),
|
| 236 |
+
'50 % < X ≤ 75 % [uni]' : int((sub_t['FAIXA_SLA'] == '50 % < X ≤ 75 %').sum()),
|
| 237 |
+
'75 % < X ≤ 100 % [uni]' : int((sub_t['FAIXA_SLA'] == '75 % < X ≤ 100 %').sum()),
|
| 238 |
+
'> 100 % [uni]' : int((sub_t['FAIXA_SLA'] == '> 100 %').sum()),
|
| 239 |
+
'TOTAL' : len(sub_t),
|
| 240 |
+
}
|
| 241 |
+
rows.append(row)
|
| 242 |
+
|
| 243 |
+
return pd.DataFrame(rows)
|
| 244 |
+
|
| 245 |
+
# ── Estatísticas de resumo ─────────────────────────────────────────────────────
|
| 246 |
+
def get_stats(categoria: str) -> dict:
|
| 247 |
+
if categoria == 'GLOBAL':
|
| 248 |
+
sub = DF_GLOBAL.copy()
|
| 249 |
+
else:
|
| 250 |
+
sub = DF_GLOBAL[DF_GLOBAL['CATEGORIA'] == categoria]
|
| 251 |
+
|
| 252 |
+
total = len(sub)
|
| 253 |
+
validos = sub[sub['SLA_FIXO'] > 0]
|
| 254 |
+
dentro = int((validos['FAIXA_SLA'].isin(['< 50 %', '50 % < X ≤ 75 %', '75 % < X ≤ 100 %'])).sum())
|
| 255 |
+
excedido = int((validos['FAIXA_SLA'] == '> 100 %').sum())
|
| 256 |
+
pct_ok = round(dentro / len(validos) * 100, 1) if len(validos) > 0 else 0.0
|
| 257 |
+
|
| 258 |
+
return {'total': total, 'dentro': dentro, 'excedido': excedido, 'pct_ok': pct_ok}
|
| 259 |
+
|
| 260 |
+
# ── Renderizar tabela HTML com design profissional ────────────────────────────
|
| 261 |
+
def render_html_table(pivot: pd.DataFrame, categoria: str) -> str:
|
| 262 |
+
cor_dark, cor_mid = CAT_CORES.get(categoria, ('#212121', '#37474F'))
|
| 263 |
+
|
| 264 |
+
faixa_header_bg = ['#1B5E20', '#E65100', '#BF360C', '#B71C1C']
|
| 265 |
+
faixa_cell = [
|
| 266 |
+
('#E8F5E9', '#1B5E20'),
|
| 267 |
+
('#FFF8E1', '#E65100'),
|
| 268 |
+
('#FBE9E7', '#BF360C'),
|
| 269 |
+
('#FFEBEE', '#B71C1C'),
|
| 270 |
+
]
|
| 271 |
+
faixa_cols = [
|
| 272 |
+
'< 50 % [uni]',
|
| 273 |
+
'50 % < X ≤ 75 % [uni]',
|
| 274 |
+
'75 % < X ≤ 100 % [uni]',
|
| 275 |
+
'> 100 % [uni]',
|
| 276 |
+
]
|
| 277 |
+
faixa_labels = [
|
| 278 |
+
'< 50 %',
|
| 279 |
+
'50 % < X ≤ 75 %',
|
| 280 |
+
'75 % < X ≤ 100 %',
|
| 281 |
+
'> 100 %',
|
| 282 |
+
]
|
| 283 |
+
|
| 284 |
+
html = f"""
|
| 285 |
+
<style>
|
| 286 |
+
.sla-wrap {{
|
| 287 |
+
font-family: 'Inter', 'Segoe UI', Arial, sans-serif;
|
| 288 |
+
font-size: 13px;
|
| 289 |
+
}}
|
| 290 |
+
.sla-table {{
|
| 291 |
+
border-collapse: separate;
|
| 292 |
+
border-spacing: 0;
|
| 293 |
+
width: 100%;
|
| 294 |
+
border-radius: 10px;
|
| 295 |
+
overflow: hidden;
|
| 296 |
+
box-shadow: 0 4px 20px rgba(0,0,0,0.10);
|
| 297 |
+
}}
|
| 298 |
+
.sla-table thead tr th {{
|
| 299 |
+
padding: 11px 16px;
|
| 300 |
+
font-weight: 700;
|
| 301 |
+
letter-spacing: 0.4px;
|
| 302 |
+
font-size: 12px;
|
| 303 |
+
text-transform: uppercase;
|
| 304 |
+
border-bottom: 2px solid rgba(255,255,255,0.18);
|
| 305 |
+
color: #ffffff !important;
|
| 306 |
+
}}
|
| 307 |
+
.sla-table th.th-tipo {{
|
| 308 |
+
background: {cor_dark};
|
| 309 |
+
color: #ffffff !important;
|
| 310 |
+
text-align: left;
|
| 311 |
+
min-width: 150px;
|
| 312 |
+
border-right: 1px solid rgba(255,255,255,0.15);
|
| 313 |
+
}}
|
| 314 |
+
.sla-table th.th-sla {{
|
| 315 |
+
background: {cor_mid};
|
| 316 |
+
color: #ffffff !important;
|
| 317 |
+
text-align: center;
|
| 318 |
+
width: 80px;
|
| 319 |
+
border-right: 1px solid rgba(255,255,255,0.15);
|
| 320 |
+
}}
|
| 321 |
+
.sla-table th.th-total {{
|
| 322 |
+
background: {cor_dark};
|
| 323 |
+
color: #ffffff !important;
|
| 324 |
+
text-align: center;
|
| 325 |
+
width: 70px;
|
| 326 |
+
}}
|
| 327 |
+
.sla-table tbody tr {{
|
| 328 |
+
transition: background 0.15s;
|
| 329 |
+
}}
|
| 330 |
+
.sla-table tbody tr:nth-child(even) td {{
|
| 331 |
+
background-color: #f7f9fc;
|
| 332 |
+
}}
|
| 333 |
+
.sla-table tbody tr:nth-child(odd) td {{
|
| 334 |
+
background-color: #ffffff;
|
| 335 |
+
}}
|
| 336 |
+
.sla-table tbody tr:hover td {{
|
| 337 |
+
background-color: #eef2ff !important;
|
| 338 |
+
}}
|
| 339 |
+
.sla-table td {{
|
| 340 |
+
padding: 9px 16px;
|
| 341 |
+
border-bottom: 1px solid #e8eaf0;
|
| 342 |
+
vertical-align: middle;
|
| 343 |
+
}}
|
| 344 |
+
.sla-table td.td-tipo {{
|
| 345 |
+
font-weight: 600;
|
| 346 |
+
color: #1a1a2e;
|
| 347 |
+
border-left: 4px solid {cor_mid};
|
| 348 |
+
text-align: left;
|
| 349 |
+
background-color: inherit;
|
| 350 |
+
}}
|
| 351 |
+
.sla-table td.td-sla {{
|
| 352 |
+
text-align: center;
|
| 353 |
+
color: #546e7a;
|
| 354 |
+
font-style: italic;
|
| 355 |
+
font-size: 12px;
|
| 356 |
+
}}
|
| 357 |
+
.sla-table td.td-faixa {{
|
| 358 |
+
text-align: center;
|
| 359 |
+
}}
|
| 360 |
+
.sla-table td.td-total {{
|
| 361 |
+
text-align: center;
|
| 362 |
+
font-weight: 800;
|
| 363 |
+
font-size: 14px;
|
| 364 |
+
color: {cor_dark};
|
| 365 |
+
background-color: #f0f4ff !important;
|
| 366 |
+
border-left: 2px solid #c5cae9;
|
| 367 |
+
}}
|
| 368 |
+
.badge {{
|
| 369 |
+
display: inline-flex;
|
| 370 |
+
align-items: center;
|
| 371 |
+
justify-content: center;
|
| 372 |
+
min-width: 36px;
|
| 373 |
+
height: 26px;
|
| 374 |
+
padding: 0 10px;
|
| 375 |
+
border-radius: 20px;
|
| 376 |
+
font-weight: 700;
|
| 377 |
+
font-size: 13px;
|
| 378 |
+
line-height: 1;
|
| 379 |
+
}}
|
| 380 |
+
.badge-zero {{
|
| 381 |
+
color: #bdbdbd;
|
| 382 |
+
font-size: 16px;
|
| 383 |
+
font-weight: 400;
|
| 384 |
+
}}
|
| 385 |
+
.sub-label {{
|
| 386 |
+
display: block;
|
| 387 |
+
font-size: 10px;
|
| 388 |
+
font-weight: 400;
|
| 389 |
+
opacity: 0.85;
|
| 390 |
+
margin-top: 2px;
|
| 391 |
+
text-transform: none;
|
| 392 |
+
letter-spacing: 0;
|
| 393 |
+
color: #ffffff !important;
|
| 394 |
+
}}
|
| 395 |
+
</style>
|
| 396 |
+
<div class="sla-wrap">
|
| 397 |
+
<table class="sla-table">
|
| 398 |
+
<thead>
|
| 399 |
+
<tr>
|
| 400 |
+
<th class="th-tipo">TIPOS</th>
|
| 401 |
+
<th class="th-sla">SLA<span class="sub-label">[dias]</span></th>
|
| 402 |
+
"""
|
| 403 |
+
for label, bg in zip(faixa_labels, faixa_header_bg):
|
| 404 |
+
html += (f' <th style="background:{bg};color:#ffffff !important;'
|
| 405 |
+
f'text-align:center;min-width:90px;">'
|
| 406 |
+
f'{label}<span class="sub-label">[uni]</span></th>\n')
|
| 407 |
+
|
| 408 |
+
html += ' <th class="th-total">TOTAL</th>\n </tr>\n </thead>\n <tbody>\n'
|
| 409 |
+
|
| 410 |
+
for _, row in pivot.iterrows():
|
| 411 |
+
sla_val = row['SLA [dias]']
|
| 412 |
+
sla_str = str(int(sla_val)) if sla_val > 0 else '—'
|
| 413 |
+
html += f' <tr>\n <td class="td-tipo">{row["TIPOS"]}</td>\n'
|
| 414 |
+
html += f' <td class="td-sla">{sla_str}</td>\n'
|
| 415 |
+
|
| 416 |
+
for col, (bg, fg) in zip(faixa_cols, faixa_cell):
|
| 417 |
+
val = int(row[col])
|
| 418 |
+
if val == 0:
|
| 419 |
+
html += ' <td class="td-faixa"><span class="badge badge-zero">—</span></td>\n'
|
| 420 |
+
else:
|
| 421 |
+
html += (f' <td class="td-faixa">'
|
| 422 |
+
f'<span class="badge" style="background:{bg};color:{fg};">{val}</span>'
|
| 423 |
+
f'</td>\n')
|
| 424 |
+
|
| 425 |
+
total = int(row['TOTAL'])
|
| 426 |
+
html += f' <td class="td-total">{total}</td>\n </tr>\n'
|
| 427 |
+
|
| 428 |
+
html += ' </tbody>\n</table>\n</div>'
|
| 429 |
+
return html
|
| 430 |
+
|
| 431 |
+
# ── Renderizar KPI cards HTML ─────────────────────────────────────────────────
|
| 432 |
+
def render_kpi_html(stats: dict, categoria: str) -> str:
|
| 433 |
+
cor_dark, cor_mid = CAT_CORES.get(categoria, ('#212121', '#37474F'))
|
| 434 |
+
pct = stats['pct_ok']
|
| 435 |
+
if pct >= 80:
|
| 436 |
+
taxa_cor, taxa_bg = '#1B5E20', '#E8F5E9'
|
| 437 |
+
elif pct >= 60:
|
| 438 |
+
taxa_cor, taxa_bg = '#E65100', '#FFF8E1'
|
| 439 |
+
else:
|
| 440 |
+
taxa_cor, taxa_bg = '#B71C1C', '#FFEBEE'
|
| 441 |
+
|
| 442 |
+
html = f"""
|
| 443 |
+
<style>
|
| 444 |
+
.kpi-grid {{
|
| 445 |
+
display: grid;
|
| 446 |
+
grid-template-columns: 1fr 1fr;
|
| 447 |
+
gap: 14px;
|
| 448 |
+
font-family: 'Inter', 'Segoe UI', Arial, sans-serif;
|
| 449 |
+
}}
|
| 450 |
+
.kpi-card {{
|
| 451 |
+
border-radius: 12px;
|
| 452 |
+
padding: 18px 16px 14px;
|
| 453 |
+
text-align: center;
|
| 454 |
+
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
|
| 455 |
+
border-top: 4px solid;
|
| 456 |
+
}}
|
| 457 |
+
.kpi-label {{
|
| 458 |
+
font-size: 11px;
|
| 459 |
+
font-weight: 600;
|
| 460 |
+
text-transform: uppercase;
|
| 461 |
+
letter-spacing: 0.8px;
|
| 462 |
+
margin-bottom: 8px;
|
| 463 |
+
opacity: 0.75;
|
| 464 |
+
}}
|
| 465 |
+
.kpi-value {{
|
| 466 |
+
font-size: 36px;
|
| 467 |
+
font-weight: 800;
|
| 468 |
+
line-height: 1;
|
| 469 |
+
}}
|
| 470 |
+
.kpi-sub {{
|
| 471 |
+
font-size: 11px;
|
| 472 |
+
margin-top: 6px;
|
| 473 |
+
opacity: 0.6;
|
| 474 |
+
}}
|
| 475 |
+
</style>
|
| 476 |
+
<div class="kpi-grid">
|
| 477 |
+
<div class="kpi-card" style="background:#F3F4F6;border-color:{cor_mid};color:{cor_dark};">
|
| 478 |
+
<div class="kpi-label">Total de Tarefas</div>
|
| 479 |
+
<div class="kpi-value">{stats['total']}</div>
|
| 480 |
+
<div class="kpi-sub">registos processados</div>
|
| 481 |
+
</div>
|
| 482 |
+
<div class="kpi-card" style="background:#E8F5E9;border-color:#2E7D32;color:#1B5E20;">
|
| 483 |
+
<div class="kpi-label">Dentro do SLA</div>
|
| 484 |
+
<div class="kpi-value">{stats['dentro']}</div>
|
| 485 |
+
<div class="kpi-sub">≤ 100 % SLA</div>
|
| 486 |
+
</div>
|
| 487 |
+
<div class="kpi-card" style="background:#FFEBEE;border-color:#C62828;color:#B71C1C;">
|
| 488 |
+
<div class="kpi-label">SLA Excedido</div>
|
| 489 |
+
<div class="kpi-value">{stats['excedido']}</div>
|
| 490 |
+
<div class="kpi-sub">> 100 % SLA</div>
|
| 491 |
+
</div>
|
| 492 |
+
<div class="kpi-card" style="background:{taxa_bg};border-color:{taxa_cor};color:{taxa_cor};">
|
| 493 |
+
<div class="kpi-label">Taxa de Cumprimento</div>
|
| 494 |
+
<div class="kpi-value">{pct} %</div>
|
| 495 |
+
<div class="kpi-sub">tarefas dentro do SLA</div>
|
| 496 |
+
</div>
|
| 497 |
+
</div>
|
| 498 |
+
"""
|
| 499 |
+
return html
|
| 500 |
+
|
| 501 |
+
# ── Exportações ────────────────────────────────────────────────────────────────
|
| 502 |
+
def exportar_csv_pivot(categoria: str) -> str:
|
| 503 |
+
pivot = build_pivot(DF_GLOBAL, categoria)
|
| 504 |
+
ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
|
| 505 |
+
nome = f"sla_pivot_{categoria.lower().replace(' ', '_')}_{ts}.csv"
|
| 506 |
+
path = os.path.join(OUTPUT_DIR, nome)
|
| 507 |
+
pivot.to_csv(path, index=False, encoding='utf-8-sig', sep=';')
|
| 508 |
+
return path
|
| 509 |
+
|
| 510 |
+
def exportar_csv_fact(categoria: str) -> str:
|
| 511 |
+
sub = DF_GLOBAL.copy() if categoria == 'GLOBAL' else DF_GLOBAL[DF_GLOBAL['CATEGORIA'] == categoria].copy()
|
| 512 |
+
fact = sub[[
|
| 513 |
+
'SUB-CIP', 'PROJETO', 'TIPO', 'TIPO_LABEL', 'RB STATUS', 'CATEGORIA',
|
| 514 |
+
'DATA_ADJ_CLIENTE', 'DATA_PREVISTA', 'TEMPO_EXECUCAO',
|
| 515 |
+
'ATUAL', 'DIFDIAS', 'SLA_FIXO', 'PCT_SLA', 'FAIXA_SLA', 'DATA_CALCULO'
|
| 516 |
+
]].copy()
|
| 517 |
+
fact['DATA_ADJ_CLIENTE'] = fact['DATA_ADJ_CLIENTE'].dt.strftime('%d/%m/%Y')
|
| 518 |
+
fact['DATA_PREVISTA'] = fact['DATA_PREVISTA'].dt.strftime('%d/%m/%Y')
|
| 519 |
+
ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
|
| 520 |
+
nome = f"sla_fact_{categoria.lower().replace(' ', '_')}_{ts}.csv"
|
| 521 |
+
path = os.path.join(OUTPUT_DIR, nome)
|
| 522 |
+
fact.to_csv(path, index=False, encoding='utf-8-sig', sep=';')
|
| 523 |
+
return path
|
| 524 |
+
|
| 525 |
+
# ── Actualizar vista principal ─────────────────────────────────────────────────
|
| 526 |
+
def atualizar_vista(categoria: str):
|
| 527 |
+
pivot = build_pivot(DF_GLOBAL, categoria)
|
| 528 |
+
tabela_html = render_html_table(pivot, categoria)
|
| 529 |
+
stats = get_stats(categoria)
|
| 530 |
+
kpi_html = render_kpi_html(stats, categoria)
|
| 531 |
+
return tabela_html, kpi_html
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
# ═══════════════════════════════════════════════════════════════════════════════
|
| 535 |
+
# ── RAG (igual ao 1º script): contexto estruturado + LLM NVIDIA ───────────────
|
| 536 |
+
# ═══════════════════════════════════════════════════════════════════════════════
|
| 537 |
+
def gerar_contexto_rag() -> str:
|
| 538 |
+
"""
|
| 539 |
+
Contexto estruturado dos dados do dashboard.
|
| 540 |
+
(Mesmo estilo do 1º script: rico, com cruzamentos, riscos, aging, tendências)
|
| 541 |
+
"""
|
| 542 |
+
hoje_ts = pd.Timestamp.today().normalize()
|
| 543 |
+
hoje_str = hoje_ts.strftime('%d/%m/%Y')
|
| 544 |
+
df = DF_GLOBAL.copy()
|
| 545 |
+
linhas = []
|
| 546 |
+
|
| 547 |
+
linhas.append("=" * 70)
|
| 548 |
+
linhas.append("DASHBOARD SLA — CONTEXTO COMPLETO (CIRCET)")
|
| 549 |
+
linhas.append("=" * 70)
|
| 550 |
+
linhas.append(f"Data de referência : {hoje_str}")
|
| 551 |
+
linhas.append(f"Total de registos : {len(df)}")
|
| 552 |
+
linhas.append(f"Tipos de tarefa : {', '.join(sorted(df['TIPO_LABEL'].unique()))}")
|
| 553 |
+
linhas.append(f"Categorias activas : EM CURSO ({(df['CATEGORIA']=='EM CURSO').sum()}) | "
|
| 554 |
+
f"LICENCIAMENTO ({(df['CATEGORIA']=='LICENCIAMENTO').sum()}) | "
|
| 555 |
+
f"VALIDADO ({(df['CATEGORIA']=='VALIDADO').sum()}) | "
|
| 556 |
+
f"GLOBAL ({(df['CATEGORIA']=='GLOBAL').sum()})")
|
| 557 |
+
linhas.append("")
|
| 558 |
+
|
| 559 |
+
# KPIs por categoria
|
| 560 |
+
linhas.append("-" * 70)
|
| 561 |
+
linhas.append("KPIs EXECUTIVOS (por categoria)")
|
| 562 |
+
linhas.append("-" * 70)
|
| 563 |
+
for cat in ['GLOBAL', 'EM CURSO', 'LICENCIAMENTO', 'VALIDADO']:
|
| 564 |
+
stats = get_stats(cat)
|
| 565 |
+
sub = df if cat == 'GLOBAL' else df[df['CATEGORIA'] == cat]
|
| 566 |
+
n_sla = sub[sub['SLA_FIXO'] > 0]
|
| 567 |
+
pct_medio = round(n_sla['PCT_SLA'].mean(), 1) if len(n_sla) > 0 else 0
|
| 568 |
+
linhas.append(f" [{cat}] Total={stats['total']} | Dentro SLA={stats['dentro']} ({stats['pct_ok']}%) | "
|
| 569 |
+
f"Excedido={stats['excedido']} | %SLA médio={pct_medio}%")
|
| 570 |
+
linhas.append("")
|
| 571 |
+
|
| 572 |
+
# Risco por tipo
|
| 573 |
+
linhas.append("-" * 70)
|
| 574 |
+
linhas.append("ANÁLISE DE RISCO — TIPOS ORDENADOS POR TAXA DE INCUMPRIMENTO")
|
| 575 |
+
linhas.append("-" * 70)
|
| 576 |
+
risco = df[df['SLA_FIXO'] > 0].groupby('TIPO_LABEL').agg(
|
| 577 |
+
total=('TIPO_LABEL', 'count'),
|
| 578 |
+
excedido=('FAIXA_SLA', lambda x: (x == '> 100 %').sum()),
|
| 579 |
+
pct_medio=('PCT_SLA', 'mean')
|
| 580 |
+
).reset_index()
|
| 581 |
+
risco['taxa_exc'] = (risco['excedido'] / risco['total'] * 100).round(1)
|
| 582 |
+
risco['pct_medio'] = risco['pct_medio'].round(1)
|
| 583 |
+
risco = risco.sort_values('taxa_exc', ascending=False)
|
| 584 |
+
for _, r in risco.iterrows():
|
| 585 |
+
nivel = "CRITICO" if r['taxa_exc'] >= 70 else ("ALTO" if r['taxa_exc'] >= 40 else ("MEDIO" if r['taxa_exc'] >= 20 else "BAIXO"))
|
| 586 |
+
linhas.append(
|
| 587 |
+
f" [{nivel}] {r['TIPO_LABEL']:<20} | Excedido: {r['excedido']}/{r['total']} ({r['taxa_exc']}%) | %SLA médio: {r['pct_medio']}%"
|
| 588 |
+
)
|
| 589 |
+
linhas.append("")
|
| 590 |
+
|
| 591 |
+
# Aging (top 20)
|
| 592 |
+
linhas.append("-" * 70)
|
| 593 |
+
linhas.append("AGING — TOP 20 PROJECTOS COM MAIOR EXCESSO DE SLA")
|
| 594 |
+
linhas.append("-" * 70)
|
| 595 |
+
excedidos = df[df['FAIXA_SLA'] == '> 100 %'].copy()
|
| 596 |
+
excedidos = excedidos.sort_values('PCT_SLA', ascending=False).head(20)
|
| 597 |
+
for _, row in excedidos.iterrows():
|
| 598 |
+
data_adj = row['DATA_ADJ_CLIENTE'].strftime('%d/%m/%Y') if pd.notna(row['DATA_ADJ_CLIENTE']) else 'N/D'
|
| 599 |
+
dias_atraso = int(row['ATUAL']) if pd.notna(row['ATUAL']) else 0
|
| 600 |
+
linhas.append(
|
| 601 |
+
f" {row['PROJETO']:<14} [{row['CATEGORIA']:<13}] "
|
| 602 |
+
f"Tipo: {row['TIPO_LABEL']:<16} Status: {row['RB STATUS']:<30} "
|
| 603 |
+
f"% SLA: {row['PCT_SLA']:>6}% | Atraso: {dias_atraso:>4}d | Adj: {data_adj}"
|
| 604 |
+
)
|
| 605 |
+
linhas.append("")
|
| 606 |
+
|
| 607 |
+
# Gargalos: tipo x status em excedidos
|
| 608 |
+
linhas.append("-" * 70)
|
| 609 |
+
linhas.append("GARGALOS — TIPO × STATUS (excedidos)")
|
| 610 |
+
linhas.append("-" * 70)
|
| 611 |
+
cross = df[df['FAIXA_SLA'] == '> 100 %'].groupby(['TIPO_LABEL', 'RB STATUS']).size().reset_index(name='n_excedidos')
|
| 612 |
+
cross = cross.sort_values('n_excedidos', ascending=False).head(25)
|
| 613 |
+
for _, r in cross.iterrows():
|
| 614 |
+
linhas.append(f" {r['TIPO_LABEL']:<20} + {r['RB STATUS']:<35} → {r['n_excedidos']} excedidos")
|
| 615 |
+
linhas.append("")
|
| 616 |
+
|
| 617 |
+
# Volume por mês (últimos 12)
|
| 618 |
+
linhas.append("-" * 70)
|
| 619 |
+
linhas.append("ADJUDICAÇÕES POR MÊS (últimos 12 meses disponíveis)")
|
| 620 |
+
linhas.append("-" * 70)
|
| 621 |
+
df_datas = df[df['DATA_ADJ_CLIENTE'].notna()].copy()
|
| 622 |
+
if len(df_datas) > 0:
|
| 623 |
+
df_datas['MES_ADJ'] = df_datas['DATA_ADJ_CLIENTE'].dt.to_period('M')
|
| 624 |
+
mes_counts = df_datas.groupby('MES_ADJ').agg(
|
| 625 |
+
total=('PROJETO', 'count'),
|
| 626 |
+
excedido=('FAIXA_SLA', lambda x: (x == '> 100 %').sum())
|
| 627 |
+
).reset_index().sort_values('MES_ADJ', ascending=False).head(12)
|
| 628 |
+
|
| 629 |
+
for _, r in mes_counts.iterrows():
|
| 630 |
+
taxa = round(r['excedido'] / r['total'] * 100, 1) if r['total'] > 0 else 0
|
| 631 |
+
linhas.append(f" {str(r['MES_ADJ']):<10} | Adjudicados: {r['total']:3d} | Excedidos: {r['excedido']:3d} ({taxa}%)")
|
| 632 |
+
else:
|
| 633 |
+
linhas.append(" (sem datas válidas em DATA_ADJ_CLIENTE)")
|
| 634 |
+
linhas.append("")
|
| 635 |
+
|
| 636 |
+
# Resumo executivo automático
|
| 637 |
+
linhas.append("-" * 70)
|
| 638 |
+
linhas.append("RESUMO EXECUTIVO AUTOMÁTICO")
|
| 639 |
+
linhas.append("-" * 70)
|
| 640 |
+
total_g = len(df)
|
| 641 |
+
exc_g = int((df['FAIXA_SLA'] == '> 100 %').sum())
|
| 642 |
+
taxa_g = round(exc_g / total_g * 100, 1) if total_g > 0 else 0.0
|
| 643 |
+
tipo_pior = risco.iloc[0]['TIPO_LABEL'] if len(risco) > 0 else 'N/D'
|
| 644 |
+
taxa_pior = risco.iloc[0]['taxa_exc'] if len(risco) > 0 else 0
|
| 645 |
+
linhas.append(f" Portfolio total : {total_g} tarefas")
|
| 646 |
+
linhas.append(f" Taxa incumprimento : {taxa_g}% ({exc_g} tarefas com SLA > 100%)")
|
| 647 |
+
linhas.append(f" Tipo mais crítico : {tipo_pior} ({taxa_pior}% de incumprimento)")
|
| 648 |
+
linhas.append("")
|
| 649 |
+
|
| 650 |
+
return "\n".join(linhas)
|
| 651 |
+
|
| 652 |
+
# Pré-calcular o contexto
|
| 653 |
+
CONTEXTO_RAG = gerar_contexto_rag()
|
| 654 |
+
|
| 655 |
+
def responder_pergunta(pergunta: str, historico: list, api_key: str, modelo: str) -> tuple:
|
| 656 |
+
if not api_key or not api_key.strip():
|
| 657 |
+
historico = historico + [
|
| 658 |
+
{"role": "user", "content": pergunta},
|
| 659 |
+
{"role": "assistant", "content": "Por favor, insira a sua chave API da NVIDIA NIM no campo acima para usar o assistente."}
|
| 660 |
+
]
|
| 661 |
+
return historico, ""
|
| 662 |
+
|
| 663 |
+
if not pergunta or not pergunta.strip():
|
| 664 |
+
return historico, ""
|
| 665 |
+
|
| 666 |
+
try:
|
| 667 |
+
client = criar_cliente_nvidia(api_key.strip())
|
| 668 |
+
|
| 669 |
+
system_prompt = f"""Você é um Gestor de Projecto Sénior com mais de 15 anos de experiência em gestão de portfolios de telecomunicações, especializado em controlo SLA, análise de risco operacional e reporte executivo.
|
| 670 |
+
O seu papel é analisar os dados reais do dashboard SLA e responder com rigor. Interprete números, identifique padrões, riscos e proponha acções.
|
| 671 |
+
Responda SEMPRE em português europeu (Portugal), linguagem profissional e directa. Use números exactos sempre que possível.
|
| 672 |
+
--- DADOS DO DASHBOARD SLA ---
|
| 673 |
+
{CONTEXTO_RAG}
|
| 674 |
+
--- FIM DOS DADOS ---
|
| 675 |
+
Regras:
|
| 676 |
+
- Estruture: Situação → Análise → Recomendação (quando aplicável)
|
| 677 |
+
- Use tabelas markdown para comparações com 3+ itens
|
| 678 |
+
- Classifique risco: 🔴 CRÍTICO (≥70%) | 🟠 ALTO (40-69%) | 🟡 MÉDIO (20-39%) | 🟢 BAIXO (<20%)
|
| 679 |
+
- Se não houver dados para responder, diga claramente o que falta
|
| 680 |
+
"""
|
| 681 |
+
|
| 682 |
+
messages = [{"role": "system", "content": system_prompt}]
|
| 683 |
+
|
| 684 |
+
for msg in historico[-10:]:
|
| 685 |
+
messages.append({"role": msg["role"], "content": msg["content"]})
|
| 686 |
+
|
| 687 |
+
messages.append({"role": "user", "content": pergunta})
|
| 688 |
+
|
| 689 |
+
response = client.chat.completions.create(
|
| 690 |
+
model=modelo,
|
| 691 |
+
messages=messages,
|
| 692 |
+
temperature=0.2,
|
| 693 |
+
max_tokens=1500,
|
| 694 |
+
)
|
| 695 |
+
|
| 696 |
+
resposta = response.choices[0].message.content
|
| 697 |
+
|
| 698 |
+
historico = historico + [
|
| 699 |
+
{"role": "user", "content": pergunta},
|
| 700 |
+
{"role": "assistant", "content": resposta}
|
| 701 |
+
]
|
| 702 |
+
return historico, ""
|
| 703 |
+
|
| 704 |
+
except Exception as e:
|
| 705 |
+
erro = str(e)
|
| 706 |
+
if "401" in erro or "Unauthorized" in erro or "invalid_api_key" in erro.lower():
|
| 707 |
+
msg_erro = "❌ Chave API inválida ou sem autorização. Verifique a sua chave NVIDIA NIM."
|
| 708 |
+
elif "429" in erro or "rate_limit" in erro.lower():
|
| 709 |
+
msg_erro = "⏳ Limite de pedidos atingido. Aguarde alguns segundos e tente novamente."
|
| 710 |
+
elif "model" in erro.lower() and "not found" in erro.lower():
|
| 711 |
+
msg_erro = f"❌ Modelo '{modelo}' não encontrado. Tente selecionar outro modelo."
|
| 712 |
+
else:
|
| 713 |
+
msg_erro = f"❌ Erro ao contactar a API NVIDIA NIM: {erro}"
|
| 714 |
+
|
| 715 |
+
historico = historico + [
|
| 716 |
+
{"role": "user", "content": pergunta},
|
| 717 |
+
{"role": "assistant", "content": msg_erro}
|
| 718 |
+
]
|
| 719 |
+
return historico, ""
|
| 720 |
+
|
| 721 |
+
def limpar_chat():
|
| 722 |
+
return [], ""
|
| 723 |
+
|
| 724 |
+
def perguntas_rapidas(pergunta_selecionada: str) -> str:
|
| 725 |
+
return pergunta_selecionada
|
| 726 |
+
|
| 727 |
+
|
| 728 |
+
# ── CSS global (IGUAL AO SEU 2º SCRIPT) ────────────────────────────────────────
|
| 729 |
+
CSS = """
|
| 730 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
|
| 731 |
+
|
| 732 |
+
body, .gradio-container {
|
| 733 |
+
font-family: 'Inter', 'Segoe UI', Arial, sans-serif !important;
|
| 734 |
+
background: #F0F2F8 !important;
|
| 735 |
+
}
|
| 736 |
+
.gradio-container {
|
| 737 |
+
max-width: 1280px !important;
|
| 738 |
+
margin: 0 auto !important;
|
| 739 |
+
padding: 0 !important;
|
| 740 |
+
}
|
| 741 |
+
.app-header {
|
| 742 |
+
background: linear-gradient(135deg, #0D47A1 0%, #1565C0 50%, #1976D2 100%);
|
| 743 |
+
padding: 28px 36px 22px;
|
| 744 |
+
border-radius: 0 0 16px 16px;
|
| 745 |
+
margin-bottom: 24px;
|
| 746 |
+
box-shadow: 0 4px 20px rgba(13,71,161,0.25);
|
| 747 |
+
}
|
| 748 |
+
.app-header * {
|
| 749 |
+
color: #ffffff !important;
|
| 750 |
+
}
|
| 751 |
+
.app-header h1 {
|
| 752 |
+
margin: 0 0 6px;
|
| 753 |
+
font-size: 26px;
|
| 754 |
+
font-weight: 800;
|
| 755 |
+
letter-spacing: -0.3px;
|
| 756 |
+
color: #ffffff !important;
|
| 757 |
+
}
|
| 758 |
+
.app-header p {
|
| 759 |
+
margin: 0;
|
| 760 |
+
font-size: 13px;
|
| 761 |
+
font-weight: 400;
|
| 762 |
+
color: #ffffff !important;
|
| 763 |
+
opacity: 0.92;
|
| 764 |
+
}
|
| 765 |
+
.cat-selector label {
|
| 766 |
+
font-weight: 700 !important;
|
| 767 |
+
font-size: 12px !important;
|
| 768 |
+
text-transform: uppercase !important;
|
| 769 |
+
letter-spacing: 0.6px !important;
|
| 770 |
+
color: #546e7a !important;
|
| 771 |
+
margin-bottom: 8px !important;
|
| 772 |
+
}
|
| 773 |
+
.cat-selector .wrap { gap: 10px !important; }
|
| 774 |
+
.cat-selector input[type=radio] + span {
|
| 775 |
+
border-radius: 8px !important;
|
| 776 |
+
padding: 9px 22px !important;
|
| 777 |
+
font-weight: 600 !important;
|
| 778 |
+
font-size: 13px !important;
|
| 779 |
+
border: 2px solid #e0e0e0 !important;
|
| 780 |
+
background: white !important;
|
| 781 |
+
color: #37474F !important;
|
| 782 |
+
transition: all 0.2s !important;
|
| 783 |
+
cursor: pointer !important;
|
| 784 |
+
}
|
| 785 |
+
.cat-selector input[type=radio]:checked + span {
|
| 786 |
+
background: #0D47A1 !important;
|
| 787 |
+
color: white !important;
|
| 788 |
+
border-color: #0D47A1 !important;
|
| 789 |
+
box-shadow: 0 4px 12px rgba(13,71,161,0.3) !important;
|
| 790 |
+
}
|
| 791 |
+
.btn-export {
|
| 792 |
+
border-radius: 8px !important;
|
| 793 |
+
font-weight: 600 !important;
|
| 794 |
+
font-size: 13px !important;
|
| 795 |
+
padding: 10px 20px !important;
|
| 796 |
+
transition: all 0.2s !important;
|
| 797 |
+
}
|
| 798 |
+
.export-section {
|
| 799 |
+
background: white;
|
| 800 |
+
border-radius: 12px;
|
| 801 |
+
padding: 20px 24px;
|
| 802 |
+
box-shadow: 0 2px 10px rgba(0,0,0,0.06);
|
| 803 |
+
margin-top: 16px;
|
| 804 |
+
}
|
| 805 |
+
.legenda-bar {
|
| 806 |
+
display: flex;
|
| 807 |
+
gap: 20px;
|
| 808 |
+
align-items: center;
|
| 809 |
+
background: white;
|
| 810 |
+
border-radius: 10px;
|
| 811 |
+
padding: 12px 20px;
|
| 812 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
| 813 |
+
margin-top: 16px;
|
| 814 |
+
flex-wrap: wrap;
|
| 815 |
+
}
|
| 816 |
+
footer { display: none !important; }
|
| 817 |
+
.gr-panel, .gr-box { border-radius: 12px !important; }
|
| 818 |
+
"""
|
| 819 |
+
|
| 820 |
+
# ── Interface Gradio ───────────────────────────────────────────────────────────
|
| 821 |
+
CATEGORIAS = ['EM CURSO', 'LICENCIAMENTO', 'VALIDADO', 'GLOBAL']
|
| 822 |
+
DATA_REF = pd.Timestamp.today().strftime('%d/%m/%Y')
|
| 823 |
+
N_TOTAL = len(DF_GLOBAL)
|
| 824 |
+
|
| 825 |
+
with gr.Blocks(title="CIRCET SLA") as demo:
|
| 826 |
+
|
| 827 |
+
# ── Header — IGUAL AO SEU ────────────────────────────────────────────────
|
| 828 |
+
gr.HTML(f"""
|
| 829 |
+
<div class="app-header" style="
|
| 830 |
+
background: linear-gradient(135deg, #0D47A1 0%, #1565C0 50%, #1976D2 100%);
|
| 831 |
+
padding: 28px 36px 22px;
|
| 832 |
+
border-radius: 0 0 16px 16px;
|
| 833 |
+
margin-bottom: 24px;
|
| 834 |
+
box-shadow: 0 4px 20px rgba(13,71,161,0.25);
|
| 835 |
+
">
|
| 836 |
+
<h1 style="margin:0 0 6px;font-size:26px;font-weight:800;letter-spacing:-0.3px;
|
| 837 |
+
color:#ffffff !important;font-family:'Inter','Segoe UI',Arial,sans-serif;">
|
| 838 |
+
📊 CIRCET SLA — Acompanhamento de Tarefas
|
| 839 |
+
</h1>
|
| 840 |
+
<p style="margin:0;font-size:13px;font-weight:400;
|
| 841 |
+
color:#ffffff !important;opacity:0.92;
|
| 842 |
+
font-family:'Inter','Segoe UI',Arial,sans-serif;">
|
| 843 |
+
Controlo SLA por tipo de tarefa · Distribuição por faixas de percentagem
|
| 844 |
+
· {N_TOTAL} registos · Referência: {DATA_REF}
|
| 845 |
+
</p>
|
| 846 |
+
</div>
|
| 847 |
+
""")
|
| 848 |
+
|
| 849 |
+
# ── Tabs (apenas para adicionar RAG sem mexer no layout do dashboard) ─────
|
| 850 |
+
with gr.Tabs():
|
| 851 |
+
|
| 852 |
+
# ── TAB 1: Dashboard (conteúdo IGUAL AO SEU) ────────────────────────
|
| 853 |
+
with gr.Tab("📊 Dashboard SLA"):
|
| 854 |
+
|
| 855 |
+
# Selector de categoria
|
| 856 |
+
with gr.Row():
|
| 857 |
+
cat_selector = gr.Radio(
|
| 858 |
+
choices=CATEGORIAS,
|
| 859 |
+
value='EM CURSO',
|
| 860 |
+
label='Categoria',
|
| 861 |
+
interactive=True,
|
| 862 |
+
elem_classes=['cat-selector'],
|
| 863 |
+
)
|
| 864 |
+
|
| 865 |
+
# Tabela + KPIs
|
| 866 |
+
with gr.Row(equal_height=True):
|
| 867 |
+
with gr.Column(scale=4):
|
| 868 |
+
tabela_out = gr.HTML()
|
| 869 |
+
with gr.Column(scale=1, min_width=220):
|
| 870 |
+
kpi_out = gr.HTML()
|
| 871 |
+
|
| 872 |
+
# Secção de exportação
|
| 873 |
+
gr.HTML('<div class="export-section"><b style="font-size:13px;color:#37474F;'
|
| 874 |
+
'text-transform:uppercase;letter-spacing:0.6px;">⬇ Exportar Dados</b></div>')
|
| 875 |
+
|
| 876 |
+
with gr.Row():
|
| 877 |
+
with gr.Column(scale=1):
|
| 878 |
+
gr.Markdown("**Pivot da categoria** — distribuição por faixas SLA")
|
| 879 |
+
btn_pivot = gr.Button("⬇ CSV — Tabela Pivot", variant="secondary", elem_classes=["btn-export"])
|
| 880 |
+
file_pivot = gr.File(label="", show_label=False)
|
| 881 |
+
|
| 882 |
+
with gr.Column(scale=1):
|
| 883 |
+
gr.Markdown("**Dados calculados completos** — todos os campos do dash.R")
|
| 884 |
+
btn_fact = gr.Button("⬇ CSV — Dados Calculados", variant="secondary", elem_classes=["btn-export"])
|
| 885 |
+
file_fact = gr.File(label="", show_label=False)
|
| 886 |
+
|
| 887 |
+
# Legenda
|
| 888 |
+
gr.HTML("""
|
| 889 |
+
<div class="legenda-bar">
|
| 890 |
+
<span style="font-size:12px;font-weight:700;color:#546e7a;text-transform:uppercase;letter-spacing:0.5px;">Legenda:</span>
|
| 891 |
+
<span style="display:inline-flex;align-items:center;gap:6px;font-size:13px;">
|
| 892 |
+
<span style="width:14px;height:14px;border-radius:50%;background:#2E7D32;display:inline-block;"></span>
|
| 893 |
+
<b style="color:#1B5E20">< 50 %</b> — Dentro do prazo
|
| 894 |
+
</span>
|
| 895 |
+
<span style="display:inline-flex;align-items:center;gap:6px;font-size:13px;">
|
| 896 |
+
<span style="width:14px;height:14px;border-radius:50%;background:#E65100;display:inline-block;"></span>
|
| 897 |
+
<b style="color:#E65100">50 % < X ≤ 75 %</b> — Atenção
|
| 898 |
+
</span>
|
| 899 |
+
<span style="display:inline-flex;align-items:center;gap:6px;font-size:13px;">
|
| 900 |
+
<span style="width:14px;height:14px;border-radius:50%;background:#BF360C;display:inline-block;"></span>
|
| 901 |
+
<b style="color:#BF360C">75 % < X ≤ 100 %</b> — Crítico
|
| 902 |
+
</span>
|
| 903 |
+
<span style="display:inline-flex;align-items:center;gap:6px;font-size:13px;">
|
| 904 |
+
<span style="width:14px;height:14px;border-radius:50%;background:#B71C1C;display:inline-block;"></span>
|
| 905 |
+
<b style="color:#B71C1C">> 100 %</b> — SLA excedido
|
| 906 |
+
</span>
|
| 907 |
+
</div>
|
| 908 |
+
""")
|
| 909 |
+
|
| 910 |
+
# Eventos do dashboard
|
| 911 |
+
cat_selector.change(fn=atualizar_vista, inputs=cat_selector, outputs=[tabela_out, kpi_out])
|
| 912 |
+
demo.load(fn=lambda: atualizar_vista('EM CURSO'), outputs=[tabela_out, kpi_out])
|
| 913 |
+
btn_pivot.click(fn=exportar_csv_pivot, inputs=cat_selector, outputs=file_pivot)
|
| 914 |
+
btn_fact.click(fn=exportar_csv_fact, inputs=cat_selector, outputs=file_fact)
|
| 915 |
+
|
| 916 |
+
# ── TAB 2: Assistente IA (RAG) ───────────────────────────────────────
|
| 917 |
+
with gr.Tab("Assistente IA (RAG)"):
|
| 918 |
+
|
| 919 |
+
gr.HTML("""
|
| 920 |
+
<div style="background:linear-gradient(135deg,#0D47A1 0%,#1565C0 50%,#1976D2 100%);
|
| 921 |
+
border-radius:10px;padding:16px 20px;margin-bottom:16px;">
|
| 922 |
+
<h3 style="margin:0 0 6px;color:#fff;font-size:16px;font-weight:700;">
|
| 923 |
+
Assistente RAG — NVIDIA NIM
|
| 924 |
+
</h3>
|
| 925 |
+
<p style="margin:0;color:rgba(255,255,255,0.85);font-size:13px;">
|
| 926 |
+
Faça perguntas em linguagem natural sobre os dados do dashboard SLA.
|
| 927 |
+
</p>
|
| 928 |
+
</div>
|
| 929 |
+
""")
|
| 930 |
+
|
| 931 |
+
with gr.Row():
|
| 932 |
+
with gr.Column(scale=3):
|
| 933 |
+
api_key_input = gr.Textbox(
|
| 934 |
+
label="🔑 Chave API NVIDIA NIM",
|
| 935 |
+
placeholder="nvapi-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
| 936 |
+
value=NVIDIA_API_KEY_ENV,
|
| 937 |
+
type="password",
|
| 938 |
+
info="Pode vir de env NVIDIA_API_KEY ou ser inserida manualmente."
|
| 939 |
+
)
|
| 940 |
+
with gr.Column(scale=2):
|
| 941 |
+
modelo_selector = gr.Dropdown(
|
| 942 |
+
choices=MODELOS_NVIDIA,
|
| 943 |
+
value="meta/llama-3.3-70b-instruct",
|
| 944 |
+
label="🧠 Modelo NVIDIA NIM"
|
| 945 |
+
)
|
| 946 |
+
|
| 947 |
+
with gr.Row():
|
| 948 |
+
perguntas_dropdown = gr.Dropdown(
|
| 949 |
+
choices=PERGUNTAS_SUGERIDAS,
|
| 950 |
+
label="💡 Perguntas sugeridas (clique para usar)",
|
| 951 |
+
value=None,
|
| 952 |
+
interactive=True,
|
| 953 |
+
)
|
| 954 |
+
|
| 955 |
+
chatbot = gr.Chatbot(
|
| 956 |
+
label="Conversa com o Assistente SLA",
|
| 957 |
+
height=480,
|
| 958 |
+
placeholder="<div style='text-align:center;padding:40px;color:#9e9e9e;'>"
|
| 959 |
+
"<b>Assistente SLA com NVIDIA NIM</b><br>"
|
| 960 |
+
"<span style='font-size:13px;'>Insira a sua chave e faça uma pergunta</span>"
|
| 961 |
+
"</div>"
|
| 962 |
+
)
|
| 963 |
+
|
| 964 |
+
with gr.Row():
|
| 965 |
+
pergunta_input = gr.Textbox(
|
| 966 |
+
label="",
|
| 967 |
+
placeholder="Ex: Quais os tipos mais críticos? Qual a taxa global? Onde estão os gargalos?",
|
| 968 |
+
lines=2,
|
| 969 |
+
scale=5,
|
| 970 |
+
show_label=False,
|
| 971 |
+
)
|
| 972 |
+
with gr.Column(scale=1, min_width=120):
|
| 973 |
+
btn_enviar = gr.Button("Enviar ▶", variant="primary", size="lg")
|
| 974 |
+
btn_limpar = gr.Button("🗑 Limpar", variant="secondary")
|
| 975 |
+
|
| 976 |
+
with gr.Accordion("ℹ️ Ver contexto RAG (dados enviados ao modelo)", open=False):
|
| 977 |
+
gr.Textbox(
|
| 978 |
+
value=CONTEXTO_RAG,
|
| 979 |
+
label="Contexto estruturado dos dados (enviado ao modelo)",
|
| 980 |
+
lines=20,
|
| 981 |
+
interactive=False,
|
| 982 |
+
)
|
| 983 |
+
|
| 984 |
+
btn_enviar.click(
|
| 985 |
+
fn=responder_pergunta,
|
| 986 |
+
inputs=[pergunta_input, chatbot, api_key_input, modelo_selector],
|
| 987 |
+
outputs=[chatbot, pergunta_input],
|
| 988 |
+
)
|
| 989 |
+
pergunta_input.submit(
|
| 990 |
+
fn=responder_pergunta,
|
| 991 |
+
inputs=[pergunta_input, chatbot, api_key_input, modelo_selector],
|
| 992 |
+
outputs=[chatbot, pergunta_input],
|
| 993 |
+
)
|
| 994 |
+
btn_limpar.click(fn=limpar_chat, outputs=[chatbot, pergunta_input])
|
| 995 |
+
perguntas_dropdown.change(fn=perguntas_rapidas, inputs=perguntas_dropdown, outputs=pergunta_input)
|
| 996 |
+
|
| 997 |
+
|
| 998 |
+
if __name__ == "__main__":
|
| 999 |
+
demo.launch(
|
| 1000 |
+
css=CSS,
|
| 1001 |
+
theme=gr.themes.Base(),
|
| 1002 |
+
allowed_paths=[OUTPUT_DIR, BASE],
|
| 1003 |
+
)
|
emcurso.csv
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
EM CURSO,1,01.1 SURVEY EM AGENDAMENTO
|
| 2 |
+
EM CURSO,1,01.2 SURVEY PENDENTE CLIENTE
|
| 3 |
+
EM CURSO,1,01.3 SURVEY EM CURSO
|
| 4 |
+
EM CURSO,1,02.1 PROJETO PENDENTE CLIENTE
|
| 5 |
+
EM CURSO,1,02.2 PROJETO POR ADJUDICAR
|
| 6 |
+
EM CURSO,1,02.3 PROJETO EM CURSO
|
| 7 |
+
EM CURSO,1,03 POR INICIAR CQ
|
| 8 |
+
EM CURSO,1,03.1 CQ EM CURSO
|
| 9 |
+
EM CURSO,1,03.2 CQ TERMINADO
|
| 10 |
+
EM CURSO,1,03.3 CQ SOGETREL
|
| 11 |
+
EM CURSO,1,04 PRE VALIDA��O PROJETO
|
| 12 |
+
EM CURSO,1,05 SUIVI PROJETO
|
| 13 |
+
|
emcurso_d.csv
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TIPOS;SLA [dias];< 50 % [uni]; 50 % < X < 75 % [uni];75 % < X < 100 % [uni];> 100 % [uni];TOTAL
|
| 2 |
+
ART 2 3;30;;;;;
|
| 3 |
+
RAMI;30;;;;;
|
| 4 |
+
CUIVRE;30;;;;;
|
| 5 |
+
R�COLEMENTS;0;;;;;
|
| 6 |
+
PAR;10;;;;;
|
| 7 |
+
FIBRE;30;;;;;
|
| 8 |
+
RACCO;30;;;;;
|
| 9 |
+
IMMO;30;;;;;
|
| 10 |
+
DESSAT;10;;;;;
|
| 11 |
+
DISSIM - POI1;30;;;;;
|
| 12 |
+
DISSIM - POI2;15;;;;;
|
| 13 |
+
DISSIM - POI3;15;;;;;
|
| 14 |
+
DISSIM;30;;;;;
|
emcursoprojecto.csv
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
EM CURSO PROJETO;01 POR INICIAR SURVEY
|
| 2 |
+
EM CURSO PROJETO;01.1 SURVEY EM AGENDAMENTO
|
| 3 |
+
EM CURSO PROJETO;01.2 SURVEY PENDENTE CLIENTE
|
| 4 |
+
EM CURSO PROJETO;01.3 SURVEY EM CURSO
|
| 5 |
+
EM CURSO PROJETO;02 POR INICAR PROJETO
|
| 6 |
+
EM CURSO PROJETO;02.1 PROJETO PENDENTE CLIENTE
|
| 7 |
+
EM CURSO PROJETO;02.2 PROJETO POR ADJUDICAR
|
| 8 |
+
EM CURSO PROJETO;02.3 PROJETO EM CURSO
|
| 9 |
+
EM CURSO PROJETO;03 POR INICIAR CQ
|
| 10 |
+
EM CURSO PROJETO;03.1 CQ EM CURSO
|
| 11 |
+
EM CURSO PROJETO;03.2 CQ TERMINADO
|
| 12 |
+
EM CURSO PROJETO;03.3 CQ SOGETREL
|
| 13 |
+
EM CURSO PROJETO;04 PRE VALIDA��O PROJETO
|
| 14 |
+
EM CURSO PROJETO;05 SUIVI PROJETO
|
emcursoprojecto_d.csv
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TIPOS;SLA [dias];< 50 % [uni]; 50 % < X < 75 % [uni];75 % < X < 100 % [uni];> 100 % [uni];TOTAL
|
| 2 |
+
ART 2 3;30;;;;;
|
| 3 |
+
RAMI;30;;;;;
|
| 4 |
+
CUIVRE;30;;;;;
|
| 5 |
+
R�COLEMENTS;0;;;;;
|
| 6 |
+
PAR;10;;;;;
|
| 7 |
+
FIBRE;30;;;;;
|
| 8 |
+
RACCO;30;;;;;
|
| 9 |
+
IMMO;30;;;;;
|
| 10 |
+
DESSAT;10;;;;;
|
| 11 |
+
DISSIM - POI1;30;;;;;
|
| 12 |
+
DISSIM - POI2;15;;;;;
|
| 13 |
+
DISSIM - POI3;15;;;;;
|
| 14 |
+
DISSIM;30;;;;;
|
global.csv
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SITUA��O GLOBAL;STATUS
|
| 2 |
+
GLOBAL;01 POR INICIAR SURVEY
|
| 3 |
+
GLOBAL;01.1 SURVEY EM AGENDAMENTO
|
| 4 |
+
GLOBAL;01.2 SURVEY PENDENTE CLIENTE
|
| 5 |
+
GLOBAL;01.3 SURVEY EM CURSO
|
| 6 |
+
GLOBAL;01.4 SURVEY CANCELADO
|
| 7 |
+
GLOBAL;02 POR INICAR PROJETO
|
| 8 |
+
GLOBAL;02.1 PROJETO PENDENTE CLIENTE
|
| 9 |
+
GLOBAL;02.2 PROJETO POR ADJUDICAR
|
| 10 |
+
GLOBAL;02.3 PROJETO EM CURSO
|
| 11 |
+
GLOBAL;03 POR INICIAR CQ
|
| 12 |
+
GLOBAL;03.1 CQ EM CURSO
|
| 13 |
+
GLOBAL;03.2 CQ TERMINADO
|
| 14 |
+
GLOBAL;03.3 CQ SOGETREL
|
| 15 |
+
GLOBAL;04 PRE VALIDA��O PROJETO
|
| 16 |
+
GLOBAL;05 SUIVI PROJETO
|
| 17 |
+
GLOBAL;06 POR INICIAR LICENCIAMENTOS
|
| 18 |
+
GLOBAL;06.1 LICENCIAMENTO POR ADJUDICAR
|
| 19 |
+
GLOBAL;06.2 AGUARDA DEVIS
|
| 20 |
+
GLOBAL;06.3 DEVIS OK
|
| 21 |
+
GLOBAL;06.4 AGUARDA PMV+DT
|
| 22 |
+
GLOBAL;06.5 PMV + DT OK
|
| 23 |
+
GLOBAL;06.6 AGUARDA CRVT
|
| 24 |
+
GLOBAL;06.7 CRVT OK
|
| 25 |
+
GLOBAL;07 VALIDA��O ORANGE
|
| 26 |
+
GLOBAL;08 PROJETO VALIDADO
|
| 27 |
+
GLOBAL;09 TRABALHOS EM CURSO
|
| 28 |
+
GLOBAL;10 TRABALHOS TERMINADOS
|
| 29 |
+
GLOBAL;11 POR INICIAR CADASTRO
|
| 30 |
+
GLOBAL;11.1 AGUARDA RT
|
| 31 |
+
GLOBAL;11.2 CADASTRO POR ADJUDICAR
|
| 32 |
+
GLOBAL;11.3 CADASTRO EM CURSO
|
| 33 |
+
GLOBAL;11.4 CADASTRO TERMINADO
|
| 34 |
+
GLOBAL;11.5 CADASTRO VALIDADO
|
| 35 |
+
GLOBAL;12 CANCELADO
|
| 36 |
+
GLOBAL;13 FATURADO
|
| 37 |
+
GLOBAL;14 DOSSIER CONCLUIDO
|
global_d.csv
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TIPOS;SLA [dias];< 50 % [uni]; 50 % < X < 75 % [uni];75 % < X < 100 % [uni];> 100 % [uni];TOTAL
|
| 2 |
+
ART 2 3;30;;;;;
|
| 3 |
+
RAMI;30;;;;;
|
| 4 |
+
CUIVRE;30;;;;;
|
| 5 |
+
R�COLEMENTS;0;;;;;
|
| 6 |
+
PAR;10;;;;;
|
| 7 |
+
FIBRE;30;;;;;
|
| 8 |
+
RACCO;30;;;;;
|
| 9 |
+
IMMO;30;;;;;
|
| 10 |
+
DESSAT;10;;;;;
|
| 11 |
+
DISSIM - POI1;30;;;;;
|
| 12 |
+
DISSIM - POI2;15;;;;;
|
| 13 |
+
DISSIM - POI3;15;;;;;
|
| 14 |
+
DISSIM;30;;;;;
|
licenciamento.csv
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
LICENCIAMENTO;06 POR INICIAR LICENCIAMENTOS
|
| 2 |
+
LICENCIAMENTO;06.1 LICENCIAMENTO POR ADJUDICAR
|
| 3 |
+
LICENCIAMENTO;06.2 AGUARDA DEVIS
|
| 4 |
+
LICENCIAMENTO;06.3 DEVIS OK
|
| 5 |
+
LICENCIAMENTO;06.4 AGUARDA PMV+DT
|
| 6 |
+
LICENCIAMENTO;06.5 PMV + DT OK
|
| 7 |
+
LICENCIAMENTO;06.6 AGUARDA CRVT
|
| 8 |
+
LICENCIAMENTO;06.7 CRVT OK
|
| 9 |
+
LICENCIAMENTO;06.7 CRVT OK
|
licenciamento_d.csv
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TIPOS;SLA [dias];< 50 % [uni]; 50 % < X < 75 % [uni];75 % < X < 100 % [uni];> 100 % [uni];TOTAL
|
| 2 |
+
ART 2 3;30;;;;;
|
| 3 |
+
RAMI;30;;;;;
|
| 4 |
+
CUIVRE;30;;;;;
|
| 5 |
+
R�COLEMENTS;0;;;;;
|
| 6 |
+
PAR;10;;;;;
|
| 7 |
+
FIBRE;30;;;;;
|
| 8 |
+
RACCO;30;;;;;
|
| 9 |
+
IMMO;30;;;;;
|
| 10 |
+
DESSAT;10;;;;;
|
| 11 |
+
DISSIM - POI1;30;;;;;
|
| 12 |
+
DISSIM - POI2;15;;;;;
|
| 13 |
+
DISSIM - POI3;15;;;;;
|
| 14 |
+
DISSIM;30;;;;;
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio>=4.0.0
|
| 2 |
+
pandas
|
| 3 |
+
numpy
|
| 4 |
+
openai
|
tarefasss_datas_corrigidas_final.csv
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SUB-CIP;PROJETO;DESIGNA??O DO PROJETO;TIPO;RB STATUS;COLABORADOR;OBSERVA??ES;DATA_ADJ_CLIENTE;DATA_LIMITE_SLA;TEMPO_EXECU������O_[DIAS];% SLA;DATA DE ENTREGA V0;DATA 1? RE-ENTREGA V1;DATA 2? RE-ENTREGA V2;DATA 3? RE-ENTREGA V3;DATA DA VALIDA??O;Qtd de Vers?es;Taxa de Rejei??o VERS?ES;Taxa de Aceita??o VERS?ES;Qtd de Revis?es;?ndice de Qualidade;COLABORADOR PROD.;DATA INICIO;DATA FIM;COLABORADOR QC;DATA INICIO_1;DATA FIM_2;% ERROS VISUAIS;% ERROS MACRO,;TAXA APROVA??O INTERNA;COLABORADOR PROD._3;DATA INICIO_4;DATA FIM_5;COLABORADOR QC_6;DATA INICIO_7;DATA FIM_8;% ERROS VISUAIS_9;% ERROS MACRO,_10;TAXA ERRO GLOBAL;COLABORADOR PROD._11;DATA INICIO_12;DATA FIM_13;COLABORADOR QC_14;DATA INICIO_15;DATA FIM_16;% ERROS VISUAIS_17;% ERROS MACRO,_18;TAXA ERRO GLOBAL_19;Resposta Cliente;DATA ;COLABORADOR PROD._20;DATA INICIO_21;DATA FIM_22;COLABORADOR QC_23;DATA INICIO_24;DATA FIM_25;% ERROS VISUAIS_26;% ERROS MACRO,_27;TAXA ERRO GLOBAL_28;COLABORADOR PROD._29;DATA INICIO_30;DATA FIM_31;COLABORADOR QC_32;DATA INICIO_33;DATA FIM_34;% ERROS VISUAIS_35;% ERROS MACRO,_36;TAXA ERRO GLOBAL_37;COLABORADOR PROD._38;DATA INICIO_39;DATA FIM_40;COLABORADOR QC_41;DATA INICIO_42;DATA FIM_43;% ERROS VISUAIS_44;% ERROS MACRO,_45;TAXA ERRO GLOBAL_46;Resposta Cliente_47;DATE;COLABORADOR PROD._48;DATA INICIO_49;DATA FIM_50;COLABORADOR QC_51;DATA INICIO_52;DATA FIM_53;% ERROS VISUAIS_54;% ERROS MACRO,_55;TAXA ERRO GLOBAL_56;COLABORADOR PROD._57;DATA INICIO_58;DATA FIM_59;COLABORADOR QC_60;DATA INICIO_61;DATA FIM_62;% ERROS VISUAIS_63;% ERROS MACRO,_64;TAXA ERRO GLOBAL_65;DESIGN BY;START DATE;COMPLETE DATE;QC DONE BY;START DATE_66;COMPLETE DATE_67;% ERROS VISUAIS_68;% ERROS MACRO,_69;TAXA ERRO GLOBAL_70;Resposta Cliente_71;DATE_72;COLABORADOR PROD._73;DATA INICIO_74;DATA FIM_75;COLABORADOR QC_76;DATA INICIO_77;DATA FIM_78;% ERROS VISUAIS_79;% ERROS MACRO,_80;TAXA ERRO GLOBAL_81;COLABORADOR PROD._82;DATA INICIO_83;DATA FIM_84;COLABORADOR QC_85;DATA INICIO_86;DATA FIM_87;% ERROS VISUAIS_88;% ERROS MACRO,_89;TAXA ERRO GLOBAL_90;DESIGN BY_91;START DATE_92;COMPLETE DATE_93;QC DONE BY_94;START DATE_95;COMPLETE DATE_96;% ERROS VISUAIS_97;% ERROS MACRO,_98;TAXA ERRO GLOBAL_99;Resposta Cliente_100;DATE_101
|
| 2 |
+
RBPT.2026.006.001;PON600009;RF, DF, FU - 376 FO TVX NON FACT;CUIVRE;03 POR INICIAR CQ;BENCOST;"DRE: 19/03 // RECEBIDO DA BENCOST A 26/02
|
| 3 |
+
ADJUDICADO � BENCOST A 23/02";02/02/2026;04/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 4 |
+
RBPT.2026.006.002;BES600083;L, AO, 9I - 520_CM FIBRE PIM DA IMMO;RAMI;02.3 PROJETO EM CURSO;DIOGO SANTOS;DRE:17/03 // FAZER CRVT;02/02/2026;04/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 5 |
+
RBPT.2026.006.003;SCX600031;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;03.2 CQ TERMINADO;BENCOST;"DRE: 16/03 // RECEBIDO DA BENCOST A 24/02
|
| 6 |
+
ADJUDICADO � BENCOST A 23/02";03/02/2026;05/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 7 |
+
RBPT.2026.006.004;PON600013;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;"DRE: 20/03 // D�VIDA CARLOS
|
| 8 |
+
ADJUDICADO � BENCOST A 23/02";06/02/2026;08/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 9 |
+
RBPT.2026.006.005;STR600078;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE: 25/03 // ADJUDICADO � BENCOST A 26/02;06/02/2026;08/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 10 |
+
RBPT.2026.006.006;PON600014;RF, DF, FU - 376 FO TVX NON FACT;CUIVRE;03.2 CQ TERMINADO;BENCOST;"DRE: 18/03 // RECEBIDO DA BENCOST A 24/02
|
| 11 |
+
ADJUDICADO � BENCOST A 23/02";09/02/2026;11/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 12 |
+
RBPT.2026.006.007;SCX600033;RF, DF, FV - 376 FO TVX FACT;CUIVRE;02.3 PROJETO EM CURSO;BENCOST;DRE:20/03 // ADJUDICADO � BENCOST A 23/02;09/02/2026;11/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 13 |
+
RBPT.2026.006.008;STR600080 ;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE: 26/03;10/02/2026;12/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 14 |
+
RBPT.2026.006.009;STR600079 ;" L, CU, CL - 011 CU REHAUSSE TRAPPES";ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE: 26/03;10/02/2026;12/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 15 |
+
RBPT.2026.006.010;BES600112;RF, DF, FV - 376 FO TVX FACT;CUIVRE;01.2 SURVEY PENDENTE CLIENTE;;"FOMOS CONTACTADOS PELA SENHORA, QUE MANDOU FOTOS A 23/02
|
| 16 |
+
LIGAR A 23/02 ";13/02/2026;15/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 17 |
+
RBPT.2026.006.011;PON600023 ;RF, DF, FV - 376 FO TVX FACT;CUIVRE;03 POR INICIAR CQ;BENCOST;DRE: 20/03 // ADJUDICADO � BENCOST A 23/02;13/02/2026;15/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 18 |
+
RBPT.2026.006.012;LUN500077 ;L, AO, 9J - 070 TVX INFRA SITE CLIENT E;CUIVRE;03 POR INICIAR CQ;BENCOST;"DRE: 19/03 // RECEBIDO DA BENCOST A 02/03
|
| 19 |
+
ADJUDICADO � BENCOST A 23/02";13/02/2026;15/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 20 |
+
RBPT.2026.006.013;VAN600007;RF, DF, FV - 376 FO TVX FACT;CUIVRE;03 POR INICIAR CQ;BENCOST;"DRE: 25/03 // RECEBIDO DA BENCOST A 02/03
|
| 21 |
+
ADJUDICADO � BENCOST A 26/02";13/02/2026;15/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 22 |
+
RBPT.2026.006.014;BAR600041 ;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;01.3 SURVEY EM CURSO;;DRE: 27/03 // Vincent Schmitt 0667309869, podemos ir ao local sem agendamento, os trabalhos s� v�o come�ar na semana 11;13/02/2026;15/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 23 |
+
RBPT.2026.006.015;ALL600112 ;RF, DF, FU - 376 FO TVX NON FACT;CUIVRE;02.3 PROJETO EM CURSO;BENCOST;DRE: 23/02 // ADJUDICADO � BENCOST A 26/02;13/02/2026;15/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 24 |
+
RBPT.2026.006.016;MTZ600072 ;L, AO, RY - 070 CU NON FACTURE DPCMT OUVRAGE;CUIVRE;02.3 PROJETO EM CURSO;BENCOST;DRE: 24/03 // ADJUDICADO � BENCOST A 26/02;13/02/2026;15/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 25 |
+
RBPT.2026.006.017;PAM500021 ;L, AO, IU - #166#CMF COLONNE MONTANTE FIBRE;IMMO;02.3 PROJETO EM CURSO;BENCOST;"DRE: 26/03 // Cliente: 1� IDA - CRVT FEITO+ FOTOS E DATA DE TRAVAUX (2� E/OU 3� IDA, CRVT EM CADA IDA)
|
| 26 |
+
ADJUDICADO � BENCOST A 26/02";13/02/2026;15/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 27 |
+
RBPT.2026.006.018;BES600115 ;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;03.2 CQ TERMINADO;BENCOST;"DRE: 19/03 // RECEBIDO DA BENCOST A 24/02
|
| 28 |
+
ADJUDICADO � BENCOST A 23/02";16/02/2026;18/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 29 |
+
RBPT.2026.006.019;BAR400128;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE: 27/03 // URGENTE;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 30 |
+
RBPT.2026.006.020;BAR500078;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:27/03;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 31 |
+
RBPT.2026.006.021;BAR500143;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:27/03;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 32 |
+
RBPT.2026.006.022;LUN500074;L, CU, BJ - 011 CU COORDIN RECURRENT;ART 2 3;01.2 SURVEY PENDENTE CLIENTE;;PEDIDO PLANOS A 19/02 - DRE:19/03;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 33 |
+
RBPT.2026.006.023;MAX500180;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:26/03 // ADJUDICADO � BENCOST A 26/02;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 34 |
+
RBPT.2026.006.024;MAX600025;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:26/03 // ADJUDICADO � A 26/02;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 35 |
+
RBPT.2026.006.025;PAM500025;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:26/03 // PRIORIT�RIO // ADJUDICADO � BENCOST A 26/02;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 36 |
+
RBPT.2026.006.026;PAM500091;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;01.1 SURVEY EM AGENDAMENTO;;;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 37 |
+
RBPT.2026.006.027;PAM500107;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:26/03 // ADJUDICADO � BENCOST A 26/02;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 38 |
+
RBPT.2026.006.028;VAN400424;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:26/03 // ADJUDICADO � BENCOST A 26/02;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 39 |
+
RBPT.2026.006.029;VAN500356;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:27/03;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 40 |
+
RBPT.2026.006.030;VAN500427;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:23/03 // ADJUDICADO � BENCOST A 26/02;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 41 |
+
RBPT.2026.006.031;VER500025;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:24/03 // ADJUDICADO � BENCOST A 26/02;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 42 |
+
RBPT.2026.006.032;VER500028;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:26/03 // ADJUDICADO � BENCOST A 26/02;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 43 |
+
RBPT.2026.006.033;VER500057;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;02.3 PROJETO EM CURSO;BENCOST;DRE:26/03 // ADJUDICADO � BENCOST A 26/02;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 44 |
+
RBPT.2026.006.034;LUN600007;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;01.2 SURVEY PENDENTE CLIENTE;;PEDIDO PLANOS A 20/02 - DRE:20/03;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 45 |
+
RBPT.2026.006.035;LUN600009;L, CU, CH - 011 CU COORD ART 323-25;ART 2 3;01.2 SURVEY PENDENTE CLIENTE;;PEDIDO PLANOS A 20/02 - DRE:05/03;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 46 |
+
RBPT.2026.006.036;MTZ600223;L, AO, RY - 070 CU NON FACTURE DPCMT OUVRAGE;CUIVRE;01.2 SURVEY PENDENTE CLIENTE;;DRE: 27/02 //PEDIDO O CONTACTO A 20/02 PARA FAZERMOS VT // M�RIO TIROU AS FOTOS A 24/02;19/02/2026;21/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 47 |
+
RBPT.2026.006.037;SCX600045 ;RF, DF, FV - 376 FO TVX FACT;CUIVRE;01 POR INICIAR SURVEY;;;23/02/2026;25/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 48 |
+
RBPT.2026.006.038;ALL600164 ;RF, DF, FV - 376 FO TVX FACT;CUIVRE;01 POR INICIAR SURVEY;;;23/02/2026;25/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
| 49 |
+
RBPT.2026.006.039;SCX600051 ;RF, DF, FV - 376 FO TVX FACT;CUIVRE;01 POR INICIAR SURVEY;;;23/02/2026;25/03/2026;;0;;;;;;;;;0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
validado.csv
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FINALIZADO;08 PROJETO VALIDADO
|
| 2 |
+
FINALIZADO;11.5 CADASTRO VALIDADO
|
| 3 |
+
FINALIZADO;13 FATURADO
|
validado_d.csv
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TIPOS;SLA [dias];< 50 % [uni]; 50 % < X < 75 % [uni];75 % < X < 100 % [uni];> 100 % [uni];TOTAL
|
| 2 |
+
ART 2 3;30;;;;;
|
| 3 |
+
RAMI;30;;;;;
|
| 4 |
+
CUIVRE;30;;;;;
|
| 5 |
+
R�COLEMENTS;0;;;;;
|
| 6 |
+
PAR;10;;;;;
|
| 7 |
+
FIBRE;30;;;;;
|
| 8 |
+
RACCO;30;;;;;
|
| 9 |
+
IMMO;30;;;;;
|
| 10 |
+
DESSAT;10;;;;;
|
| 11 |
+
DISSIM - POI1;30;;;;;
|
| 12 |
+
DISSIM - POI2;15;;;;;
|
| 13 |
+
DISSIM - POI3;15;;;;;
|
| 14 |
+
DISSIM;30;;;;;
|