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

139 secondary backend replace session usage with db entry #173

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 5.0.2 on 2024-04-26 07:01

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('masteriqapp', '0002_auto_20240303_1147'),
]

operations = [
migrations.CreateModel(
name='QuestionUser',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('options_asked', models.BooleanField(default=False)),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='masteriqapp.question')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='customuser',
name='questions',
field=models.ManyToManyField(through='masteriqapp.QuestionUser', to='masteriqapp.question'),
),
migrations.AddField(
model_name='question',
name='users',
field=models.ManyToManyField(through='masteriqapp.QuestionUser', to=settings.AUTH_USER_MODEL),
),
]
5 changes: 4 additions & 1 deletion api/masteriqapp/models/CustomUser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
from django.db import models
from django.conf import settings

from masteriqapp.models import Question


class CustomUser(AbstractUser):
iqs = models.ManyToManyField(settings.AUTH_USER_MODEL, through="IQ")
iqs = models.ManyToManyField(settings.AUTH_USER_MODEL, through="IQ")
questions = models.ManyToManyField(Question, through="QuestionUser")
2 changes: 2 additions & 0 deletions api/masteriqapp/models/Question.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from django.conf import settings
from django.db import models
from masteriqapp.models.Category import Category


class Question(models.Model):
text = models.CharField(max_length=1024)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
users = models.ManyToManyField(settings.AUTH_USER_MODEL, through="QuestionUser")
11 changes: 11 additions & 0 deletions api/masteriqapp/models/QuestionUser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.conf import settings

from masteriqapp.models import Question


class QuestionUser(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
options_asked = models.BooleanField(default=False)
1 change: 1 addition & 0 deletions api/masteriqapp/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
from .Question import Question
from .Option import Option
from .CustomUser import CustomUser
from .QuestionUser import QuestionUser
2 changes: 1 addition & 1 deletion api/masteriqapp/tests/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_route(self):
"options": ["11", "15", "He wasnt born"],
"answer": "1"}, headers=headers)

response = c.get("/api/question/options/", headers=headers)
response = c.get("/api/question/1/options/", headers=headers)
assert response.status_code == 200
assert response.json()['question_id'] is not None
assert response.json()['number_of_options'] is not None
Expand Down
77 changes: 43 additions & 34 deletions api/masteriqapp/views/QuestionView.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,25 @@ class QuestionView(viewsets.ViewSet):
category_model = masteriq.get_model("Category")
question_model = masteriq.get_model("Question")
option_model = masteriq.get_model("Option")
question_user_model = masteriq.get_model("QuestionUser")
iq_model = masteriq.get_model("IQ")
queryset = category_model.objects.all()
permission_classes = (IsAuthenticated,)

@action(detail=True, methods=["GET"], permission_classes=[IsAuthenticated])
def new(self, request, pk):
category = get_object_or_404(self.queryset, pk=pk)
if 'question' in request.session:
actual_question = self.question_model.objects.get(pk=request.session['question'])
if actual_question is not None and actual_question.category == category:
serializer = QuestionSerializer(actual_question)
return Response(serializer.data, status=status.HTTP_200_OK)
actual_question = request.user.questions.filter(category=category).first()
if actual_question is not None and actual_question.category == category:
serializer = QuestionSerializer(actual_question)
return Response(serializer.data, status=status.HTTP_200_OK)

questions = self.question_model.objects.filter(category=category)
if len(questions) == 0:
return Response({"error":"No questions in this category"}, status=status.HTTP_404_NOT_FOUND)
new_question = random.choice(questions)
request.session['question'] = new_question.id
request.session['options_asked'] = False
new_question_user = self.question_user_model(user=request.user, question=new_question, options_asked=False)
new_question_user.save()
serializer = QuestionSerializer(new_question)
return Response(serializer.data, status=status.HTTP_200_OK)

Expand Down Expand Up @@ -105,28 +105,32 @@ def new_community(self, request):

return Response(question_serializer.data, status=status.HTTP_201_CREATED)

@action(detail=False, methods=["GET"], permission_classes=[IsAuthenticated])
def options(self, request):
if not 'question' in request.session:
return Response(status=449, data={"error": "No question being answered at the moment"})
question_id = request.session['question']
request.session['options_asked'] = True
question = self.question_model.objects.get(pk=question_id)
@action(detail=True, methods=["GET"], permission_classes=[IsAuthenticated])
def options(self, request, pk):
category = get_object_or_404(self.queryset, pk=pk)
actual_question = self.question_user_model.objects.filter(question__category=category, user=request.user).first()
if actual_question is None:
return Response(status=status.HTTP_400_BAD_REQUEST, data={"error": "No question being answered at the moment in this category"})
actual_question.options_asked = True
actual_question.save()
question = self.question_model.objects.get(pk=actual_question.question_id)
data_to_send = {'question_id': question.id, 'number_of_options': len(question.options.all()), 'options': {}}
for option in question.options.all():
data_to_send['options'][option.id] = option.text
return Response(status=status.HTTP_200_OK, data=data_to_send)

@action(detail=False, methods=["POST"], url_path="answer_text", permission_classes=[IsAuthenticated])
def answer_text(self, request):
@action(detail=True, methods=["POST"], url_path="answer_text", permission_classes=[IsAuthenticated])
def answer_text(self, request, pk):
if not 'answer' in request.data:
return Response(status=status.HTTP_400_BAD_REQUEST, data={"error": "No answer given"})
if not 'question' in request.session or not 'options_asked' in request.session:
return Response(status=449, data={"error": "No question being answered at the moment"})
if request.session['options_asked']:
category = get_object_or_404(self.queryset, pk=pk)
actual_question = self.question_user_model.objects.filter(question__category=category, user=request.user).first()
if actual_question is None:
return Response(status=status.HTTP_400_BAD_REQUEST, data={"error": "No question being answered at the moment in this category"})
if actual_question.options_asked:
return Response(status=status.HTTP_400_BAD_REQUEST, data={"error": "Options already asked, you can only "
"answer with options"})
question = self.question_model.objects.get(pk=request.session['question'])
question = actual_question.question
right_answer = question.options.get(is_correct=True)
user_is_correct = check_if_text_answer_is_correct(request.data['answer'], right_answer.text)
iq = self.iq_model.objects.get(user=request.user, category=question.category)
Expand All @@ -138,21 +142,24 @@ def answer_text(self, request):
iq.save()
data_to_send = {"user_is_correct": user_is_correct, "right_answer": right_answer.text,
"answer_sent": request.data['answer']}
del request.session['question']
del request.session['options_asked']
actual_question.delete()
return Response(status=status.HTTP_200_OK, data=data_to_send)

@action(detail=False, methods=["POST"], url_path="answer_option", permission_classes=[IsAuthenticated])
def answer_options(self, request):
@action(detail=True, methods=["POST"], url_path="answer_option", permission_classes=[IsAuthenticated])
def answer_options(self, request, pk):
if not 'answer' in request.data:
return Response(status=status.HTTP_400_BAD_REQUEST, data={"error": "No answer given"})
if not 'question' in request.session or not 'options_asked' in request.session:
return Response(status=449, data={"error": "No question being answered at the moment"})
if not request.session['options_asked']:
category = get_object_or_404(self.queryset, pk=pk)
actual_question = self.question_user_model.objects.filter(question__category=category,
user=request.user).first()
if actual_question is None:
return Response(status=status.HTTP_400_BAD_REQUEST,
data={"error": "No question being answered at the moment in this category"})
if not actual_question.options_asked:
return Response(status=status.HTTP_400_BAD_REQUEST,
data={"error": "Options already asked, you can only "
"answer with options"})
question = self.question_model.objects.get(pk=request.session['question'])
question = actual_question.question
right_answer = question.options.get(is_correct=True)
answer_sent = self.option_model.objects.get(pk=request.data['answer'])
user_is_correct = False
Expand All @@ -166,14 +173,16 @@ def answer_options(self, request):

data_to_send = {"user_is_correct": user_is_correct, "right_answer": right_answer.text,
"answer_sent": answer_sent.text}
del request.session['question']
del request.session['options_asked']
actual_question.delete()
return Response(status=status.HTTP_200_OK, data=data_to_send)

@action(detail=False, methods=["GET"], permission_classes=[IsAuthenticated])
def options_asked(self, request):
if not 'question' in request.session or not 'options_asked' in request.session:
@action(detail=True, methods=["GET"], permission_classes=[IsAuthenticated])
def options_asked(self, request, pk):
category = get_object_or_404(self.queryset, pk=pk)
actual_question = self.question_user_model.objects.filter(question__category=category, user=request.user).first()
print(actual_question.options_asked)
if actual_question is None:
data_to_send = {"options_asked": False}
else:
data_to_send = {"options_asked": request.session['options_asked']}
data_to_send = {"options_asked": actual_question.options_asked}
return Response(status=status.HTTP_200_OK, data=data_to_send)
16 changes: 8 additions & 8 deletions frontend/src/api_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ export default
* Get if the user has already asked for options
* @returns {Boolean} true if options have been asked, false otherwise
*/
static async getIfOptionsAsked() {
const response = await axios.get(`/api/question/options_asked`);
static async getIfOptionsAsked(category_id) {
const response = await axios.get(`/api/question/${category_id}/options_asked`);
return !!response.data.options_asked;
}

/**
* Get the options for the current question
* @returns {Object} {"id_option": "text_option"}
*/
static async getOptions() {
const response = await axios.get(`/api/question/options`);
static async getOptions(category_id) {
const response = await axios.get(`/api/question/${category_id}/options`);
return response.data.options;
}

Expand Down Expand Up @@ -138,8 +138,8 @@ export default
* @param {String} answer_text
* @returns {Object} {user_is_correct: Boolean, right_answer: String, answer_sent: String}
*/
static async postAnswerText(answer_text) {
const response = await axios.post(`/api/question/answer_text/`, {
static async postAnswerText(answer_text, category_id) {
const response = await axios.post(`/api/question/${category_id}/answer_text/`, {
answer: answer_text,
});
return response.data;
Expand All @@ -150,8 +150,8 @@ export default
* @param {String} option_id
* @returns {Object} {user_is_correct: Boolean, right_answer: String, answer_sent: String}
*/
static async postAnswerOption(option_id) {
const response = await axios.post(`/api/question/answer_option/`, {
static async postAnswerOption(option_id, category_id) {
const response = await axios.post(`/api/question/${category_id}/answer_option/`, {
answer: option_id,
});
return response.data;
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/components/AnswerForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
import APIClient from '@/api_client';
import { defineEmits, onMounted, ref, defineProps, watch } from 'vue';
import AnswerMessage from '@/components/AnswerMessage.vue';
import {useRoute} from "vue-router";

// variables specific to this component
const route = useRoute()
const id_category = route.params.id_category;
const emit = defineEmits(['newQuestion', 'updateUserIq'])
const answer_sent = ref(false);
const show_text_form = ref(true);
Expand All @@ -21,20 +24,20 @@ const props = defineProps({
})

const submitAnswerText = async () => {
response_to_answer.value = await APIClient.postAnswerText(answer_text.value);
response_to_answer.value = await APIClient.postAnswerText(answer_text.value, id_category);
answer_sent.value = true;
emit('updateUserIq');
}

const submitAnswerOption = async (id) => {
response_to_answer.value = await APIClient.postAnswerOption(id);
response_to_answer.value = await APIClient.postAnswerOption(id, id_category);
answer_sent.value = true;
emit('updateUserIq');
}

const fetchOptions = async () => {
show_text_form.value = false;
options.value = await APIClient.getOptions();
options.value = await APIClient.getOptions(id_category);
}

const newQuestion = () => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/views/QuizView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const fetchNewQuestion = async () => {
question.value = await APIClient.getNewQuestion(id_category);

// wait for the question before checking if the user has asked for options
hasAskedOptions.value = await APIClient.getIfOptionsAsked();
hasAskedOptions.value = await APIClient.getIfOptionsAsked(id_category);
};

// Fetch user IQ and add it to the graph
Expand Down
Loading