0

I'm working on a Quiz app and one question gets uploaded every week in a month (4 ques/month). Now I want to make sure that a logged in user can only attempt the question twice per week and not more than that. How can I do this using throttling or any other way? Here's my Quiz model:

Days = (
   ("Mon", "Monday"),
   ("Tue", "Tuesday"),
   ("Wed", "Wednesday"),
   ("Thu", "Thursday"),
   ("Fri", "Friday"),
   ("Sat", "Saturday"),
   ("Sun", "Sunday")
   )

class QuizDetail(models.Model):
    name = models.CharField(max_lenght=255, blank=False, null=False)
    start_date = models.DateTimeField()
    end_date = models.DateTimeField()
    publisehd_week_day = models.CharField(max_length=255, choices=Days)

The published_week_day can change every month, so basically one month it can be Tuesday and next month it can be Thursday.

Note: If in a month published_week_day is Tuesday and a user attempts last week's quiz on Monday and exhausts his two attempts then on Tuesday he should be able to attempt as it will be a fresh quiz.

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
  • what I would do is, create one model UserQuizAttempts which keeps track of the attempts made by the user. so that model would basically have a foreign key relationship with the QuizDetail model, a foreign key relationship with the User model, and the one field for the Datetime to capture when the user made the last attempt. so whenever a user attempts a question so basically I would create a record in the UserQuizAttempts model. – Aashay Amballi Jul 09 '22 at 19:49
  • So when you want to check whether the user has made 4 attempts for a question so basically, you query the UserQuizAttempts model based on the current month's date and on that question that user is trying to attempt, to check whether any attempts record has been created or not. If it has and if the attempts the user has made exceed 4 attempts then just return a response saying that Attempts exceed for the month. If not then create the record in the UserQuizAttempt model – Aashay Amballi Jul 09 '22 at 19:52
  • Okay, I read the question wrong. So yeah if you want to restrict the user 2 attempts per week then yeah basically you have filter it by week in the UserQuizAttempt model – Aashay Amballi Jul 09 '22 at 20:27

2 Answers2

0

Store it in another field

I am assuming end_date only stores the last time the quiz ended.

Create a field, say count. Now increase count by 1 everytime a test is attempted. If it reaches above 2, dont allow the quiz to be taken.

What about resetting it? You could calculate it every single time you make the request. However this is clearly extremely ineffiecient. Therefore, I would do something like so:

  1. Create another process.(Have a look at Simple approach to launching background task in Django)
  2. That process constantly searches through the database at fixed intervals.
  3. If it finds that a week has passed since the last time the user took the test, it resets the count to 0. To do this simple subtract current datetime(datetime.now()) with end_time and compare it to 2 weeks.
Naitik Mundra
  • 418
  • 3
  • 14
0

You have to create one model which basically would have a relationship with your QuizDetail model

class UserQuizAttempt(models.Model)
    quiz = models.ForeignKey(QuizDetail, on_delete=models.CASCADE)
    user = models.ForeginKey(User, on_delete=models.CASCADE)
    attempt_made_on = models.DateTimeField(auto_now_add=True)

so in your views.py file, where the user will make an API call to attempt the quiz you have to check whether the user has made any attempt for that particular quiz in that particular week. If it is exceeding 2 then just return a response saying that he's exceeding the limit of the attempt for the week.

a basic example could look like this

from datetime import date

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated

class QuizAPI(APIView):
    authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]
    
    def check_user_attempts(self, request, quiz_instance):
        current_week = date.today().isocalendar()[1] 
        attempts = UserQuizAttempt.objects.filter(attempt_made_on__week=current_week=current_week, quiz=quiz_instance, user=request.user)
        
       if attempts.count() > 2:
           return False

       return True

    def post(self, request):
        quiz_id = request.data.get('quiz_id')
        
        quiz_instance = QuizDetail.objects.get(id=quiz_id)

        if self.check_user_attempts(request, quiz_instance):
            UserQuizAttempt.objects.create(quiz=quiz_instance, user=request.user)
            
            # your logic goes here
            ...

        else:
            return Response("Your attempts exceeding for the week! Please try next week", status=status.HTTP_406_NOT_ACCEPTABLE)

So with this, you will have the history of the user's attempt made on the quiz which can be used for reporting or something.

Aashay Amballi
  • 1,321
  • 3
  • 17
  • 39
  • I suspect the current week is returning weeks starting from Monday to Sunday. I need something more dynamic. If the quiz starts on Wednesday and ends on Tuesday, how would I implement this? – Arthur Dayne Jul 11 '22 at 06:22
  • In your QuizDetail model which field you're capturing when the quiz was started? is it the start_date? – Aashay Amballi Jul 11 '22 at 06:29
  • start_date and end_date is the month's starting and end. Published_week_day will show the day of the week when the quiz will get published. So if it's Tuesday, then the quiz will get published on every Tuesday of that month. – Arthur Dayne Jul 11 '22 at 06:33