Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Assessments app Initial model structure #2

Open
wants to merge 2 commits into
base: features/assessments-app
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/assessments/constants/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
HTK_ASSESSMENTS_ASSESSMENT_MODEL = 'htk.Assessment'
HTK_ASSESSMENTS_QUESTION_MODEL = 'htk.Question'
HTK_ASSESSMENTS_QUESTION_CHOICE_MODEL = 'htk.QuestionChoice'
HTK_ASSESSMENTS_ANSWER_MODEL = 'htk.Answer'
Empty file.
22 changes: 22 additions & 0 deletions apps/assessments/models/answer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Django Imports
from django.db import models

# HTK Imports
from htk.apps.assessments.models.fk_fields import (
fk_question,
fk_question_choice,
)
from htk.models.classes import HtkBaseModel
from htk.models.fk_fields import fk_user


DEFAULT_RELATED_NAME = 'answers'


class Answer(HtkBaseModel):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class Assessment:
    @property
    def max_score(self):
        return self.questions.count()


class AssessmentQuestion:
    assessment: fk(Assesssment)


class AssessmentResponse
     assessment: fk
     user: fk

    @property
    def score(self):
        score = len([response for response in self.responses.all() if response.is_correct])
        return score

    @property
    def display_score(self):
        return f'{self.score} / {self.assessment.max_score}'

    @property
    def score_percentage(self):
        return self.score / self.assessment.max_score * 100


class AssessmentQuestionCorrectResponse:  # or `AnswerKey`?
     assessment: fk
     question(_id): fk, related_name="answer_key", unique=True  # (or maybe OneToOneField?)
     answer_text: str(correct answer)
     answer_choices: M2M(AnswerChoice)


class AssessmentQuestionResponse:
    assessment_response: fk(AssessmentResponse, related_name="responses")
    question: fk(the question this response is for)
    answer_text: str(user's text)
    answer_choices: M2M(AnswerChoice)

    def __init__(self, assessment_response):
        pass

    @property
    def is_correct(self):
         is_correct = True  # innocent until proven guilty

         answer_key = self.question.answer_key
         
         if is_correct and answer_key.answer_text != self.answer_text:
            is_correct = False

         if is_correct and answer_key.answer_choices != self.answer_choices:
            is_correct = False

        return is_correct

user = fk_user(DEFAULT_RELATED_NAME, required=True)
question = fk_question(DEFAULT_RELATED_NAME, required=True)
choice = fk_question_choice(DEFAULT_RELATED_NAME)

class Meta:
verbose_name = 'Assessment Question Answer'
7 changes: 7 additions & 0 deletions apps/assessments/models/assessment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# HTK Imports
from htk.models.classes import HtkBaseModel


class Assessment(HtkBaseModel):
class Meta:
verbose_name = 'Assessment'
33 changes: 33 additions & 0 deletions apps/assessments/models/fk_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Django Imports
from django.db import models

# HTK Imports
from htk.models.fk_fields import build_kwargs
from htk.utils import htk_setting


def fk_assessment(related_name, required=False):
field = models.ForeignKey(
htk_setting('HTK_ASSESSMENTS_ASSESSMENT_MODEL'),
related_name=related_name,
**build_kwargs(required=required),
)
return field


def fk_question(related_name, required=False):
field = models.ForeignKey(
htk_setting('HTK_ASSESSMENTS_QUESTION_MODEL'),
related_name=related_name,
**build_kwargs(required=required),
)
return field


def fk_question_choice(related_name, required=False):
field = models.ForeignKey(
htk_setting('HTK_ASSESSMENTS_QUESTION_MODEL'),
related_name=related_name,
**build_kwargs(required=required),
)
return field
56 changes: 56 additions & 0 deletions apps/assessments/models/question.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Python Standard Library Imports
import random

# Django Imports
from django.db import models

# HTK Imports
from htk.apps.assessments.models.fk_fields import fk_assessment
from htk.models.classes import HtkBaseModel


DEFAULT_RELATED_NAME = 'questions'


class Question(HtkBaseModel):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking that there should be:

Question Types:

  • BasicQuestion - answer is a free text response (e.g. What is 1+1?: 2)
  • MultiChoiceQuestion - multiple answer choices are presented, only one is correct
  • MultiChoiceMultiAnswerQuestion - multiple answer choices are presented, any number (0 or many) can be correct, all correct responses must be selected

assessment = fk_assessment(DEFAULT_RELATED_NAME, required=True)
text = models.TextField(max_length=512)
order = models.PositiveSmallIntegerField(default=0)
should_shuffle_choices = models.BooleanField(default=False)
has_correct_answer = models.BooleanField(default=False)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove


class Meta:
verbose_name = 'Assessment Question'

##
# Properties

@property
def choices_ordered(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

answer_choices_**

choices = self.choices.order_by('order')
return choices

@property
def choices_shuffled(self):
choices = self.choices.all()
random.shuffle(choices)
return choices

@property
def all_choices(self):
choices = (
self.choices_shuffled
if self.should_shuffle_choices
else self.choices_ordered
)
return choices

##
# Helpers

def is_answered_by_user(self, user, check_correction=False):
answers = user.answers.filter(choice__question=self)
if check_correction:
answers = answers.filter(choice__is_correct=True)
is_answered = answers.exists()
return is_answered
19 changes: 19 additions & 0 deletions apps/assessments/models/question_choice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Django Imports
from django.db import models

# HTK Imports
from htk.apps.assessments.models.fk_fields import fk_question
from htk.models.classes import HtkBaseModel


DEFAULT_RELATED_NAME = 'choices'


class QuestionChoice(HtkBaseModel):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • AnswerChoice

question = fk_question(DEFAULT_RELATED_NAME, required=True)
text = models.TextField(max_length=256)
order = models.PositiveSmallIntegerField(default=0)
is_correct = models.BooleanField(default=False)

class Meta:
verbose_name = 'Assessment Question Choice'