File size: 11,878 Bytes
740c1f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515a788
740c1f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b3acf5d
740c1f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd7c45e
740c1f1
 
 
 
 
 
 
 
 
fd7c45e
740c1f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# database/db_manager.py

import sqlite3
import json
import logging
from datetime import datetime
from typing import Dict, List, Optional, Any
import os

# Configuração de logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class DatabaseManager:
    """Gerenciador central do banco de dados"""
    
    def __init__(self, db_path: str = 'revalida.db'):
        """Inicializa o gerenciador de banco de dados"""
        self.db_path = db_path
        self.conn = None
        self.initialize_database()

    def get_connection(self) -> sqlite3.Connection:
        """Obtém conexão com o banco de dados"""
        if not self.conn:
            self.conn = sqlite3.connect(self.db_path)
            self.conn.row_factory = sqlite3.Row
        return self.conn

    def initialize_database(self) -> None:
        """Inicializa todas as tabelas necessárias"""
        try:
            conn = self.get_connection()
            cursor = conn.cursor()

            # Tabela de Usuários
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS users (
                user_id TEXT PRIMARY KEY,
                name TEXT NOT NULL,
                email TEXT UNIQUE,
                registration_date DATE,
                target_date DATE,
                study_hours INTEGER,
                weak_areas TEXT,
                preferences TEXT
            )''')

            # Tabela de Progresso de Estudo
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS study_progress (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id TEXT,
                date DATE,
                topic TEXT,
                hours_studied FLOAT,
                questions_answered INTEGER,
                performance_score FLOAT,
                notes TEXT,
                FOREIGN KEY (user_id) REFERENCES users(user_id)
            )''')

            # Tabela de Questões Anteriores
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS previous_questions (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                year INTEGER,
                area TEXT,
                question_text TEXT,
                options TEXT,
                correct_answer TEXT,
                explanation TEXT,
                difficulty TEXT,
                ref_sources TEXT,
                tags TEXT,
                times_used INTEGER DEFAULT 0,
                success_rate FLOAT DEFAULT 0.0
            )''')

            # Tabela de Casos Clínicos
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS clinical_cases (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                title TEXT,
                area TEXT,
                difficulty TEXT,
                description TEXT,
                steps TEXT,
                expected_answers TEXT,
                hints TEXT,
                ref_sources TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )''')

            # Tabela de Simulados
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS simulados (
                id TEXT PRIMARY KEY,
                user_id TEXT,
                created_at TIMESTAMP,
                completed_at TIMESTAMP,
                difficulty TEXT,
                questions TEXT,
                answers TEXT,
                score FLOAT,
                time_taken INTEGER,
                analysis TEXT,
                FOREIGN KEY (user_id) REFERENCES users(user_id)
            )''')

            # Tabela de Sessões de Estudo
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS study_sessions (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id TEXT,
                start_time TIMESTAMP,
                end_time TIMESTAMP,
                topic TEXT,
                activity_type TEXT,
                productivity_score FLOAT,
                notes TEXT,
                FOREIGN KEY (user_id) REFERENCES users(user_id)
            )''')

            # Tabela de Metas e Objetivos
            cursor.execute('''
            CREATE TABLE IF NOT EXISTS goals (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id TEXT,
                goal_type TEXT,
                description TEXT,
                target_date DATE,
                progress FLOAT,
                status TEXT,
                priority INTEGER,
                FOREIGN KEY (user_id) REFERENCES users(user_id)
            )''')

            conn.commit()
            logger.info("Banco de dados inicializado com sucesso")

        except Exception as e:
            logger.error(f"Erro ao inicializar banco de dados: {e}")
            raise

    def load_questions_from_json(self, file_path: str) -> bool:
        """Carrega questões de um arquivo JSON"""
        try:
            if not os.path.exists(file_path):
                logger.error(f"Arquivo não encontrado: {file_path}")
                return False

            with open(file_path, 'r', encoding='utf-8') as f:
                questions = json.load(f)

            conn = self.get_connection()
            cursor = conn.cursor()

            for q in questions:
                cursor.execute('''
                INSERT OR IGNORE INTO previous_questions
                (year, area, question_text, options, correct_answer, 
                 explanation, difficulty, ref_sources, tags)  # <- Alterado aqui também
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                ''', (
                    q.get('year'),
                    q.get('area'),
                    q.get('question_text'),
                    json.dumps(q.get('options', {})),
                    q.get('correct_answer'),
                    q.get('explanation'),
                    q.get('difficulty', 'medium'),
                    json.dumps(q.get('references', [])),  # O nome no JSON pode continuar 'references'
                    json.dumps(q.get('tags', []))
                ))

            conn.commit()
            logger.info(f"Questões carregadas com sucesso de {file_path}")
            return True

        except Exception as e:
            logger.error(f"Erro ao carregar questões: {e}")
            return False

    def add_user(self, user_data: Dict[str, Any]) -> bool:
        """Adiciona ou atualiza usuário"""
        try:
            conn = self.get_connection()
            cursor = conn.cursor()

            cursor.execute('''
            INSERT OR REPLACE INTO users
            (user_id, name, email, registration_date, target_date, 
             study_hours, weak_areas, preferences)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            ''', (
                user_data['user_id'],
                user_data['name'],
                user_data.get('email'),
                user_data.get('registration_date', datetime.now().date()),
                user_data.get('target_date'),
                user_data.get('study_hours'),
                json.dumps(user_data.get('weak_areas', [])),
                json.dumps(user_data.get('preferences', {}))
            ))

            conn.commit()
            logger.info(f"Usuário {user_data['user_id']} adicionado/atualizado com sucesso")
            return True

        except Exception as e:
            logger.error(f"Erro ao adicionar usuário: {e}")
            return False

    def get_user_profile(self, user_id: str) -> Optional[Dict]:
        """Obtém perfil completo do usuário"""
        try:
            conn = self.get_connection()
            cursor = conn.cursor()

            # Dados básicos do usuário
            cursor.execute('SELECT * FROM users WHERE user_id = ?', (user_id,))
            user = cursor.fetchone()

            if not user:
                return None

            # Convertendo Row para dict
            user_dict = dict(user)
            
            # Decodificando JSON
            user_dict['weak_areas'] = json.loads(user_dict['weak_areas'])
            user_dict['preferences'] = json.loads(user_dict['preferences'])

            # Progresso recente
            cursor.execute('''
            SELECT topic, SUM(hours_studied) as total_hours,
                   AVG(performance_score) as avg_score
            FROM study_progress
            WHERE user_id = ?
            GROUP BY topic
            ''', (user_id,))
            
            user_dict['progress'] = {
                row['topic']: {
                    'hours': row['total_hours'],
                    'score': row['avg_score']
                }
                for row in cursor.fetchall()
            }

            # Metas ativas
            cursor.execute('''
            SELECT * FROM goals
            WHERE user_id = ? AND status != 'completed'
            ORDER BY priority DESC
            ''', (user_id,))
            
            user_dict['active_goals'] = [dict(row) for row in cursor.fetchall()]

            return user_dict

        except Exception as e:
            logger.error(f"Erro ao obter perfil do usuário: {e}")
            return None

    def update_study_progress(self, progress_data: Dict[str, Any]) -> bool:
        """Atualiza progresso de estudo"""
        try:
            conn = self.get_connection()
            cursor = conn.cursor()

            cursor.execute('''
            INSERT INTO study_progress
            (user_id, date, topic, hours_studied, 
             questions_answered, performance_score, notes)
            VALUES (?, ?, ?, ?, ?, ?, ?)
            ''', (
                progress_data['user_id'],
                progress_data.get('date', datetime.now().date()),
                progress_data['topic'],
                progress_data['hours_studied'],
                progress_data.get('questions_answered', 0),
                progress_data.get('performance_score', 0.0),
                progress_data.get('notes')
            ))

            conn.commit()
            logger.info(f"Progresso atualizado para usuário {progress_data['user_id']}")
            return True

        except Exception as e:
            logger.error(f"Erro ao atualizar progresso: {e}")
            return False

    def close(self):
        """Fecha conexão com o banco de dados"""
        if self.conn:
            self.conn.close()
            self.conn = None

    def __enter__(self):
        """Suporte para context manager"""
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """Fecha conexão ao sair do contexto"""
        self.close()

# Função de inicialização
def initialize_database(db_path: str = 'revalida.db') -> DatabaseManager:
    """Inicializa e retorna instância do gerenciador de banco de dados"""
    try:
        db_manager = DatabaseManager(db_path)
        return db_manager
    except Exception as e:
        logger.error(f"Erro ao inicializar gerenciador de banco de dados: {e}")
        raise

if __name__ == "__main__":
    # Código para testes
    try:
        db_manager = initialize_database('test_revalida.db')
        
        # Teste de inserção de usuário
        test_user = {
            'user_id': 'test123',
            'name': 'Teste User',
            'email': 'test@example.com',
            'study_hours': 4,
            'weak_areas': ['ClínicaMédica', 'Cirurgia']
        }
        
        success = db_manager.add_user(test_user)
        if success:
            print("Sistema de banco de dados funcionando corretamente")
            
            # Recuperar perfil para verificar
            profile = db_manager.get_user_profile('test123')
            print(f"Perfil recuperado: {json.dumps(profile, indent=2)}")
            
    except Exception as e:
        print(f"Erro nos testes: {e}")
    finally:
        db_manager.close()