diff --git a/onlinecourse/admin.py b/onlinecourse/admin.py index ffd8a631d..9f5abac9e 100644 --- a/onlinecourse/admin.py +++ b/onlinecourse/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin # <HINT> Import any new Models here -from .models import Course, Lesson, Instructor, Learner +from .models import Course, Lesson, Instructor, Learner, Question, Choice # <HINT> Register QuestionInline and ChoiceInline classes here @@ -9,6 +9,14 @@ class LessonInline(admin.StackedInline): model = Lesson extra = 5 +class QuestionInline(admin.StackedInline): + model = Question + extra = 5 + +class ChoiceInline(admin.StackedInline): + model = Choice + extra = 5 + # Register your models here. class CourseAdmin(admin.ModelAdmin): @@ -21,6 +29,9 @@ class CourseAdmin(admin.ModelAdmin): class LessonAdmin(admin.ModelAdmin): list_display = ['title'] +class QuestionAdmin(admin.ModelAdmin): + inlines = [ ChoiceInline] + # <HINT> Register Question and Choice models here @@ -28,3 +39,4 @@ class LessonAdmin(admin.ModelAdmin): admin.site.register(Lesson, LessonAdmin) admin.site.register(Instructor) admin.site.register(Learner) +admin.site.register(Question, QuestionAdmin) diff --git a/onlinecourse/models.py b/onlinecourse/models.py index e5b540b16..0874e6c4b 100644 --- a/onlinecourse/models.py +++ b/onlinecourse/models.py @@ -115,6 +115,19 @@ class Enrollment(models.Model): # else: # return False +class Question(models.Model): + course = models.ForeignKey(Course, on_delete=models.CASCADE) + title = models.CharField(max_length=550, default="question title") + text = models.CharField(max_length=550, default="question text") + grade = models.FloatField(default=5.0) + + def is_get_score(self, selected_ids): + all_answers = self.choice_set.filter(is_correct=True).count() + selected_correct = self.choice_set.filter(is_correct=True, id__in=selected_ids).count() + if all_answers == selected_correct: + return True + else: + return False # <HINT> Create a Choice Model with: # Used to persist choice content for a question @@ -123,6 +136,10 @@ class Enrollment(models.Model): # Indicate if this choice of the question is a correct one or not # Other fields and methods you would like to design # class Choice(models.Model): +class Choice(models.Model): + question = models.ForeignKey(Question, on_delete=models.CASCADE) + text = models.CharField(max_length=550, default="question text") + is_correct = models.BooleanField(default=False) # <HINT> The submission model # One enrollment could have multiple submission @@ -130,5 +147,8 @@ class Enrollment(models.Model): # One choice could belong to multiple submissions #class Submission(models.Model): # enrollment = models.ForeignKey(Enrollment, on_delete=models.CASCADE) -# choices = models.ManyToManyField(Choice) -# Other fields and methods you would like to design \ No newline at end of file +# chocies = models.ManyToManyField(Choice) +# Other fields and methods you would like to design +class Submission(models.Model): + enrollment = models.ForeignKey(Enrollment, on_delete=models.CASCADE) + choices = models.ManyToManyField(Choice) diff --git a/onlinecourse/templates/onlinecourse/course_detail_bootstrap.html b/onlinecourse/templates/onlinecourse/course_detail_bootstrap.html index 7a22e1694..8dada80a0 100644 --- a/onlinecourse/templates/onlinecourse/course_detail_bootstrap.html +++ b/onlinecourse/templates/onlinecourse/course_detail_bootstrap.html @@ -28,7 +28,7 @@ {% csrf_token %} <div class="input-group"> <input type="text" class="form-control" placeholder="Username" name="username" > - <input type="password" class="form-control" placeholder="Username" name="psw" > + <input type="password" class="form-control" placeholder="Password" name="psw" > <button class="btn btn-primary" type="submit">Login</button> <a class="btn btn-link" href="{% url 'onlinecourse:registration' %}">Sign Up</a> </div> @@ -62,7 +62,37 @@ <h2>{{ course.name }}</h2> </div> --> + <button data-toggle="collapse" data-target="#exam" + class="btn btn-info" role="button" style="width:100%"> + Start Exam</button> + <div id="exam" class="collapse"> + {% if user.is_authenticated %} + <form id="questionform" action="{% url 'onlinecourse:submit' course.id %}" method="post"> + {% for question in course.question_set.all %} + <div class="card mt-1"> + <div class="card-header"> + <h5>{{question.title}}</h5> + </div> + {% csrf_token %} + <b>{{question.text}}</b><br/> + <div class="form-group"> + {% for choice in question.choice_set.all %} + <div class="form-check"> + <label class="form-check-label"> + <input type="checkbox" name="choice_{{choice.id}}" + class="form-check-input" id="{{choice.id}}" + value="{{choice.id}}">{{ choice.text }} + </label> + </div> + {% endfor %} + </div> + </div> + {% endfor %} + <input class="btn btn-success btn-block" type="submit" value="Submit"> + </form> + {% endif %} + </div> <!-- <HINT> If user is authenticated, show course exam with a list of question --> <!-- <HINT> Each example will have many questions --> @@ -99,4 +129,4 @@ <h2>{{ course.name }}</h2> https://www.w3schools.com/bootstrap4/bootstrap_forms_inputs.asp--> </div> </body> -</html> \ No newline at end of file +</html> diff --git a/onlinecourse/templates/onlinecourse/course_list_bootstrap.html b/onlinecourse/templates/onlinecourse/course_list_bootstrap.html index 34793b319..6f5b870b6 100644 --- a/onlinecourse/templates/onlinecourse/course_list_bootstrap.html +++ b/onlinecourse/templates/onlinecourse/course_list_bootstrap.html @@ -25,7 +25,7 @@ {% csrf_token %} <div class="input-group"> <input type="text" class="form-control" placeholder="Username" name="username" > - <input type="password" class="form-control" placeholder="Username" name="psw" > + <input type="password" class="form-control" placeholder="Password" name="psw" > <button class="btn btn-primary" type="submit">Login</button> <a class="btn btn-link" href="{% url 'onlinecourse:registration' %}">Sign Up</a> </div> @@ -62,4 +62,4 @@ <h5 class="card-title">{{ course.name }}, <span class="text-success"> <p>No courses are available.</p> {% endif %} </body> -</html> \ No newline at end of file +</html> diff --git a/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html b/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html index 264728f5c..dfa4971dc 100644 --- a/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html +++ b/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html @@ -24,7 +24,7 @@ {% csrf_token %} <div class="input-group"> <input type="text" class="form-control" placeholder="Username" name="username" > - <input type="password" class="form-control" placeholder="Username" name="psw" > + <input type="password" class="form-control" placeholder="Password" name="psw" > <button class="btn btn-primary" type="submit">Login</button> <a class="btn btn-link" href="{% url 'onlinecourse:registration' %}">Sign Up</a> </div> @@ -39,17 +39,40 @@ {% if grade > 80 %} <div class="alert alert-success"> <!--HINT Display passed info --> + <b> Congratulations, {{user.name}} ! </b> You have passed the exam and completed + the course with score {{mark}} /{{total_mark}} </div> {% else %} <div class="alert alert-danger"> <!--HINT Display failed info --> + <b> Failed ! </b>Sorry, {{user.name}} ! You have failed with score {{mark}} /{{total_mark}} </div> <a class="btn btn-link text-danger" href="{% url 'onlinecourse:course_details' course.id %}">Re-test</a> {% endif %} <div class="card-columns-vertical mt-1"> <h5 class="">Exam results</h5> <!--HINT Display exam results--> + {% for question in course.question_set.all %} + <div class="card"> + <div class="card-header border-light"> + <h5>{{question.text}}</h5> + </div> + <div class="card-body"> + {% for choice in question.choice_set.all %} + {% if choice.is_correct and choice in choices %} + <p class="card-text" style="color:green">Correct answer: {{choice.text}}</p> + {% elif choice.is_correct and choice not in choices %} + <p class="card-text" style="color:orange">Not selected: {{choice.text}}</p> + {% elif choice.is_correct == False and choice in choices %} + <p class="card-text" style="color:red">Wrong answer: {{choice.text}}</p> + {% else %} + <p class="card-text" style="color:black">{{choice.text}}</p> + {% endif %} + {% endfor %} + </div> + </div> + {% endfor %} </div> </div> </body> -</html> \ No newline at end of file +</html> diff --git a/onlinecourse/templates/onlinecourse/user_login_bootstrap.html b/onlinecourse/templates/onlinecourse/user_login_bootstrap.html index c58611c57..3df099c27 100644 --- a/onlinecourse/templates/onlinecourse/user_login_bootstrap.html +++ b/onlinecourse/templates/onlinecourse/user_login_bootstrap.html @@ -25,7 +25,7 @@ {% csrf_token %} <div class="input-group"> <input type="text" class="form-control" placeholder="Username" name="username" > - <input type="password" class="form-control" placeholder="Username" name="psw" > + <input type="password" class="form-control" placeholder="Password" name="psw" > <button class="btn btn-primary" type="submit">Login</button> <a class="btn btn-link" href="{% url 'onlinecourse:registration' %}">Sign Up</a> </div> @@ -56,4 +56,4 @@ <h1>Login</h1> </div> </form> -</body> \ No newline at end of file +</body> diff --git a/onlinecourse/templates/onlinecourse/user_registration_bootstrap.html b/onlinecourse/templates/onlinecourse/user_registration_bootstrap.html index 14dc39584..0c43389db 100644 --- a/onlinecourse/templates/onlinecourse/user_registration_bootstrap.html +++ b/onlinecourse/templates/onlinecourse/user_registration_bootstrap.html @@ -25,7 +25,7 @@ {% csrf_token %} <div class="input-group"> <input type="text" class="form-control" placeholder="Username" name="username" > - <input type="password" class="form-control" placeholder="Username" name="psw" > + <input type="password" class="form-control" placeholder="Password" name="psw" > <button class="btn btn-primary" type="submit">Login</button> <a class="btn btn-link" href="{% url 'onlinecourse:registration' %}">Sign Up</a> </div> @@ -61,4 +61,4 @@ <h1>Sign Up</h1> </div> </form> -</body> \ No newline at end of file +</body> diff --git a/onlinecourse/urls.py b/onlinecourse/urls.py index 2960e1045..026d13127 100644 --- a/onlinecourse/urls.py +++ b/onlinecourse/urls.py @@ -18,7 +18,7 @@ path('<int:course_id>/enroll/', views.enroll, name='enroll'), # <HINT> Create a route for submit view - + path('<int:course_id>/submit/', views.submit, name='submit'), # <HINT> Create a route for show_exam_result view - + path('course/<int:course_id>/submission/<int:submission_id>/result/', views.show_exam_result, name='show_exam_result'), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/onlinecourse/views.py b/onlinecourse/views.py index 6567363ea..f5a74889c 100644 --- a/onlinecourse/views.py +++ b/onlinecourse/views.py @@ -1,7 +1,7 @@ from django.shortcuts import render from django.http import HttpResponseRedirect # <HINT> Import any new Models here -from .models import Course, Enrollment +from .models import Course, Enrollment, Question, Choice, Submission from django.contrib.auth.models import User from django.shortcuts import get_object_or_404, render, redirect from django.urls import reverse @@ -111,6 +111,19 @@ def enroll(request, course_id): # Add each selected choice object to the submission object # Redirect to show_exam_result with the submission id #def submit(request, course_id): +def submit(request, course_id): + user = request.user + course = get_object_or_404(Course, pk=course_id) + enrollment = Enrollment.objects.get(user=user, course=course) + submission = Submission.objects.create(enrollment=enrollment) + answers = extract_answers(request) + submission.choices.set(answers) + submission.save() + return HttpResponseRedirect(reverse( + viewname='onlinecourse:show_exam_result', + args=(course.id, submission.id,) + )) + # <HINT> A example method to collect the selected choices from the exam form from the request object @@ -122,7 +135,14 @@ def enroll(request, course_id): # choice_id = int(value) # submitted_anwsers.append(choice_id) # return submitted_anwsers - +def extract_answers(request): + submitted_anwsers = [] + for key in request.POST: + if key.startswith('choice'): + value = request.POST[key] + choice_id = int(value) + submitted_anwsers.append(choice_id) + return submitted_anwsers # <HINT> Create an exam result view to check if learner passed exam and show their question results and result for each question, # you may implement it based on the following logic: @@ -131,6 +151,23 @@ def enroll(request, course_id): # For each selected choice, check if it is a correct answer or not # Calculate the total score #def show_exam_result(request, course_id, submission_id): - +def show_exam_result(request, course_id, submission_id): + course = get_object_or_404(Course, pk=course_id) + submission = get_object_or_404(Submission, pk=submission_id) + choices = submission.choices.all() + total_mark, mark = 0, 0 + for question in course.question_set.all(): + total_mark += question.grade + if question.is_get_score(choices): + mark += question.grade + + return render( + request, + 'onlinecourse/exam_result_bootstrap.html', + {"course":course, "choices":choices,"mark":mark, + "total_mark": total_mark, + "submission": submission, + "grade": int((mark / total_mark) * 100) } + )