SilvioLima commited on
Commit
cbfe84b
·
1 Parent(s): 1968945

Ajuste routers.py

Browse files
backend/services/tracking_service copy 2.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # backend/services/tracking_service.py
2
+ from sqlalchemy.orm import Session
3
+ from sqlalchemy import func
4
+ from database import TokenUsage, User
5
+ from datetime import datetime, timedelta
6
+ from faker_mock import USUARIOS_MOCK
7
+
8
+ # Mapeamento de IDs para nomes (inclui IDs antigos do banco para compatibilidade)
9
+ USER_NAME_LOOKUP = {u["id"]: u["name"] for u in USUARIOS_MOCK}
10
+ # IDs legados que podem estar no banco de dados
11
+ USER_NAME_LOOKUP.update({
12
+ "1": "Ana Silva",
13
+ "2": "Carlos D.",
14
+ "3": "Bento",
15
+ "4": "Silvio Lima"
16
+ })
17
+
18
+ class TrackingService:
19
+ @staticmethod
20
+ def log_usage(db: Session, user_id: int, model: str, prompt_tokens: int, completion_tokens: int, cost: float):
21
+ """Registra o uso de tokens e custos."""
22
+ total_tokens = prompt_tokens + completion_tokens
23
+ usage = TokenUsage(
24
+ user_id=user_id,
25
+ model=model,
26
+ prompt_tokens=prompt_tokens,
27
+ completion_tokens=completion_tokens,
28
+ total_tokens=total_tokens,
29
+ cost=cost,
30
+ timestamp=datetime.utcnow()
31
+ )
32
+ db.add(usage)
33
+ db.commit()
34
+ db.refresh(usage)
35
+ print(f"📊 Uso registrado: {model} | {total_tokens} tokens | ${cost:.6f}")
36
+ return usage
37
+
38
+ @staticmethod
39
+ def get_metrics(db: Session, days: int = 30):
40
+ """Retorna métricas agregadas para o dashboard do admin."""
41
+ thirty_days_ago = datetime.utcnow() - timedelta(days=days)
42
+
43
+ # Métricas de usuários
44
+ total_users = db.query(User).count()
45
+ admins = db.query(User).filter(User.role == "admin").count()
46
+ students = db.query(User).filter(User.role == "student").count()
47
+
48
+ # Métricas de tokens (últimos 30 dias)
49
+ token_metrics = db.query(TokenUsage).filter(
50
+ TokenUsage.timestamp >= thirty_days_ago
51
+ ).all()
52
+
53
+ total_tokens = sum(m.total_tokens for m in token_metrics) if token_metrics else 0
54
+ total_cost = sum(m.cost for m in token_metrics) if token_metrics else 0.0
55
+ total_requests = len(token_metrics)
56
+
57
+ # Métricas por modelo
58
+ model_usage = {}
59
+ for m in token_metrics:
60
+ model_usage[m.model] = model_usage.get(m.model, 0) + m.total_tokens
61
+
62
+ # Métricas por usuário
63
+ users_query = db.query(TokenUsage.user_id, func.sum(TokenUsage.cost), func.sum(TokenUsage.total_tokens)).group_by(TokenUsage.user_id).all()
64
+ db_users = {u.id: u.name for u in db.query(User).all()}
65
+ users_data = []
66
+ for u, c, t in users_query:
67
+ user_id = str(u)
68
+ user_name = db_users.get(u) or USER_NAME_LOOKUP.get(user_id) or f"Usuário {user_id[:8]}"
69
+ users_data.append({"user_id": user_id, "user_name": user_name, "cost": c, "tokens": t})
70
+
71
+ # Usuários recentes
72
+ recent_users = []
73
+ for u in db.query(User).order_by(User.created_at.desc()).limit(5).all():
74
+ recent_users.append({
75
+ "id": str(u.id),
76
+ "name": u.name,
77
+ "email": u.email,
78
+ "role": u.role,
79
+ "created_at": u.created_at.isoformat() if u.created_at else None
80
+ })
81
+
82
+ return {
83
+ "total_users": total_users,
84
+ "admins": admins,
85
+ "students": students,
86
+ "total_tokens": total_tokens,
87
+ "total_cost": total_cost,
88
+ "total_requests": total_requests,
89
+ "model_usage": model_usage,
90
+ "users_data": users_data,
91
+ "recent_users": recent_users
92
+ }
backend/services/tracking_service.py CHANGED
@@ -5,14 +5,11 @@ from database import TokenUsage, User
5
  from datetime import datetime, timedelta
6
  from faker_mock import USUARIOS_MOCK
7
 
8
- # Mapeamento de IDs para nomes (inclui IDs antigos do banco para compatibilidade)
9
  USER_NAME_LOOKUP = {u["id"]: u["name"] for u in USUARIOS_MOCK}
10
- # IDs legados que podem estar no banco de dados
11
  USER_NAME_LOOKUP.update({
12
- "1": "Ana Silva",
13
- "2": "Carlos D.",
14
- "3": "Bento",
15
- "4": "Silvio Lima"
16
  })
17
 
18
  class TrackingService:
@@ -32,7 +29,14 @@ class TrackingService:
32
  db.add(usage)
33
  db.commit()
34
  db.refresh(usage)
35
- print(f"📊 Uso registrado: {model} | {total_tokens} tokens | ${cost:.6f}")
 
 
 
 
 
 
 
36
  return usage
37
 
38
  @staticmethod
@@ -56,21 +60,61 @@ class TrackingService:
56
 
57
  # Métricas por modelo
58
  model_usage = {}
 
59
  for m in token_metrics:
60
  model_usage[m.model] = model_usage.get(m.model, 0) + m.total_tokens
 
 
 
 
 
 
 
 
 
61
 
62
- # Métricas por usuário
63
- users_query = db.query(TokenUsage.user_id, func.sum(TokenUsage.cost), func.sum(TokenUsage.total_tokens)).group_by(TokenUsage.user_id).all()
 
 
 
 
 
 
 
64
  db_users = {u.id: u.name for u in db.query(User).all()}
65
- users_data = []
66
- for u, c, t in users_query:
67
- user_id = str(u)
68
- user_name = db_users.get(u) or USER_NAME_LOOKUP.get(user_id) or f"Usuário {user_id[:8]}"
69
- users_data.append({"user_id": user_id, "user_name": user_name, "cost": c, "tokens": t})
70
 
71
- # Usuários recentes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  recent_users = []
73
- for u in db.query(User).order_by(User.created_at.desc()).limit(5).all():
74
  recent_users.append({
75
  "id": str(u.id),
76
  "name": u.name,
@@ -86,7 +130,8 @@ class TrackingService:
86
  "total_tokens": total_tokens,
87
  "total_cost": total_cost,
88
  "total_requests": total_requests,
89
- "model_usage": model_usage,
90
- "users_data": users_data,
 
91
  "recent_users": recent_users
92
  }
 
5
  from datetime import datetime, timedelta
6
  from faker_mock import USUARIOS_MOCK
7
 
 
8
  USER_NAME_LOOKUP = {u["id"]: u["name"] for u in USUARIOS_MOCK}
 
9
  USER_NAME_LOOKUP.update({
10
+ "1": "Administrador",
11
+ "2": "João Silva",
12
+ "3": "Maria Santos",
 
13
  })
14
 
15
  class TrackingService:
 
29
  db.add(usage)
30
  db.commit()
31
  db.refresh(usage)
32
+
33
+ # Log detalhado
34
+ print(f"📊 Uso registrado:")
35
+ print(f" - Usuário: {user_id}")
36
+ print(f" - Modelo: {model}")
37
+ print(f" - Tokens: {total_tokens} (prompt: {prompt_tokens}, completion: {completion_tokens})")
38
+ print(f" - Custo: ${cost:.6f}")
39
+
40
  return usage
41
 
42
  @staticmethod
 
60
 
61
  # Métricas por modelo
62
  model_usage = {}
63
+ model_cost = {}
64
  for m in token_metrics:
65
  model_usage[m.model] = model_usage.get(m.model, 0) + m.total_tokens
66
+ model_cost[m.model] = model_cost.get(m.model, 0) + m.cost
67
+
68
+ model_summary = []
69
+ for model in model_usage:
70
+ model_summary.append({
71
+ "model": model,
72
+ "tokens": model_usage[model],
73
+ "cost": model_cost.get(model, 0.0)
74
+ })
75
 
76
+ # Métricas por usuário (apenas alunos)
77
+ users_query = db.query(
78
+ TokenUsage.user_id,
79
+ func.sum(TokenUsage.total_tokens).label("total_tokens"),
80
+ func.sum(TokenUsage.cost).label("total_cost"),
81
+ func.count(TokenUsage.id).label("request_count")
82
+ ).group_by(TokenUsage.user_id).all()
83
+
84
+ # Busca nomes dos usuários
85
  db_users = {u.id: u.name for u in db.query(User).all()}
 
 
 
 
 
86
 
87
+ users_summary = []
88
+ for u in users_query:
89
+ user_id = str(u.user_id)
90
+ user_name = db_users.get(u.user_id) or USER_NAME_LOOKUP.get(user_id) or f"Usuário {user_id[:8]}"
91
+
92
+ # Verifica se é admin (não mostra em alunos)
93
+ user_role = db.query(User).filter(User.id == u.user_id).first()
94
+ if user_role and user_role.role == "student":
95
+ users_summary.append({
96
+ "user_id": user_id,
97
+ "user_name": user_name,
98
+ "tokens": int(u.total_tokens or 0),
99
+ "cost": float(u.total_cost or 0.0),
100
+ "requests": int(u.request_count or 0)
101
+ })
102
+
103
+ # Últimas requisições (para mostrar histórico)
104
+ recent_requests = []
105
+ for m in token_metrics[:10]: # Últimos 10 registros
106
+ user_name = db_users.get(m.user_id) or USER_NAME_LOOKUP.get(str(m.user_id), "Desconhecido")
107
+ recent_requests.append({
108
+ "user": user_name,
109
+ "model": m.model,
110
+ "tokens": m.total_tokens,
111
+ "cost": m.cost,
112
+ "timestamp": m.timestamp.isoformat() if m.timestamp else None
113
+ })
114
+
115
+ # Usuários recentes (apenas alunos)
116
  recent_users = []
117
+ for u in db.query(User).filter(User.role == "student").order_by(User.created_at.desc()).limit(5).all():
118
  recent_users.append({
119
  "id": str(u.id),
120
  "name": u.name,
 
130
  "total_tokens": total_tokens,
131
  "total_cost": total_cost,
132
  "total_requests": total_requests,
133
+ "model_summary": model_summary, # <-- NOVO: detalhes por modelo
134
+ "users_summary": users_summary, # <-- NOVO: detalhes por aluno
135
+ "recent_requests": recent_requests, # <-- NOVO: histórico de uso
136
  "recent_users": recent_users
137
  }
frontend/src/pages/AdminDashboard copy.jsx ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // frontend/src/pages/AdminDashboard.jsx
2
+ import { useState, useEffect } from 'react';
3
+ import { API_URL } from '../config';
4
+
5
+ function AdminDashboard() {
6
+ const [metrics, setMetrics] = useState(null);
7
+ const [loading, setLoading] = useState(true);
8
+ const [error, setError] = useState(null);
9
+
10
+ useEffect(() => {
11
+ const fetchMetrics = async () => {
12
+ try {
13
+ console.log('📊 Buscando métricas do admin...');
14
+ const response = await fetch(`${API_URL}/admin/metrics`);
15
+
16
+ if (!response.ok) {
17
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
18
+ }
19
+
20
+ const data = await response.json();
21
+ console.log('📊 Métricas recebidas:', data);
22
+ setMetrics(data);
23
+ setLoading(false);
24
+ } catch (err) {
25
+ console.error('❌ Erro ao carregar métricas:', err);
26
+ setError(err.message);
27
+ setLoading(false);
28
+ }
29
+ };
30
+
31
+ fetchMetrics();
32
+ }, []);
33
+
34
+ if (loading) {
35
+ return <div className="flex justify-center items-center h-64">Carregando...</div>;
36
+ }
37
+
38
+ if (error) {
39
+ return (
40
+ <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
41
+ <p className="font-bold">Erro</p>
42
+ <p>{error}</p>
43
+ <button
44
+ onClick={() => window.location.reload()}
45
+ className="mt-2 bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600"
46
+ >
47
+ Tentar novamente
48
+ </button>
49
+ </div>
50
+ );
51
+ }
52
+
53
+ if (!metrics) {
54
+ return <div>Nenhum dado disponível</div>;
55
+ }
56
+
57
+ return (
58
+ <div className="p-6">
59
+ <h2 className="text-2xl font-bold mb-6">📊 Dashboard Administrativo</h2>
60
+
61
+ {/* Cards de resumo */}
62
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
63
+ <div className="bg-blue-100 p-4 rounded-lg shadow">
64
+ <h3 className="text-sm text-gray-600">Total de Usuários</h3>
65
+ <p className="text-3xl font-bold text-blue-800">{metrics.total_users || 0}</p>
66
+ </div>
67
+ <div className="bg-green-100 p-4 rounded-lg shadow">
68
+ <h3 className="text-sm text-gray-600">Alunos</h3>
69
+ <p className="text-3xl font-bold text-green-800">{metrics.students || 0}</p>
70
+ </div>
71
+ <div className="bg-purple-100 p-4 rounded-lg shadow">
72
+ <h3 className="text-sm text-gray-600">Administradores</h3>
73
+ <p className="text-3xl font-bold text-purple-800">{metrics.admins || 0}</p>
74
+ </div>
75
+ <div className="bg-yellow-100 p-4 rounded-lg shadow">
76
+ <h3 className="text-sm text-gray-600">Tokens Utilizados</h3>
77
+ <p className="text-3xl font-bold text-yellow-800">{metrics.total_tokens || 0}</p>
78
+ </div>
79
+ </div>
80
+
81
+ {/* Custos */}
82
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
83
+ <div className="bg-red-50 p-4 rounded-lg shadow">
84
+ <h3 className="text-sm text-gray-600">💰 Custo Total</h3>
85
+ <p className="text-2xl font-bold text-red-800">${metrics.total_cost?.toFixed(4) || '0.0000'}</p>
86
+ <p className="text-sm text-gray-500">Últimos 30 dias</p>
87
+ </div>
88
+ <div className="bg-indigo-50 p-4 rounded-lg shadow">
89
+ <h3 className="text-sm text-gray-600">📞 Requisições</h3>
90
+ <p className="text-2xl font-bold text-indigo-800">{metrics.total_requests || 0}</p>
91
+ <p className="text-sm text-gray-500">Total de chamadas à API</p>
92
+ </div>
93
+ </div>
94
+
95
+ {/* Modelos usados */}
96
+ {metrics.model_summary && metrics.model_summary.length > 0 && (
97
+ <div className="bg-white p-4 rounded-lg shadow mb-6">
98
+ <h3 className="text-lg font-semibold mb-3">🤖 Modelos Utilizados</h3>
99
+ <div className="overflow-x-auto">
100
+ <table className="min-w-full">
101
+ <thead>
102
+ <tr className="border-b">
103
+ <th className="text-left py-2">Modelo</th>
104
+ <th className="text-right py-2">Tokens</th>
105
+ <th className="text-right py-2">Custo</th>
106
+ </tr>
107
+ </thead>
108
+ <tbody>
109
+ {metrics.model_summary.map((item, index) => (
110
+ <tr key={index} className="border-b hover:bg-gray-50">
111
+ <td className="py-2">{item.model}</td>
112
+ <td className="text-right py-2">{item.tokens}</td>
113
+ <td className="text-right py-2">${item.cost?.toFixed(4) || '0.0000'}</td>
114
+ </tr>
115
+ ))}
116
+ </tbody>
117
+ </table>
118
+ </div>
119
+ </div>
120
+ )}
121
+
122
+ {/* Uso por aluno */}
123
+ {metrics.users_summary && metrics.users_summary.length > 0 && (
124
+ <div className="bg-white p-4 rounded-lg shadow mb-6">
125
+ <h3 className="text-lg font-semibold mb-3">👨‍🎓 Consumo por Aluno</h3>
126
+ <div className="overflow-x-auto">
127
+ <table className="min-w-full">
128
+ <thead>
129
+ <tr className="border-b">
130
+ <th className="text-left py-2">Aluno</th>
131
+ <th className="text-right py-2">Requisições</th>
132
+ <th className="text-right py-2">Tokens</th>
133
+ <th className="text-right py-2">Custo</th>
134
+ </tr>
135
+ </thead>
136
+ <tbody>
137
+ {metrics.users_summary.map((user, index) => (
138
+ <tr key={index} className="border-b hover:bg-gray-50">
139
+ <td className="py-2">{user.user_name}</td>
140
+ <td className="text-right py-2">{user.requests || 0}</td>
141
+ <td className="text-right py-2">{user.tokens || 0}</td>
142
+ <td className="text-right py-2">${user.cost?.toFixed(4) || '0.0000'}</td>
143
+ </tr>
144
+ ))}
145
+ </tbody>
146
+ </table>
147
+ </div>
148
+ </div>
149
+ )}
150
+
151
+ {/* Histórico recente */}
152
+ {metrics.recent_requests && metrics.recent_requests.length > 0 && (
153
+ <div className="bg-white p-4 rounded-lg shadow mb-6">
154
+ <h3 className="text-lg font-semibold mb-3">🔄 Atividades Recentes</h3>
155
+ <div className="overflow-x-auto">
156
+ <table className="min-w-full">
157
+ <thead>
158
+ <tr className="border-b">
159
+ <th className="text-left py-2">Usuário</th>
160
+ <th className="text-left py-2">Modelo</th>
161
+ <th className="text-right py-2">Tokens</th>
162
+ <th className="text-right py-2">Custo</th>
163
+ <th className="text-left py-2">Data</th>
164
+ </tr>
165
+ </thead>
166
+ <tbody>
167
+ {metrics.recent_requests.map((req, index) => (
168
+ <tr key={index} className="border-b hover:bg-gray-50">
169
+ <td className="py-2">{req.user}</td>
170
+ <td className="py-2">{req.model}</td>
171
+ <td className="text-right py-2">{req.tokens}</td>
172
+ <td className="text-right py-2">${req.cost?.toFixed(4) || '0.0000'}</td>
173
+ <td className="py-2">{req.timestamp ? new Date(req.timestamp).toLocaleString() : '-'}</td>
174
+ </tr>
175
+ ))}
176
+ </tbody>
177
+ </table>
178
+ </div>
179
+ </div>
180
+ )}
181
+
182
+ {/* Usuários recentes */}
183
+ {metrics.recent_users && metrics.recent_users.length > 0 && (
184
+ <div className="bg-white p-4 rounded-lg shadow mb-6">
185
+ <h3 className="text-lg font-semibold mb-3">📋 Últimos Alunos Cadastrados</h3>
186
+ <ul className="divide-y divide-gray-200">
187
+ {metrics.recent_users.map((user, index) => (
188
+ <li key={index} className="py-2 flex justify-between">
189
+ <span className="font-medium">{user.name}</span>
190
+ <span className="text-gray-600">{user.email}</span>
191
+ <span className="text-sm text-gray-500">
192
+ {user.created_at ? new Date(user.created_at).toLocaleDateString() : ''}
193
+ </span>
194
+ </li>
195
+ ))}
196
+ </ul>
197
+ </div>
198
+ )}
199
+
200
+ {/* Debug: mostra dados brutos */}
201
+ <details className="mt-6">
202
+ <summary className="cursor-pointer text-gray-500 hover:text-gray-700">
203
+ 🔧 Dados brutos (debug)
204
+ </summary>
205
+ <pre className="mt-2 bg-gray-100 p-4 rounded text-xs overflow-auto max-h-96">
206
+ {JSON.stringify(metrics, null, 2)}
207
+ </pre>
208
+ </details>
209
+ </div>
210
+ );
211
+ }
212
+
213
+ export default AdminDashboard;