cert-study-app / models.py
Kentlo's picture
์ค‘๋ณต๋‹ต๋ณ€ ๊ฐ€๋Šฅ ๋ฒ„์ „
08348fb
# ==============================================
# models.py (v2025-final)
# ==============================================
from sqlalchemy import Column, Integer, String, Text, Boolean, ForeignKey
from sqlalchemy.orm import relationship
import json
# db.py์—์„œ ์ƒ์„ฑํ•œ Base ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
from db import Base
# ----------------------------------------------
# Question ๋ชจ๋ธ
# ----------------------------------------------
class Question(Base):
__tablename__ = "questions"
id = Column(Integer, primary_key=True, autoincrement=True)
# ๋ฌธ์ œ ์งˆ๋ฌธ (๊ธฐ์กด ์ด๋ฆ„ ์œ ์ง€)
stem = Column(Text, nullable=False)
# ์ •๋‹ต (๋‹จ์ˆœ ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€๊ฒฝ๋˜์–ด๋„ "A", "B" ๋˜๋Š” "1", "2" ๊ฐ™์€ ์ธ๋ฑ์Šค ํ‚ค ์ €์žฅ)
answer = Column(String(255))
# ํ•ด์„ค
explanation = Column(Text)
# ๋ฌธ์ œ ์œ ํ˜• (MCQ: ๊ฐ๊ด€์‹)
question_type = Column(String(50), default="MCQ")
# ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ
page = Column(Integer)
category = Column(String(100))
subcategory = Column(String(100))
code = Column(Text)
source = Column(String(255))
# -------------------------------------------------------
# [๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๋ณ€๊ฒฝ ํ•ต์‹ฌ]
# ๊ธฐ์กด: [{"key": "A", "text": "๋‚ด์šฉ"}]
# ๋ณ€๊ฒฝ: ["๋‚ด์šฉ1", "๋‚ด์šฉ2", "๋‚ด์šฉ3"] (๋‹จ์ˆœ ๋ฌธ์ž์—ด ๋ฆฌ์ŠคํŠธ)
# -------------------------------------------------------
options_json = Column(Text, default="[]")
pairs = Column(Text, default="{}")
sequence = Column(Text, default="[]")
# -------------------------------------------------------
# Helper Methods (JSON ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™”)
# -------------------------------------------------------
def set_options(self, options):
"""
๋ฆฌ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
Input ์˜ˆ์‹œ: ["EC2", "S3", "Lambda"]
"""
try:
if options is None:
self.options_json = "[]"
elif isinstance(options, (list, dict)):
# ensure_ascii=False: ํ•œ๊ธ€ ๊นจ์ง ๋ฐฉ์ง€
self.options_json = json.dumps(options, ensure_ascii=False)
else:
# ๋ฌธ์ž์—ด๋กœ ๋“ค์–ด์˜ค๋ฉด ๊ทธ๋Œ€๋กœ ์ €์žฅ ์‹œ๋„ ํ˜น์€ ๋ฆฌ์ŠคํŠธ๋กœ ๊ฐ์‹ธ๊ธฐ
self.options_json = json.dumps([str(options)], ensure_ascii=False)
except Exception as e:
print(f"Error setting options: {e}")
self.options_json = "[]"
def get_options(self):
"""
์ €์žฅ๋œ JSON ๋ฌธ์ž์—ด์„ ํŒŒ์ด์ฌ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
Return ์˜ˆ์‹œ: ["EC2", "S3", "Lambda"]
"""
try:
if not self.options_json:
return []
return json.loads(self.options_json)
except Exception:
return []
# (์ถ”๊ฐ€) pairs, sequence ์ฒ˜๋ฆฌ๋„ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์•ˆ์ •์„ฑ ํ™•๋ณด
def get_pairs(self):
try:
return json.loads(self.pairs) if self.pairs else {}
except:
return {}
def get_sequence(self):
try:
return json.loads(self.sequence) if self.sequence else []
except:
return []
def __repr__(self):
return f"<Question id={self.id} type={self.question_type} stem={self.stem[:20]}...>"
# ----------------------------------------------
# Attempt ๋ชจ๋ธ (์‚ฌ์šฉ์ž ํ’€์ด ๊ธฐ๋ก)
# ----------------------------------------------
class Attempt(Base):
__tablename__ = "attempts"
id = Column(Integer, primary_key=True, autoincrement=True)
# ์‚ฌ์šฉ์ž ID (๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์ด ์—†๋‹ค๋ฉด guest ํ˜น์€ ๋ธŒ๋ผ์šฐ์ € ์ง€๋ฌธ ๋“ฑ ์‚ฌ์šฉ)
user_id = Column(String(100), nullable=False, default="guest")
# ๋ฌธ์ œ ID (FK)
question_id = Column(Integer, ForeignKey("questions.id"))
# ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ๋‹ต (ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์ƒ์„ฑํ•œ Key: "A", "B", "1" ๋“ฑ)
chosen = Column(String(10))
# ์ •๋‹ต ์—ฌ๋ถ€
correct = Column(Boolean, default=False)
# ์˜ค๋‹ต ๋…ธํŠธ ์œ ํ˜• (wrong: ํ‹€๋ฆผ, bookmark: ์ค‘์š” ํ‘œ์‹œ ๋“ฑ)
note_type = Column(String(20), default="wrong")
# ๊ด€๊ณ„ ์„ค์ • (Attempt.question ์œผ๋กœ ๋ฌธ์ œ ์ •๋ณด ์ ‘๊ทผ ๊ฐ€๋Šฅ)
question = relationship("Question", backref="attempts")
def __repr__(self):
return f"<Attempt q={self.question_id}, user={self.user_id}, correct={self.correct}>"