danielraynaud commited on
Commit
bcb4967
·
verified ·
1 Parent(s): 1252b41

Create models/question_models.py

Browse files
Files changed (1) hide show
  1. models/question_models.py +265 -0
models/question_models.py ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # models/question_models.py
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Dict, List, Optional
5
+ from datetime import datetime
6
+ import json
7
+
8
+ @dataclass
9
+ class Question:
10
+ """Modelo para questões do Revalida"""
11
+ id: int
12
+ text: str
13
+ options: Dict[str, str]
14
+ correct_answer: str
15
+ explanation: str
16
+ area: str
17
+ year: Optional[int] = None
18
+ difficulty: str = "medium"
19
+ tags: List[str] = field(default_factory=list)
20
+ references: List[str] = field(default_factory=list)
21
+ times_used: int = 0
22
+ success_rate: float = 0.0
23
+
24
+ def to_dict(self) -> Dict:
25
+ """Converte para dicionário"""
26
+ return {
27
+ "id": self.id,
28
+ "text": self.text,
29
+ "options": self.options,
30
+ "correct_answer": self.correct_answer,
31
+ "explanation": self.explanation,
32
+ "area": self.area,
33
+ "year": self.year,
34
+ "difficulty": self.difficulty,
35
+ "tags": self.tags,
36
+ "references": self.references,
37
+ "times_used": self.times_used,
38
+ "success_rate": self.success_rate
39
+ }
40
+
41
+ def format_for_display(self, show_answer: bool = False) -> str:
42
+ """Formata questão para exibição"""
43
+ formatted = f"📝 Questão {self.id}\n\n"
44
+ formatted += f"{self.text}\n\n"
45
+
46
+ for letra, texto in self.options.items():
47
+ formatted += f"{letra}) {texto}\n"
48
+
49
+ if show_answer:
50
+ formatted += f"\n✅ Resposta: {self.correct_answer}\n"
51
+ formatted += f"📋 Explicação: {self.explanation}\n"
52
+
53
+ if self.references:
54
+ formatted += "\n📚 Referências:\n"
55
+ for ref in self.references:
56
+ formatted += f"• {ref}\n"
57
+
58
+ return formatted
59
+
60
+ @dataclass
61
+ class ClinicalCase:
62
+ """Modelo para casos clínicos"""
63
+ id: int
64
+ title: str
65
+ description: str
66
+ area: str
67
+ steps: Dict[str, str]
68
+ expected_answers: Dict[str, str]
69
+ hints: Dict[str, List[str]]
70
+ difficulty: str = "medium"
71
+ references: List[str] = field(default_factory=list)
72
+ created_at: datetime = field(default_factory=datetime.now)
73
+
74
+ def to_dict(self) -> Dict:
75
+ """Converte para dicionário"""
76
+ return {
77
+ "id": self.id,
78
+ "title": self.title,
79
+ "description": self.description,
80
+ "area": self.area,
81
+ "steps": self.steps,
82
+ "expected_answers": self.expected_answers,
83
+ "hints": self.hints,
84
+ "difficulty": self.difficulty,
85
+ "references": self.references,
86
+ "created_at": self.created_at.isoformat()
87
+ }
88
+
89
+ def get_step(self, step_number: int) -> Optional[Dict[str, str]]:
90
+ """Retorna informações de uma etapa específica"""
91
+ step_key = str(step_number)
92
+ if step_key not in self.steps:
93
+ return None
94
+
95
+ return {
96
+ "description": self.steps[step_key],
97
+ "expected_answer": self.expected_answers.get(step_key, ""),
98
+ "hints": self.hints.get(step_key, [])
99
+ }
100
+
101
+ def format_step(self, step_number: int, show_answer: bool = False) -> str:
102
+ """Formata uma etapa do caso clínico para exibição"""
103
+ step = self.get_step(step_number)
104
+ if not step:
105
+ return "Etapa não encontrada."
106
+
107
+ formatted = f"🏥 Caso Clínico: {self.title}\n"
108
+ formatted += f"Etapa {step_number}\n\n"
109
+ formatted += f"{step['description']}\n"
110
+
111
+ if show_answer:
112
+ formatted += f"\n✅ Resposta esperada:\n{step['expected_answer']}\n"
113
+
114
+ if step['hints']:
115
+ formatted += "\n💡 Dicas:\n"
116
+ for hint in step['hints']:
117
+ formatted += f"• {hint}\n"
118
+
119
+ return formatted
120
+
121
+ @dataclass
122
+ class Simulado:
123
+ """Modelo para simulados"""
124
+ id: str
125
+ questions: List[Question]
126
+ difficulty: str
127
+ created_at: datetime = field(default_factory=datetime.now)
128
+ completed_at: Optional[datetime] = None
129
+ user_answers: Dict[int, str] = field(default_factory=dict)
130
+ score: Optional[float] = None
131
+ time_taken: Optional[int] = None
132
+ analysis: Dict = field(default_factory=dict)
133
+
134
+ def to_dict(self) -> Dict:
135
+ """Converte para dicionário"""
136
+ return {
137
+ "id": self.id,
138
+ "questions": [q.to_dict() for q in self.questions],
139
+ "difficulty": self.difficulty,
140
+ "created_at": self.created_at.isoformat(),
141
+ "completed_at": self.completed_at.isoformat() if self.completed_at else None,
142
+ "user_answers": self.user_answers,
143
+ "score": self.score,
144
+ "time_taken": self.time_taken,
145
+ "analysis": self.analysis
146
+ }
147
+
148
+ def submit_answer(self, question_id: int, answer: str) -> bool:
149
+ """Registra resposta do usuário"""
150
+ if not any(q.id == question_id for q in self.questions):
151
+ return False
152
+
153
+ self.user_answers[question_id] = answer
154
+ return True
155
+
156
+ def calculate_score(self) -> float:
157
+ """Calcula pontuação do simulado"""
158
+ if not self.questions or not self.user_answers:
159
+ return 0.0
160
+
161
+ correct = sum(
162
+ 1 for q in self.questions
163
+ if q.id in self.user_answers and
164
+ q.correct_answer.upper() == self.user_answers[q.id].upper()
165
+ )
166
+
167
+ return (correct / len(self.questions)) * 100
168
+
169
+ def generate_analysis(self) -> Dict:
170
+ """Gera análise detalhada do desempenho"""
171
+ analysis = {
172
+ "total_questions": len(self.questions),
173
+ "answered_questions": len(self.user_answers),
174
+ "score": self.calculate_score(),
175
+ "performance_by_area": {},
176
+ "weak_areas": [],
177
+ "recommendations": []
178
+ }
179
+
180
+ # Análise por área
181
+ area_stats = {}
182
+ for question in self.questions:
183
+ if question.area not in area_stats:
184
+ area_stats[question.area] = {"total": 0, "correct": 0}
185
+
186
+ area_stats[question.area]["total"] += 1
187
+ if (question.id in self.user_answers and
188
+ question.correct_answer.upper() == self.user_answers[question.id].upper()):
189
+ area_stats[question.area]["correct"] += 1
190
+
191
+ # Calcula percentuais e identifica áreas fracas
192
+ for area, stats in area_stats.items():
193
+ percentage = (stats["correct"] / stats["total"]) * 100
194
+ analysis["performance_by_area"][area] = {
195
+ "total": stats["total"],
196
+ "correct": stats["correct"],
197
+ "percentage": percentage
198
+ }
199
+
200
+ if percentage < 60:
201
+ analysis["weak_areas"].append(area)
202
+
203
+ # Gera recomendações
204
+ if analysis["weak_areas"]:
205
+ analysis["recommendations"].append(
206
+ "Revisar os seguintes tópicos: " + ", ".join(analysis["weak_areas"])
207
+ )
208
+
209
+ if analysis["score"] < 70:
210
+ analysis["recommendations"].append(
211
+ "Aumentar a quantidade de questões práticas"
212
+ )
213
+
214
+ return analysis
215
+
216
+ def load_question_from_dict(data: Dict) -> Question:
217
+ """Cria instância de Question a partir de dicionário"""
218
+ return Question(
219
+ id=data["id"],
220
+ text=data["text"],
221
+ options=data["options"],
222
+ correct_answer=data["correct_answer"],
223
+ explanation=data["explanation"],
224
+ area=data["area"],
225
+ year=data.get("year"),
226
+ difficulty=data.get("difficulty", "medium"),
227
+ tags=data.get("tags", []),
228
+ references=data.get("references", []),
229
+ times_used=data.get("times_used", 0),
230
+ success_rate=data.get("success_rate", 0.0)
231
+ )
232
+
233
+ def load_clinical_case_from_dict(data: Dict) -> ClinicalCase:
234
+ """Cria instância de ClinicalCase a partir de dicionário"""
235
+ return ClinicalCase(
236
+ id=data["id"],
237
+ title=data["title"],
238
+ description=data["description"],
239
+ area=data["area"],
240
+ steps=data["steps"],
241
+ expected_answers=data["expected_answers"],
242
+ hints=data["hints"],
243
+ difficulty=data.get("difficulty", "medium"),
244
+ references=data.get("references", []),
245
+ created_at=datetime.fromisoformat(data["created_at"])
246
+ if "created_at" in data else datetime.now()
247
+ )
248
+
249
+ if __name__ == "__main__":
250
+ # Testes básicos
251
+ test_question = Question(
252
+ id=1,
253
+ text="Qual é o principal sintoma da hipertensão?",
254
+ options={
255
+ "A": "Dor de cabeça",
256
+ "B": "Tontura",
257
+ "C": "Náusea",
258
+ "D": "Assintomático"
259
+ },
260
+ correct_answer="D",
261
+ explanation="A hipertensão é frequentemente assintomática...",
262
+ area="ClínicaMédica"
263
+ )
264
+
265
+ print(test_question.format_for_display(show_answer=True))