101 lines
3.9 KiB
Python
101 lines
3.9 KiB
Python
from django.db import models
|
|
|
|
|
|
class Grade(models.Model):
|
|
title = models.CharField(max_length=50)
|
|
icon = models.CharField(max_length=100)
|
|
color = models.CharField(max_length=10)
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
|
|
class Unit(models.Model):
|
|
grade = models.ForeignKey(Grade, on_delete=models.CASCADE, related_name='units')
|
|
title = models.CharField(max_length=100)
|
|
order = models.PositiveIntegerField(default=0)
|
|
|
|
class Meta:
|
|
ordering = ['order']
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
|
|
class Node(models.Model):
|
|
unit = models.ForeignKey(Unit, on_delete=models.CASCADE, related_name='nodes')
|
|
title = models.CharField(max_length=150)
|
|
order = models.PositiveIntegerField(default=0)
|
|
|
|
class Meta:
|
|
ordering = ['order']
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
|
|
class Lesson(models.Model):
|
|
node = models.ForeignKey(Node, on_delete=models.CASCADE, related_name='lessons')
|
|
title = models.CharField(max_length=150)
|
|
intro_text = models.TextField()
|
|
mascot_animation = models.CharField(max_length=50, default='explain')
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
|
|
class Question(models.Model):
|
|
TYPE_CHOICES = [('multiple_choice', 'چهارگزینهای'), ('count_objects', 'شمارش اشیاء')]
|
|
|
|
lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE, related_name='questions')
|
|
type = models.CharField(max_length=30, choices=TYPE_CHOICES)
|
|
prompt = models.TextField()
|
|
asset_group = models.CharField(max_length=100, blank=True)
|
|
|
|
def __str__(self):
|
|
return self.prompt[:50]
|
|
|
|
|
|
class Choice(models.Model):
|
|
"""
|
|
⚠️ اصلاح نسبت به سند اولیه: دیاگرام معماری فقط فیلد `value` رو برای
|
|
Choice مشخص کرده بود و فیلد تشخیص گزینهی درست (is_correct) رو نداشت
|
|
(این نکته توی سند models هم صراحتاً بهعنوان یک ایراد/نقص علامتگذاری
|
|
شده بود: "یا این فیلد سهواً جا افتاده یا تشخیص جای دیگهای انجام میشه").
|
|
چون بدون این فیلد امکان پیادهسازی POST /api/questions/{id}/answer
|
|
(که باید correct را برگردونه) وجود نداره، این فیلد اینجا اضافه شده.
|
|
اگه منطق تشخیص پاسخ درست قراره جای دیگهای (سرویس content-authoring)
|
|
انجام بشه، این فیلد رو میشه بعداً حذف/جابهجا کرد.
|
|
"""
|
|
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='choices')
|
|
value = models.CharField(max_length=100)
|
|
is_correct = models.BooleanField(default=False) # <-- اصلاح/افزوده شده
|
|
|
|
def __str__(self):
|
|
return self.value
|
|
|
|
|
|
class UserLessonCompletion(models.Model):
|
|
user_id = models.IntegerField(db_index=True) # ارجاع سبک به accounts.User
|
|
lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE, related_name='completions')
|
|
correct_count = models.PositiveIntegerField()
|
|
stars = models.PositiveSmallIntegerField()
|
|
xp_earned = models.PositiveIntegerField(default=0)
|
|
coins_earned = models.PositiveIntegerField(default=0)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
def __str__(self):
|
|
return f"completion-user{self.user_id}-lesson{self.lesson_id}"
|
|
|
|
|
|
class UserAnswer(models.Model):
|
|
user_id = models.IntegerField(db_index=True) # ارجاع سبک به accounts.User
|
|
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='answers')
|
|
choice = models.ForeignKey(Choice, on_delete=models.CASCADE, related_name='answers')
|
|
correct = models.BooleanField()
|
|
xp_earned = models.PositiveIntegerField(default=0)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
def __str__(self):
|
|
return f"answer-user{self.user_id}-q{self.question_id}"
|