danielraynaud commited on
Commit
740c1f1
·
verified ·
1 Parent(s): be9b915

Create database/db_manager.py

Browse files
Files changed (1) hide show
  1. database/db_manager.py +348 -0
database/db_manager.py ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # database/db_manager.py
2
+
3
+ import sqlite3
4
+ import json
5
+ import logging
6
+ from datetime import datetime
7
+ from typing import Dict, List, Optional, Any
8
+ import os
9
+
10
+ # Configuração de logging
11
+ logging.basicConfig(
12
+ level=logging.INFO,
13
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
14
+ )
15
+ logger = logging.getLogger(__name__)
16
+
17
+ class DatabaseManager:
18
+ """Gerenciador central do banco de dados"""
19
+
20
+ def __init__(self, db_path: str = 'revalida.db'):
21
+ """Inicializa o gerenciador de banco de dados"""
22
+ self.db_path = db_path
23
+ self.conn = None
24
+ self.initialize_database()
25
+
26
+ def get_connection(self) -> sqlite3.Connection:
27
+ """Obtém conexão com o banco de dados"""
28
+ if not self.conn:
29
+ self.conn = sqlite3.connect(self.db_path)
30
+ self.conn.row_factory = sqlite3.Row
31
+ return self.conn
32
+
33
+ def initialize_database(self) -> None:
34
+ """Inicializa todas as tabelas necessárias"""
35
+ try:
36
+ conn = self.get_connection()
37
+ cursor = conn.cursor()
38
+
39
+ # Tabela de Usuários
40
+ cursor.execute('''
41
+ CREATE TABLE IF NOT EXISTS users (
42
+ user_id TEXT PRIMARY KEY,
43
+ name TEXT NOT NULL,
44
+ email TEXT UNIQUE,
45
+ registration_date DATE,
46
+ target_date DATE,
47
+ study_hours INTEGER,
48
+ weak_areas TEXT,
49
+ preferences TEXT
50
+ )''')
51
+
52
+ # Tabela de Progresso de Estudo
53
+ cursor.execute('''
54
+ CREATE TABLE IF NOT EXISTS study_progress (
55
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
56
+ user_id TEXT,
57
+ date DATE,
58
+ topic TEXT,
59
+ hours_studied FLOAT,
60
+ questions_answered INTEGER,
61
+ performance_score FLOAT,
62
+ notes TEXT,
63
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
64
+ )''')
65
+
66
+ # Tabela de Questões Anteriores
67
+ cursor.execute('''
68
+ CREATE TABLE IF NOT EXISTS previous_questions (
69
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
70
+ year INTEGER,
71
+ area TEXT,
72
+ question_text TEXT,
73
+ options TEXT,
74
+ correct_answer TEXT,
75
+ explanation TEXT,
76
+ difficulty TEXT,
77
+ references TEXT,
78
+ tags TEXT,
79
+ times_used INTEGER DEFAULT 0,
80
+ success_rate FLOAT DEFAULT 0.0
81
+ )''')
82
+
83
+ # Tabela de Casos Clínicos
84
+ cursor.execute('''
85
+ CREATE TABLE IF NOT EXISTS clinical_cases (
86
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
87
+ title TEXT,
88
+ area TEXT,
89
+ difficulty TEXT,
90
+ description TEXT,
91
+ steps TEXT,
92
+ expected_answers TEXT,
93
+ hints TEXT,
94
+ references TEXT,
95
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
96
+ )''')
97
+
98
+ # Tabela de Simulados
99
+ cursor.execute('''
100
+ CREATE TABLE IF NOT EXISTS simulados (
101
+ id TEXT PRIMARY KEY,
102
+ user_id TEXT,
103
+ created_at TIMESTAMP,
104
+ completed_at TIMESTAMP,
105
+ difficulty TEXT,
106
+ questions TEXT,
107
+ answers TEXT,
108
+ score FLOAT,
109
+ time_taken INTEGER,
110
+ analysis TEXT,
111
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
112
+ )''')
113
+
114
+ # Tabela de Sessões de Estudo
115
+ cursor.execute('''
116
+ CREATE TABLE IF NOT EXISTS study_sessions (
117
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
118
+ user_id TEXT,
119
+ start_time TIMESTAMP,
120
+ end_time TIMESTAMP,
121
+ topic TEXT,
122
+ activity_type TEXT,
123
+ productivity_score FLOAT,
124
+ notes TEXT,
125
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
126
+ )''')
127
+
128
+ # Tabela de Metas e Objetivos
129
+ cursor.execute('''
130
+ CREATE TABLE IF NOT EXISTS goals (
131
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
132
+ user_id TEXT,
133
+ goal_type TEXT,
134
+ description TEXT,
135
+ target_date DATE,
136
+ progress FLOAT,
137
+ status TEXT,
138
+ priority INTEGER,
139
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
140
+ )''')
141
+
142
+ conn.commit()
143
+ logger.info("Banco de dados inicializado com sucesso")
144
+
145
+ except Exception as e:
146
+ logger.error(f"Erro ao inicializar banco de dados: {e}")
147
+ raise
148
+
149
+ def load_questions_from_json(self, file_path: str) -> bool:
150
+ """Carrega questões de um arquivo JSON"""
151
+ try:
152
+ if not os.path.exists(file_path):
153
+ logger.error(f"Arquivo não encontrado: {file_path}")
154
+ return False
155
+
156
+ with open(file_path, 'r', encoding='utf-8') as f:
157
+ questions = json.load(f)
158
+
159
+ conn = self.get_connection()
160
+ cursor = conn.cursor()
161
+
162
+ for q in questions:
163
+ cursor.execute('''
164
+ INSERT OR IGNORE INTO previous_questions
165
+ (year, area, question_text, options, correct_answer,
166
+ explanation, difficulty, references, tags)
167
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
168
+ ''', (
169
+ q.get('year'),
170
+ q.get('area'),
171
+ q.get('question_text'),
172
+ json.dumps(q.get('options', {})),
173
+ q.get('correct_answer'),
174
+ q.get('explanation'),
175
+ q.get('difficulty', 'medium'),
176
+ json.dumps(q.get('references', [])),
177
+ json.dumps(q.get('tags', []))
178
+ ))
179
+
180
+ conn.commit()
181
+ logger.info(f"Questões carregadas com sucesso de {file_path}")
182
+ return True
183
+
184
+ except Exception as e:
185
+ logger.error(f"Erro ao carregar questões: {e}")
186
+ return False
187
+
188
+ def add_user(self, user_data: Dict[str, Any]) -> bool:
189
+ """Adiciona ou atualiza usuário"""
190
+ try:
191
+ conn = self.get_connection()
192
+ cursor = conn.cursor()
193
+
194
+ cursor.execute('''
195
+ INSERT OR REPLACE INTO users
196
+ (user_id, name, email, registration_date, target_date,
197
+ study_hours, weak_areas, preferences)
198
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
199
+ ''', (
200
+ user_data['user_id'],
201
+ user_data['name'],
202
+ user_data.get('email'),
203
+ user_data.get('registration_date', datetime.now().date()),
204
+ user_data.get('target_date'),
205
+ user_data.get('study_hours'),
206
+ json.dumps(user_data.get('weak_areas', [])),
207
+ json.dumps(user_data.get('preferences', {}))
208
+ ))
209
+
210
+ conn.commit()
211
+ logger.info(f"Usuário {user_data['user_id']} adicionado/atualizado com sucesso")
212
+ return True
213
+
214
+ except Exception as e:
215
+ logger.error(f"Erro ao adicionar usuário: {e}")
216
+ return False
217
+
218
+ def get_user_profile(self, user_id: str) -> Optional[Dict]:
219
+ """Obtém perfil completo do usuário"""
220
+ try:
221
+ conn = self.get_connection()
222
+ cursor = conn.cursor()
223
+
224
+ # Dados básicos do usuário
225
+ cursor.execute('SELECT * FROM users WHERE user_id = ?', (user_id,))
226
+ user = cursor.fetchone()
227
+
228
+ if not user:
229
+ return None
230
+
231
+ # Convertendo Row para dict
232
+ user_dict = dict(user)
233
+
234
+ # Decodificando JSON
235
+ user_dict['weak_areas'] = json.loads(user_dict['weak_areas'])
236
+ user_dict['preferences'] = json.loads(user_dict['preferences'])
237
+
238
+ # Progresso recente
239
+ cursor.execute('''
240
+ SELECT topic, SUM(hours_studied) as total_hours,
241
+ AVG(performance_score) as avg_score
242
+ FROM study_progress
243
+ WHERE user_id = ?
244
+ GROUP BY topic
245
+ ''', (user_id,))
246
+
247
+ user_dict['progress'] = {
248
+ row['topic']: {
249
+ 'hours': row['total_hours'],
250
+ 'score': row['avg_score']
251
+ }
252
+ for row in cursor.fetchall()
253
+ }
254
+
255
+ # Metas ativas
256
+ cursor.execute('''
257
+ SELECT * FROM goals
258
+ WHERE user_id = ? AND status != 'completed'
259
+ ORDER BY priority DESC
260
+ ''', (user_id,))
261
+
262
+ user_dict['active_goals'] = [dict(row) for row in cursor.fetchall()]
263
+
264
+ return user_dict
265
+
266
+ except Exception as e:
267
+ logger.error(f"Erro ao obter perfil do usuário: {e}")
268
+ return None
269
+
270
+ def update_study_progress(self, progress_data: Dict[str, Any]) -> bool:
271
+ """Atualiza progresso de estudo"""
272
+ try:
273
+ conn = self.get_connection()
274
+ cursor = conn.cursor()
275
+
276
+ cursor.execute('''
277
+ INSERT INTO study_progress
278
+ (user_id, date, topic, hours_studied,
279
+ questions_answered, performance_score, notes)
280
+ VALUES (?, ?, ?, ?, ?, ?, ?)
281
+ ''', (
282
+ progress_data['user_id'],
283
+ progress_data.get('date', datetime.now().date()),
284
+ progress_data['topic'],
285
+ progress_data['hours_studied'],
286
+ progress_data.get('questions_answered', 0),
287
+ progress_data.get('performance_score', 0.0),
288
+ progress_data.get('notes')
289
+ ))
290
+
291
+ conn.commit()
292
+ logger.info(f"Progresso atualizado para usuário {progress_data['user_id']}")
293
+ return True
294
+
295
+ except Exception as e:
296
+ logger.error(f"Erro ao atualizar progresso: {e}")
297
+ return False
298
+
299
+ def close(self):
300
+ """Fecha conexão com o banco de dados"""
301
+ if self.conn:
302
+ self.conn.close()
303
+ self.conn = None
304
+
305
+ def __enter__(self):
306
+ """Suporte para context manager"""
307
+ return self
308
+
309
+ def __exit__(self, exc_type, exc_val, exc_tb):
310
+ """Fecha conexão ao sair do contexto"""
311
+ self.close()
312
+
313
+ # Função de inicialização
314
+ def initialize_database(db_path: str = 'revalida.db') -> DatabaseManager:
315
+ """Inicializa e retorna instância do gerenciador de banco de dados"""
316
+ try:
317
+ db_manager = DatabaseManager(db_path)
318
+ return db_manager
319
+ except Exception as e:
320
+ logger.error(f"Erro ao inicializar gerenciador de banco de dados: {e}")
321
+ raise
322
+
323
+ if __name__ == "__main__":
324
+ # Código para testes
325
+ try:
326
+ db_manager = initialize_database('test_revalida.db')
327
+
328
+ # Teste de inserção de usuário
329
+ test_user = {
330
+ 'user_id': 'test123',
331
+ 'name': 'Teste User',
332
+ 'email': '[email protected]',
333
+ 'study_hours': 4,
334
+ 'weak_areas': ['ClínicaMédica', 'Cirurgia']
335
+ }
336
+
337
+ success = db_manager.add_user(test_user)
338
+ if success:
339
+ print("Sistema de banco de dados funcionando corretamente")
340
+
341
+ # Recuperar perfil para verificar
342
+ profile = db_manager.get_user_profile('test123')
343
+ print(f"Perfil recuperado: {json.dumps(profile, indent=2)}")
344
+
345
+ except Exception as e:
346
+ print(f"Erro nos testes: {e}")
347
+ finally:
348
+ db_manager.close()