how to work with the timer to the quiz fully functionality

Hi I am creating a quiz app where user has to complete test within particular time, and i used the following javascript code for the timer

window.onload = function begin(){
document.getElementById('timer').innerHTML =
  1 + ":" + 00;
startTimer();
}

function startTimer() {
  var presentTime = document.getElementById('timer').innerHTML;
  var timeArray = presentTime.split(/[:]+/);
  var m = timeArray[0];
  var s = checkSecond((timeArray[1] - 1));
  if(s==59){m=m-1}
  if(m<0){
    document.getElementById('quiz').submit();
  }
  document.getElementById('timer').innerHTML =
    m + ":" + s;
  setTimeout(startTimer, 1000);
}

function checkSecond(sec) {
  if (sec < 10 && sec >= 0) {sec = "0" + sec}; // add zero in front of numbers < 10
  if (sec < 0) {sec = "59"};
  return sec;
}
`` 
the timer is working count down but i cant handle the quiz even if the time is 00 and when i refresh teh browser the time is reset...any one who have an exprince with this please help me

First, keep in mind that if you track the timer only on the browser, whoever is taking the quiz can reset it to whatever they want the value to be.

This means that regardless of how you want to display this in the browser, you really do need to also manage it on the server.

That gives you a couple of choices:

  • Go through the questions using a “skeleton” page, where the page stays the same but each question is retrieved from the server in turn and injected into the page upon retrieval.

  • If each question is a separate page and retrieval, then mark the start of that page when it is rendered and figure out how long it took to submit that page.

If you absolutely need a “hard timeout” on a per-question level, then that means you need to use a websocket connection (or something very close to that) where the server can send the timeout notification to the client. (Depending upon your precise requirements, you may also need to add a security layer to that timer connection to prevent tampering.)

ok i want the first choices and first of all let me show you all my progress in my project
here is my model.py

from django.db import models
from django.contrib.auth.models import User

class Quizzes(models.Model):
	title = models.CharField(max_length=200)
	description = models.TextField()
	question_number = models.PositiveIntegerField()
	total_marks = models.PositiveIntegerField()
	user = models.ForeignKey(User, on_delete=models.CASCADE)
	date = models.DateTimeField(auto_now_add=True)
	#allowed_attempts = models.PositiveIntegerField()
	time_limit_mins = models.PositiveIntegerField()
	#required_score_to_pass = models.IntegerField(help_text="required score in %")


	def __str__(self):
		return self.title		

class Question(models.Model):
	quiz = models.ForeignKey(Quizzes,on_delete=models.CASCADE)
	question=models.CharField(max_length=600)
	marks=models.PositiveIntegerField()
	option1=models.CharField(max_length=200)
	option2=models.CharField(max_length=200)
	option3=models.CharField(max_length=200)
	option4=models.CharField(max_length=200)
	cat=(('Option1','Option1'),('Option2','Option2'),('Option3','Option3'),('Option4','Option4'))
	answer=models.CharField(max_length=200,choices=cat)
    
    
class Attempter(models.Model):
	user = models.ForeignKey(User, on_delete=models.CASCADE)
	quiz = models.ForeignKey(Quizzes, on_delete=models.CASCADE)
	score = models.PositiveIntegerField()
	completed = models.DateTimeField(auto_now_add=True)

	def __str__(self):
		return self.user.username

class Result(models.Model):
    #student = models.ForeignKey(User,on_delete=models.CASCADE)
    exam = models.ForeignKey(Quizzes,on_delete=models.CASCADE)
    marks = models.PositiveIntegerField()
    date = models.DateTimeField(auto_now=True)

and here is views .py

from django.shortcuts import render,redirect,get_object_or_404
from . import forms
from .models import *

# Create your views here.
#create new quz 
def NewQuiz(request):
    quizForm=forms.QuizForm()
    if request.method=='POST':
        quizForm=forms.QuizForm(request.POST)
        if quizForm.is_valid():        
           quiz= quizForm.save()
           quiz_id = quiz.id
        else:
            print("form is invalid")
        return redirect('quiz:new-question',quiz_id=quiz_id)
    return render(request,'quiz/createquiz.html',{'quizForm':quizForm})

#create new question
def NewQuestion(request,quiz_id):
	user = request.user
	quiz = get_object_or_404(Quizzes, id=quiz_id)
	questionForm=forms.QuestionForm()
	if request.method=='POST':
		questionForm=forms.QuestionForm(request.POST)
		if questionForm.is_valid():
			question=questionForm.save(commit=False)
			quiz=models.quiz.objects.get(id=request.POST.get('quizID'))
			question.quiz=quiz
			question.save() 
		else:
			print("form is invalid")
		return redirect('quiz:new-question',quiz_id=quiz_id)
	return render(request,'quiz/createqusetion.html',{'questionForm':questionForm})

 #about quiz
def QuizDetail(request,quiz_id):
	user = request.user
	quiz = get_object_or_404(Quizzes, id=quiz_id)
	my_attempts = Attempter.objects.filter(quiz=quiz)

	context = {
		'quiz': quiz,
		'my_attempts': my_attempts,
	}
	return render(request, 'quiz/quizdetail.html', context)   

#take the quiz

def TakeQuiz(request,quiz_id):
    quiz=get_object_or_404(Quizzes, id=quiz_id)
    questions=Question.objects.all().filter(quiz=quiz)
    if request.method=='POST':
        pass
    response= render(request,'quiz/startexam.html',{'quiz':quiz,'questions':questions})
    response.set_cookie('quiz_id',quiz.id)
    return response    

#calculate the marks
def calculate_marks_view(request):
    if request.COOKIES.get('quiz_id') is not None:
        quiz_id = request.COOKIES.get('quiz_id')
        quiz=Quizzes.objects.get(id=quiz_id)
        total_marks=0
        questions=Question.objects.all().filter(quiz=quiz)
        for i in range(len(questions)):
            selected_ans = request.COOKIES.get(str(i+1))
            actual_answer = questions[i].answer
            if selected_ans == actual_answer:
                total_marks = total_marks + questions[i].marks
        user = User.objects.get(user_id=request.user.id) #used for the specfic user
        result = Result()
        result.marks=total_marks
        result.exam=quiz
        #result.student=student assign the result to the user 
        result.save()

        return redirect('quiz:check-marks',quiz_id=quiz_id)  

def check_marks_view(request,quiz_id):
    quiz=Quizzes.objects.get(id=quiz_id)
    #student = User.objects.get(user_id=request.user.id)
    results= Result.objects.all().filter(exam=quiz)#.filter(student=student)
    return render(request,'quiz/checkmarks.html',{'results':results})      
       

and here is my start exam.html

{% block content %}
<head>
  <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
  <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
  <script src="//code.jquery.com/jquery-1.11.1.min.js"></script>


</head>


<div class="jumbotron my-4">
  <div class="row justify-content-left">
    <div class="col-mx-auto">
      <div class="alert alert-success" role="alert"><center>Time left : <span id="timer"></span></center></div>
    </div>
  </div>


  <form class="form" id="quiz" autocomplete="off" onsubmit="return saveAns()"  action="/quiz/calculate-marks" method="POST">
    {% csrf_token %}

    <h1 style="text-align: center;">{{course.course_name}}</h1>
    {% for q in questions%}
    <h3 class="text-info">{{ forloop.counter }}. {{q.question}}</h3><h4 style="text-align: right;">[Marks {{q.marks}}]</h4>
    
        <input type="hidden" name="csrfmiddlewaretoken" value="C24rUotmdHawVQJL3KrqiWxvti8UffOFYUc8TRbZtLt36AVLdP3jbkzUVe3beRAa">
        
        
          <div class="form-check mx-4">
            <input class="form-check-input" type="radio" name="{{ forloop.counter }}" id="{{q.option1}}" value="Option1">
            <label class="form-check-label" for="option1">
              {{q.option1}}
            </label>
          </div>

        
          <div class="form-check mx-4">
            <input class="form-check-input" type="radio" name="{{ forloop.counter }}" id="{{q.option2}}" value="Option2">
            <label class="form-check-label" for="option2">
              {{q.option2}}
            </label>
          </div>

        
          <div class="form-check mx-4">
            <input class="form-check-input" type="radio" name="{{ forloop.counter }}" id="{{q.option3}}" value="Option3">
            <label class="form-check-label" for="option3">
              {{q.option3}}
            </label>
          </div>

        
          <div class="form-check mx-4">
            <input class="form-check-input" type="radio" name="{{ forloop.counter }}" id="{{q.option4}}" value="Option4">
            <label class="form-check-label" for="option4">
              {{q.option4}}
            </label>
          </div>

        {% endfor %}
        <input class="btn btn-info btn-lg"   type="submit" value="Submit">  
    </form>
  </div>

<script> 
    function saveAns(){  

        var ele = document.getElementsByTagName('input'); 
        for(i = 0; i < ele.length; i++) { 
            if(ele[i].type="radio") { 
                if(ele[i].checked){        
                  setCookie(ele[i].name,ele[i].value,3)
                }
            } 
        } 
          
    }
    
    function setCookie(cname, cvalue, exdays) {
  var d = new Date();
  d.setTime(d.getTime() + (exdays*24*60*60*1000));
  var expires = "expires="+ d.toUTCString();
  document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
window.onload = function begin(){
document.getElementById('timer').innerHTML =
  1 + ":" + 00;
startTimer();
}

function startTimer() {
  var presentTime = document.getElementById('timer').innerHTML;
  var timeArray = presentTime.split(/[:]+/);
  var m = timeArray[0];
  var s = checkSecond((timeArray[1] - 1));
  if(s==59){m=m-1}
  if(m<0){
    document.getElementById('quiz').submit();
  }
  document.getElementById('timer').innerHTML =
    m + ":" + s;
  setTimeout(startTimer, 1000);
}

function checkSecond(sec) {
  if (sec < 10 && sec >= 0) {sec = "0" + sec}; // add zero in front of numbers < 10
  if (sec < 0) {sec = "59"};
  return sec;
}

</script> 


{% endblock content %}

so how can i start with choice one ??

If this is what you mean by choice one -

  • Create a view that renders the basic page, perhaps with a “Start Quiz” button.
  • When the “Start Quiz” or “Submit” button is pressed, issue an AJAX call to retrieve an html fragment containing the next question to be displayed. The html fragment retrieved is then placed into your page.
  • When an answer is selected, submit the answer to the server and retrieve the next question.

This means that you need one view for the basic page, and one view to handle the submission of answers, along with the appropriate templates and forms for each.

Exactly how that’s going to be done depends upon what JavaScript framework you may be using and to a lesser degree how the rest of your site is architected.

Now, external to this, you’ll need to have a separate process running (perhaps using Channels), to manage the timer for each user. When a page (or page fragment) is retrieved, the JavaScript on that page will open a websocket connection back to the server. Then, at the appropriate times, that process can send messages to the browser to take actions when necessary.

the question are displayed all in one page

Ok, that makes the “per-page handling” easier, but you’re still going to need to manage the timer on the server and not in the browser.

ok so how can i manage the timer in the server side ?

To augment these responses a bit, you don’t need to use Channels. You could use any Websocket server, such as something using socket.io or ws, but then you still need to integrate it with your Django environment.

When the quiz is first loaded, you start a timer on the server with whatever information you need to track this timer. If the page is refreshed, it needs to determine that an existing timer already exists and to retrieve the current status of that timer.
When the timer expires, the server then asks the client to return whatever data is to be submitted, and then it feeds it back into Django. If the socket has been disconnected or doesn’t respond in the appropriate period of time, then you can consider the quiz abandoned.

so first i have to read about Websocket server…do you have any suggestion or a sample code considering my case please?

There are plenty of examples out there. I’m not particularly up to date on doing this. It’s been at least 3 years (4 years now? I forget) since I’ve handled a case like this and I’m not aware of what might be considered the current best practices.