From 56a75034d6fbe3bdd6abba0f0ba4a6575d877bf7 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Fri, 29 Jul 2022 12:56:18 +0100 Subject: [PATCH 01/27] Update views.py --- onlinecourse/views.py | 51 ++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/onlinecourse/views.py b/onlinecourse/views.py index 6567363ea..11a80764a 100644 --- a/onlinecourse/views.py +++ b/onlinecourse/views.py @@ -8,11 +8,11 @@ from django.views import generic from django.contrib.auth import login, logout, authenticate import logging +from .models import Submission, Learner, Choice, Question, Enrolement, Lesson, Course # Get an instance of a logger logger = logging.getLogger(__name__) # Create your views here. - def registration_request(request): context = {} if request.method == 'GET': @@ -38,7 +38,6 @@ def registration_request(request): context['message'] = "User already exists." return render(request, 'onlinecourse/user_registration_bootstrap.html', context) - def login_request(request): context = {} if request.method == "POST": @@ -105,32 +104,38 @@ def enroll(request, course_id): # <HINT> Create a submit view to create an exam submission record for a course enrollment, # you may implement it based on following logic: - # Get user and course object, then get the associated enrollment object created when the user enrolled the course - # Create a submission object referring to the enrollment - # Collect the selected choices from exam form - # 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): + course = get_object_or_404(Course, pk=course_id) + user = request.user + Submission.objects.create(user=user, course=course ) + +# Get user and course object, then get the associated enrollment object +# created when the user enrolled the course +# Create a submission object referring to the enrollment +# Collect the selected choices from exam form +# Add each selected choice object to the submission object +# Redirect to show_exam_result with the submission id + # <HINT> A example method to collect the selected choices from the exam form from the request object -#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 +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: - # Get course and submission based on their ids - # Get the selected choice ids from the submission record - # 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) +# Get course and submission based on their ids +# Get the selected choice ids from the submission record +# For each selected choice, check if it is a correct answer or not +# Calculate the total score From 8cc29a90501851b38220342cc9babca615ee7f8a Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Fri, 29 Jul 2022 12:58:39 +0100 Subject: [PATCH 02/27] Add Question, Choice, Submission Defined Question, Choice, Submission --- onlinecourse/models.py | 60 ++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/onlinecourse/models.py b/onlinecourse/models.py index e5b540b16..480ae4738 100644 --- a/onlinecourse/models.py +++ b/onlinecourse/models.py @@ -95,40 +95,60 @@ class Enrollment(models.Model): rating = models.FloatField(default=5.0) -# <HINT> Create a Question Model with: - # Used to persist question content for a course - # Has a One-To-Many (or Many-To-Many if you want to reuse questions) relationship with course - # Has a grade point for each question - # Has question content - # Other fields and methods you would like to design -#class Question(models.Model): +class Question(models.Model): + text = models.TextField(max_length=500, default="question is...", null=False) + grade = models.CharField(default="U") + lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE) + course = models.ManyToMany(Course , on_delete=models.SET_NULL) + A = 'A' + B ='B' + C ='C' + D= 'D' + Choices = [(A , 'A'), (B, 'B'),(C, 'C'), (D, 'D') ] + correct_ans = models.CharField(choices=Choices, default=A, null=False) # Foreign key to lesson # question text # question grade/mark - # <HINT> A sample model method to calculate if learner get the score of the question - #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 + 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: +class Choice(models.Model): # Used to persist choice content for a question # One-To-Many (or Many-To-Many if you want to reuse choices) relationship with Question + question = models.ManyToManyField(Question) # Choice content + A = 'A' + B ='B' + C ='C' + D= 'D' + Choices = [(A , 'A'), (B, 'B'),(C, 'C'), (D, 'D') ] + answer = models.CharField(max_length=500, choices=Choices, default=A) + def is_correct(self, answer): + correct_ans = question.correct_ans + if answer == correct_ans: + return True + else: + return False # 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): + # <HINT> The submission model # One enrollment could have multiple submission # One submission could have multiple choices # 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 +class Submission(models.Model): + enrollment = models.ForeignKey(Enrollment, on_delete=models.PROTECT) + chocies = models.ManyToManyField(Choice) + user = models.ManyToManyField(Learner) + course = models.ManyToManyField(Course) + #total = + # Other fields and methods you would like to design From 1d7c851ea3b27c447afb5b90b14f47b4980e64ea Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Fri, 29 Jul 2022 13:10:00 +0100 Subject: [PATCH 03/27] Edited Choice --- onlinecourse/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/models.py b/onlinecourse/models.py index 480ae4738..70c5cdac9 100644 --- a/onlinecourse/models.py +++ b/onlinecourse/models.py @@ -123,7 +123,7 @@ def is_get_score(self, selected_ids): class Choice(models.Model): # Used to persist choice content for a question # One-To-Many (or Many-To-Many if you want to reuse choices) relationship with Question - question = models.ManyToManyField(Question) + question = models.ForeignKey(Question, on_delete=models.CASCADE) # Choice content A = 'A' B ='B' From 323306803c7c01127c3bf1b2bfe99093d4016ac5 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Fri, 29 Jul 2022 13:12:52 +0100 Subject: [PATCH 04/27] registered question & choice --- onlinecourse/admin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/onlinecourse/admin.py b/onlinecourse/admin.py index ffd8a631d..8b42dedf1 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, Submission # <HINT> Register QuestionInline and ChoiceInline classes here @@ -28,3 +28,5 @@ class LessonAdmin(admin.ModelAdmin): admin.site.register(Lesson, LessonAdmin) admin.site.register(Instructor) admin.site.register(Learner) +admin.site.register(Question) +admin.site.register(Choice) From 5a0d578052fea0e2b853480c0079a0e999a9a8c2 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Fri, 29 Jul 2022 13:14:54 +0100 Subject: [PATCH 05/27] edited Question --- onlinecourse/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/models.py b/onlinecourse/models.py index 70c5cdac9..4e8eedc10 100644 --- a/onlinecourse/models.py +++ b/onlinecourse/models.py @@ -97,7 +97,7 @@ class Enrollment(models.Model): class Question(models.Model): text = models.TextField(max_length=500, default="question is...", null=False) - grade = models.CharField(default="U") + grade = models.CharField(default="U",, null=True) lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE) course = models.ManyToMany(Course , on_delete=models.SET_NULL) A = 'A' From 4dd0c0d9b7dc4a0d6d1a1fcf55f802f95f6d2afc Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Fri, 29 Jul 2022 15:15:27 +0100 Subject: [PATCH 06/27] changed the form --- .../onlinecourse/course_detail_bootstrap.html | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/onlinecourse/templates/onlinecourse/course_detail_bootstrap.html b/onlinecourse/templates/onlinecourse/course_detail_bootstrap.html index 7a22e1694..5a85e1de8 100644 --- a/onlinecourse/templates/onlinecourse/course_detail_bootstrap.html +++ b/onlinecourse/templates/onlinecourse/course_detail_bootstrap.html @@ -56,26 +56,34 @@ <h2>{{ course.name }}</h2> <!-- - A collapse example here: + A collapse example here: + + Click to expand elements within the collapse div --> <div id="exam" class="collapse"> - Click to expand elements within the collapse div </div> - - --> - <!-- <HINT> If user is authenticated, show course exam with a list of question --> - <!-- <HINT> Each example will have many questions --> - <!-- <HINT> Each question will have many choices --> - + {% if user.is_authenticated %} + <ul> + {% for question in question.all %} + + <li> + <p> {{question }}</p> + <ul> + {% for choice in question.choices %} + <li>{{choice }}</p> <br> + <input type="check" name="choice_{{choice.id}}" id="{{choice.id}}" ...></li></ul> + </li> + </ul> + <!-- <HINT> Create a form to collect the selected choices for all questions --> <!-- <HINT> For each question choice, you may create a checkbox input like - <input type="check" name="choice_{{choice.id}}" id="{{choice.id}}" ...> + --> - <!-- A choice submission form example + <!-- A choice submission form example --> <form id="questionform" action="point to a submit view" method="post"> ... for each question in the course ... <div class="card mt-1"> @@ -93,10 +101,10 @@ <h2>{{ course.name }}</h2> </div> </div> <input class="btn btn-success btn-block" type="submit" value="Submit"> - </form> --> + </form> <!--Check here to see more details Bootstrap checkbox https://www.w3schools.com/bootstrap4/bootstrap_forms_inputs.asp--> </div> </body> -</html> \ No newline at end of file +</html> From 94de0c7b826ae985f63a4244aee4554ec2d571f0 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 13:40:40 +0100 Subject: [PATCH 07/27] Update models.py --- onlinecourse/models.py | 72 ++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/onlinecourse/models.py b/onlinecourse/models.py index 4e8eedc10..fa7f8d100 100644 --- a/onlinecourse/models.py +++ b/onlinecourse/models.py @@ -17,7 +17,7 @@ class Instructor(models.Model): on_delete=models.CASCADE, ) full_time = models.BooleanField(default=True) - total_learners = models.IntegerField() + total_learners = models.IntegerField(default=0) def __str__(self): return self.user.username @@ -57,7 +57,7 @@ class Course(models.Model): name = models.CharField(null=False, max_length=30, default='online course') image = models.ImageField(upload_to='course_images/') description = models.CharField(max_length=1000) - pub_date = models.DateField(null=True) + pub_date = models.DateField(auto_now_add=True) instructors = models.ManyToManyField(Instructor) users = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Enrollment') total_enrollment = models.IntegerField(default=0) @@ -73,7 +73,7 @@ class Lesson(models.Model): title = models.CharField(max_length=200, default="title") order = models.IntegerField(default=0) course = models.ForeignKey(Course, on_delete=models.CASCADE) - content = models.TextField() + content = models.TextField(max_length=200, default="Programming...") # Enrollment model @@ -90,65 +90,55 @@ class Enrollment(models.Model): ] user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) course = models.ForeignKey(Course, on_delete=models.CASCADE) - date_enrolled = models.DateField(default=now) - mode = models.CharField(max_length=5, choices=COURSE_MODES, default=AUDIT) + date_enrolled = models.DateField(auto_now=True) + mode = models.CharField(max_length=50, choices=COURSE_MODES, default=AUDIT) rating = models.FloatField(default=5.0) +# <HINT> Create a Question Model with: + # Used to persist question content for a course + # Has a One-To-Many (or Many-To-Many if you want to reuse questions) relationship with course + # Has a grade point for each question + # Has question content + # Other fields and methods you would like to design class Question(models.Model): - text = models.TextField(max_length=500, default="question is...", null=False) - grade = models.CharField(default="U",, null=True) - lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE) - course = models.ManyToMany(Course , on_delete=models.SET_NULL) - A = 'A' - B ='B' - C ='C' - D= 'D' - Choices = [(A , 'A'), (B, 'B'),(C, 'C'), (D, 'D') ] - correct_ans = models.CharField(choices=Choices, default=A, null=False) # Foreign key to lesson + lesson_id = models.ForeignKey(Lesson, on_delete=models.CASCADE) # question text + question_text = models.TextField(max_length=1000 ,default='Question is') # question grade/mark + grade = models.IntegerField() # <HINT> A sample model method to calculate if learner get the score of the question 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 - + 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 + def __str__(self): + return self.question_text # <HINT> Create a Choice Model with: -class Choice(models.Model): # Used to persist choice content for a question # One-To-Many (or Many-To-Many if you want to reuse choices) relationship with Question - question = models.ForeignKey(Question, on_delete=models.CASCADE) # Choice content - A = 'A' - B ='B' - C ='C' - D= 'D' - Choices = [(A , 'A'), (B, 'B'),(C, 'C'), (D, 'D') ] - answer = models.CharField(max_length=500, choices=Choices, default=A) - def is_correct(self, answer): - correct_ans = question.correct_ans - if answer == correct_ans: - return True - else: - return False # 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): + question_id = models.ForeignKey(Question, on_delete=models.CASCADE) + choice_text = models.TextField(max_length=1000) + is_correct = models.BooleanField(default=False) + def __str__(self): + return self.choice_text # <HINT> The submission model # One enrollment could have multiple submission # One submission could have multiple choices # One choice could belong to multiple submissions class Submission(models.Model): - enrollment = models.ForeignKey(Enrollment, on_delete=models.PROTECT) + enrollment = models.ForeignKey(Enrollment, on_delete=models.CASCADE) chocies = models.ManyToManyField(Choice) - user = models.ManyToManyField(Learner) - course = models.ManyToManyField(Course) +# user = models.ManyToManyField(Learner) +# course = models.ManyToManyField(Course) #total = # Other fields and methods you would like to design From ef4e38a4c4495dfff4c6f709217820ca06e85239 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 13:54:05 +0100 Subject: [PATCH 08/27] Update views.py --- onlinecourse/views.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/onlinecourse/views.py b/onlinecourse/views.py index 11a80764a..12884a3d1 100644 --- a/onlinecourse/views.py +++ b/onlinecourse/views.py @@ -132,8 +132,17 @@ def extract_answers(request): # <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: 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) + context = {} + course = Course.objects.get(id = course_id) + submit = Submission.objects.get(id = submission_id) + selected = Submission.objects.filter(id = submission_id).values_list('choices',flat = True) + score = 0 + for i in submit.choices.all().filter(is_correct=True).values_list('question_id'): + score += Question.objects.filter(id=i[0]).first().grade + context['selected'] = selected + context['grade'] = score + context['course'] = course + return render(request, 'onlinecourse/exam_result_bootstrap.html', context) # Get course and submission based on their ids # Get the selected choice ids from the submission record From 90e36dbe34fcf6bb688bcf1faa7a9f6dc0177158 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 13:55:18 +0100 Subject: [PATCH 09/27] Update views.py --- onlinecourse/views.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/onlinecourse/views.py b/onlinecourse/views.py index 12884a3d1..1a86570cf 100644 --- a/onlinecourse/views.py +++ b/onlinecourse/views.py @@ -107,7 +107,14 @@ def enroll(request, course_id): def submit(request, course_id): course = get_object_or_404(Course, pk=course_id) user = request.user - Submission.objects.create(user=user, course=course ) + choices = extract_answers(request) + enrolled = Enrollment.objects.filter(user=user, course=course).get() + submission = Submission.objects.create(enrollment_id = enrolled.id ) + for choice in choices: + choi = Choice.objects.filter(id = int(choice)).get() + submission.choices.add(choi) + submission.save() + return HttpResponseRedirect(reverse(viewname='onlinecourse:exam_result', args=(course.id,submission.id ))) # Get user and course object, then get the associated enrollment object # created when the user enrolled the course From a8cd58c16a3d93a532d0b4627d5728bc59823a2b Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 13:56:37 +0100 Subject: [PATCH 10/27] Update urls.py --- onlinecourse/urls.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onlinecourse/urls.py b/onlinecourse/urls.py index 2960e1045..1643d6748 100644 --- a/onlinecourse/urls.py +++ b/onlinecourse/urls.py @@ -16,7 +16,8 @@ path('<int:pk>/', views.CourseDetailView.as_view(), name='course_details'), # ex: /enroll/5/ path('<int:course_id>/enroll/', views.enroll, name='enroll'), - + path('course/<int:course_id>/submission/<int:submission_id>/result/', \ + views.show_exam_result, name = 'exam_result'), # <HINT> Create a route for submit view # <HINT> Create a route for show_exam_result view From 2b094340f55830760dd95d1bd3d9ca70c1548531 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 13:56:52 +0100 Subject: [PATCH 11/27] Update urls.py --- onlinecourse/urls.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/onlinecourse/urls.py b/onlinecourse/urls.py index 1643d6748..a90d24875 100644 --- a/onlinecourse/urls.py +++ b/onlinecourse/urls.py @@ -16,8 +16,7 @@ path('<int:pk>/', views.CourseDetailView.as_view(), name='course_details'), # ex: /enroll/5/ path('<int:course_id>/enroll/', views.enroll, name='enroll'), - path('course/<int:course_id>/submission/<int:submission_id>/result/', \ - views.show_exam_result, name = 'exam_result'), + path('course/<int:course_id>/submission/<int:submission_id>/result/', views.show_exam_result, name = 'exam_result'), # <HINT> Create a route for submit view # <HINT> Create a route for show_exam_result view From c610af5df995f7ac8b92ac4fbd664c2f4cd08b9e Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 15:01:58 +0100 Subject: [PATCH 12/27] corrected spelling error corrected spelling error --- onlinecourse/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/views.py b/onlinecourse/views.py index 1a86570cf..67804de0f 100644 --- a/onlinecourse/views.py +++ b/onlinecourse/views.py @@ -8,7 +8,7 @@ from django.views import generic from django.contrib.auth import login, logout, authenticate import logging -from .models import Submission, Learner, Choice, Question, Enrolement, Lesson, Course +from .models import Submission, Learner, Choice, Question, Enrollement, Lesson, Course # Get an instance of a logger logger = logging.getLogger(__name__) # Create your views here. From 19f9a2bc4e2c92c97dec975b3f9735033f1a1355 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 15:02:14 +0100 Subject: [PATCH 13/27] corrected spelling error corrected spelling error --- onlinecourse/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/views.py b/onlinecourse/views.py index 67804de0f..6cb37aa43 100644 --- a/onlinecourse/views.py +++ b/onlinecourse/views.py @@ -8,7 +8,7 @@ from django.views import generic from django.contrib.auth import login, logout, authenticate import logging -from .models import Submission, Learner, Choice, Question, Enrollement, Lesson, Course +from .models import Submission, Learner, Choice, Question, Enrollment, Lesson, Course # Get an instance of a logger logger = logging.getLogger(__name__) # Create your views here. From 8909a9c86fddd650d08dc4cabb3afe395d4c23b1 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 15:06:04 +0100 Subject: [PATCH 14/27] Update admin.py --- onlinecourse/admin.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/onlinecourse/admin.py b/onlinecourse/admin.py index 8b42dedf1..33aeb44d2 100644 --- a/onlinecourse/admin.py +++ b/onlinecourse/admin.py @@ -1,15 +1,12 @@ from django.contrib import admin # <HINT> Import any new Models here -from .models import Course, Lesson, Instructor, Learner, Question, Choice, Submission +from .models import Question, Choice, Course, Lesson, Instructor, Learner # <HINT> Register QuestionInline and ChoiceInline classes here - class LessonInline(admin.StackedInline): model = Lesson extra = 5 - - # Register your models here. class CourseAdmin(admin.ModelAdmin): inlines = [LessonInline] @@ -17,16 +14,22 @@ class CourseAdmin(admin.ModelAdmin): list_filter = ['pub_date'] search_fields = ['name', 'description'] +class ChoiceInline(admin.StackedInline): + model = Choice + extra = 5 class LessonAdmin(admin.ModelAdmin): list_display = ['title'] +class QuestionAdmin(admin.ModelAdmin): + inlines = [ChoiceInline] + fields = ('question_text', 'grade', 'lesson_id') -# <HINT> Register Question and Choice models here + admin.site.register(Course, CourseAdmin) admin.site.register(Lesson, LessonAdmin) admin.site.register(Instructor) admin.site.register(Learner) -admin.site.register(Question) +admin.site.register(Question, QuestionAdmin) admin.site.register(Choice) From 3794705a480db4e05895a9441ed8342413f7e74e Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 18:24:08 +0100 Subject: [PATCH 15/27] changed 20.0.4 to gunicorn==20.1.0 changed to gunicorn==20.1.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e8db24a8e..bd4493a08 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ Django==3.1.3 -gunicorn==20.0.4 +gunicorn==20.1.0 Pillow==8.0.1 From 05629a181f7c13831da5ce6d2f8be4a1b529bdc9 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 18:30:43 +0100 Subject: [PATCH 16/27] Update urls.py --- onlinecourse/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/urls.py b/onlinecourse/urls.py index a90d24875..eec45a4ff 100644 --- a/onlinecourse/urls.py +++ b/onlinecourse/urls.py @@ -16,7 +16,7 @@ path('<int:pk>/', views.CourseDetailView.as_view(), name='course_details'), # ex: /enroll/5/ path('<int:course_id>/enroll/', views.enroll, name='enroll'), - path('course/<int:course_id>/submission/<int:submission_id>/result/', views.show_exam_result, name = 'exam_result'), + path('course/<int:course_id>/submission/<int:submission_id>/result/', views.submit, name = 'exam_result'), # <HINT> Create a route for submit view # <HINT> Create a route for show_exam_result view From 26ec4abdee3b7d84ad3e0cc18bf8b88aa74d1a0a Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 18:31:13 +0100 Subject: [PATCH 17/27] Update views.py --- onlinecourse/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/views.py b/onlinecourse/views.py index 6cb37aa43..0f53852f7 100644 --- a/onlinecourse/views.py +++ b/onlinecourse/views.py @@ -138,7 +138,7 @@ def extract_answers(request): # <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: -def show_exam_result(request, course_id, submission_id): +def submit(request, course_id, submission_id): context = {} course = Course.objects.get(id = course_id) submit = Submission.objects.get(id = submission_id) From 5734cd203a9d8167a08bef84e6c4221960a19a69 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 18:32:04 +0100 Subject: [PATCH 18/27] Update views.py --- onlinecourse/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/views.py b/onlinecourse/views.py index 0f53852f7..ada455471 100644 --- a/onlinecourse/views.py +++ b/onlinecourse/views.py @@ -104,7 +104,7 @@ def enroll(request, course_id): # <HINT> Create a submit view to create an exam submission record for a course enrollment, # you may implement it based on following logic: -def submit(request, course_id): +def show_exam_result(request, course_id): course = get_object_or_404(Course, pk=course_id) user = request.user choices = extract_answers(request) From bd06d2306300b9d93e315ba8bdd1404bc781676b Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 18:32:37 +0100 Subject: [PATCH 19/27] Update urls.py --- onlinecourse/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/urls.py b/onlinecourse/urls.py index eec45a4ff..a90d24875 100644 --- a/onlinecourse/urls.py +++ b/onlinecourse/urls.py @@ -16,7 +16,7 @@ path('<int:pk>/', views.CourseDetailView.as_view(), name='course_details'), # ex: /enroll/5/ path('<int:course_id>/enroll/', views.enroll, name='enroll'), - path('course/<int:course_id>/submission/<int:submission_id>/result/', views.submit, name = 'exam_result'), + path('course/<int:course_id>/submission/<int:submission_id>/result/', views.show_exam_result, name = 'exam_result'), # <HINT> Create a route for submit view # <HINT> Create a route for show_exam_result view From fcd52fd2bc23468c6e7c8cadc2b6bf172aef02fe Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 18:41:47 +0100 Subject: [PATCH 20/27] Update urls.py --- onlinecourse/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/urls.py b/onlinecourse/urls.py index a90d24875..f7ce58511 100644 --- a/onlinecourse/urls.py +++ b/onlinecourse/urls.py @@ -9,9 +9,9 @@ # view refers to the view function # name the URL path(route='', view=views.CourseListView.as_view(), name='index'), - path('registration/', views.registration_request, name='registration'), path('login/', views.login_request, name='login'), path('logout/', views.logout_request, name='logout'), + path('registration/', views.registration_request, name='registration'), # ex: /onlinecourse/5/ path('<int:pk>/', views.CourseDetailView.as_view(), name='course_details'), # ex: /enroll/5/ From 925b2805258132e67781dff86e401867b07d0f50 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 18:43:34 +0100 Subject: [PATCH 21/27] Update exam_result_bootstrap.html --- .../onlinecourse/exam_result_bootstrap.html | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html b/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html index 264728f5c..25d95e178 100644 --- a/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html +++ b/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html @@ -35,21 +35,47 @@ </div> </nav> -<div class="container-fluid"> +<div class="container-fluid" style='padding: 20px;'> {% if grade > 80 %} <div class="alert alert-success"> <!--HINT Display passed info --> + <p><b>Congratulations, {{user.first_name}}! </b>You have passed the exam and completed the course with score {{grade}} / 100</p> </div> {% else %} <div class="alert alert-danger"> <!--HINT Display failed info --> + <p><b>Failed. </b>Sorry, {{user.first_name}}! You have failed the exam with score {{grade}} / 100</p> </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 each question in the course ...--> + {% for lesson in course.lesson_set.all %} + {% for question in lesson.question_set.all %} + <div class="card mt-1"> + <div class="card-header"><h5 style="font-weight:bold">{{ question.question_text}}</h5></div> + <div class="form-group"> + <!--... for each choice in the question choice_{{choice.id}}...--> + {% for choice in question.choice_set.all %} + <div class="form-check"> + {% if choice.id in selected %} + {% if choice.is_correct == True %} + <label style="color:#2ea94a;font-weight:bold">Correct answer: {{choice.choice_text}}</label> + {% else %} + <label style="color:red;font-weight:bold">Wrong answer: {{choice.choice_text}}</label> + {% endif %} + {% else %} + <label>{{choice.choice_text}}</label> + {% endif %} + </div> + {% endfor %} + </div> + </div> + {% endfor %} + {% endfor %} </div> </div> </body> -</html> \ No newline at end of file +</html> From 630b122902ce9e1ac7c18510e4a4de1041474759 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 18:44:53 +0100 Subject: [PATCH 22/27] Update course_detail_bootstrap.html --- .../onlinecourse/course_detail_bootstrap.html | 109 +++++++++--------- 1 file changed, 56 insertions(+), 53 deletions(-) diff --git a/onlinecourse/templates/onlinecourse/course_detail_bootstrap.html b/onlinecourse/templates/onlinecourse/course_detail_bootstrap.html index 5a85e1de8..91f10d2e9 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> @@ -41,70 +41,73 @@ <!-- Page content --> <div class="container-fluid"> - <h2>{{ course.name }}</h2> - <div class="card-columns-vertical"> - {% for lesson in course.lesson_set.all %} - <div class="card mt-1"> - <div class="card-header"><h5>Lesson {{lesson.order|add:1}}: {{lesson.title}}</h5></div> - <div class="card-body">{{lesson.content}}</div> - </div> - {% endfor %} - </div> + <h2>{{ course.name }}</h2> + <div class="card-columns-vertical"> + {% for lesson in course.lesson_set.all %} + <div class="card mt-1"> + <div class="card-header"><h5>Lesson {{lesson.order|add:1}}: {{lesson.title}}</h5></div> + <div class="card-body">{{lesson.content}}</div> + </div> + {% endfor %} + </div> <!-- Task: show questions and choices --> <!-- <HINT> Use Bootstrap Collapse to hide exam first, more details could be found here https://www.w3schools.com/bootstrap4/bootstrap_collapse.asp--> - - <!-- - - A collapse example here: - - Click to expand elements within the collapse div --> - <div id="exam" class="collapse"> - </div> + <br> + <center><button data-toggle="collapse" data-target="#exam">Examination</button></center> + <br> + <div id="exam" class="collapse"> + {% if user.is_authenticated %} <!-- <HINT> If user is authenticated, show course exam with a list of question --> <!-- <HINT> Each example will have many questions --> <!-- <HINT> Each question will have many choices --> - {% if user.is_authenticated %} - <ul> - {% for question in question.all %} - - <li> - <p> {{question }}</p> - <ul> - {% for choice in question.choices %} - <li>{{choice }}</p> <br> - <input type="check" name="choice_{{choice.id}}" id="{{choice.id}}" ...></li></ul> - </li> - </ul> - - <!-- <HINT> Create a form to collect the selected choices for all questions --> <!-- <HINT> For each question choice, you may create a checkbox input like - + <input type="check" name="choice_{{choice.id}}" id="{{choice.id}}" ...> --> - - <!-- A choice submission form example --> - <form id="questionform" action="point to a submit view" method="post"> - ... for each question in the course ... - <div class="card mt-1"> - <div class="card-header"><h5>{{ question.question_text}}</h5></div> - {% csrf_token %} - <div class="form-group"> - ... for each choice in the question ... - <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.choice_text }} - </label> - </div> - </div> + <!-- A choice submission form example--> + <form id="questionform" action="submit/" method="post"> + <!--... for each question in the course ...--> + {% for lesson in course.lesson_set.all %} + {% for question in lesson.question_set.all %} + <div class="card mt-1"> + <div class="card-header"><h5>{{ question.question_text}}</h5></div> + {% csrf_token %} + <div class="form-group" style='padding: 20px;'> + <!--... for each choice in the question ...--> + {% 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.choice_text }} + </label> </div> - <input class="btn btn-success btn-block" type="submit" value="Submit"> + {% endfor %} + </div> + </div> + {% endfor %} + {% endfor %} + <br> + <input class="btn btn-success btn-block" type="submit" value="Submit"> + <br><br><br> </form> - <!--Check here to see more details Bootstrap checkbox - https://www.w3schools.com/bootstrap4/bootstrap_forms_inputs.asp--> + https://www.w3schools.com/bootstrap4/bootstrap_forms_inputs.asp--> + {% else %} + <li> + <form class="form-inline" action="{% url 'onlinecourse:login' %}" method="post"> + {% csrf_token %} + <div class="input-group"> + <input type="text" class="form-control" placeholder="Username" name="username" > + <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> + </form> + </li> + {% endif %} + </div> </div> </body> </html> From 5aa67d1dbc01ee5d6cd5a7093376a6b656fcd3b3 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 23:03:16 +0100 Subject: [PATCH 23/27] Update urls.py --- onlinecourse/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/urls.py b/onlinecourse/urls.py index f7ce58511..5ac2400b0 100644 --- a/onlinecourse/urls.py +++ b/onlinecourse/urls.py @@ -16,7 +16,7 @@ path('<int:pk>/', views.CourseDetailView.as_view(), name='course_details'), # ex: /enroll/5/ path('<int:course_id>/enroll/', views.enroll, name='enroll'), - path('course/<int:course_id>/submission/<int:submission_id>/result/', views.show_exam_result, name = 'exam_result'), + path('onlinecourse/<int:course_id>/submit/<int:submission_id>/result/', views.show_exam_result, name = 'exam_result'), # <HINT> Create a route for submit view # <HINT> Create a route for show_exam_result view From 83e64323ef069643d136833a046eb4db73a01fa5 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sat, 30 Jul 2022 23:09:43 +0100 Subject: [PATCH 24/27] Update urls.py --- onlinecourse/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onlinecourse/urls.py b/onlinecourse/urls.py index 5ac2400b0..f7ce58511 100644 --- a/onlinecourse/urls.py +++ b/onlinecourse/urls.py @@ -16,7 +16,7 @@ path('<int:pk>/', views.CourseDetailView.as_view(), name='course_details'), # ex: /enroll/5/ path('<int:course_id>/enroll/', views.enroll, name='enroll'), - path('onlinecourse/<int:course_id>/submit/<int:submission_id>/result/', views.show_exam_result, name = 'exam_result'), + path('course/<int:course_id>/submission/<int:submission_id>/result/', views.show_exam_result, name = 'exam_result'), # <HINT> Create a route for submit view # <HINT> Create a route for show_exam_result view From b43ffa28bc185d7d46823da29e53274cadd755f5 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sun, 31 Jul 2022 17:18:40 +0100 Subject: [PATCH 25/27] Update views.py --- onlinecourse/views.py | 46 ++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/onlinecourse/views.py b/onlinecourse/views.py index ada455471..cfd37b854 100644 --- a/onlinecourse/views.py +++ b/onlinecourse/views.py @@ -104,18 +104,40 @@ def enroll(request, course_id): # <HINT> Create a submit view to create an exam submission record for a course enrollment, # you may implement it based on following logic: -def show_exam_result(request, course_id): - course = get_object_or_404(Course, pk=course_id) - user = request.user - choices = extract_answers(request) - enrolled = Enrollment.objects.filter(user=user, course=course).get() - submission = Submission.objects.create(enrollment_id = enrolled.id ) - for choice in choices: - choi = Choice.objects.filter(id = int(choice)).get() - submission.choices.add(choi) - submission.save() - return HttpResponseRedirect(reverse(viewname='onlinecourse:exam_result', args=(course.id,submission.id ))) - +# def show_exam_result(request, course_id): +# course = get_object_or_404(Course, pk=course_id) +# user = request.user +# choices = extract_answers(request) +# enrolled = Enrollment.objects.filter(user=user, course=course).get() +# submission = Submission.objects.create(enrollment_id = enrolled.id ) +# for choice in choices: +# choi = Choice.objects.filter(id = int(choice)).get() +# submission.choices.add(choi) +# submission.save() +# return HttpResponseRedirect(reverse(viewname='onlinecourse:exam_result', args=(course.id,submission.id ))) + +def show_exam_result(request, course_id, submission_id): + context = {} + course = Course.objects.get(pk = course_id) + total_answers = extract_answers(request) + submission = Submission.objects.get(pk = submission_id) + lesson = Lesson.objects.get(course_id = course_id) + number_of_questions = Question.objects.filter(lesson_id = lesson.pk).aggregate(Count('pk')) + number_of_questions = number_of_questions['pk__count'] + score:float = 100.00 + questions = Question.objects.filter(lesson_id = lesson.pk) + + for question in questions: + if not question.is_get_score(total_answers): + score = score - (100/number_of_questions) + + + + context['lesson'] = lesson + context['selected_ids'] = total_answers + context['course'] = course + context['grade'] = int(score) + return render(request, 'onlinecourse/exam_result_bootstrap.html', context) # Get user and course object, then get the associated enrollment object # created when the user enrolled the course # Create a submission object referring to the enrollment From afeeee376aeaef3b350ef03f816e6395ea59e206 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sun, 31 Jul 2022 17:19:22 +0100 Subject: [PATCH 26/27] Update exam_result_bootstrap.html --- .../onlinecourse/exam_result_bootstrap.html | 74 ++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html b/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html index 25d95e178..8af098010 100644 --- a/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html +++ b/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html @@ -1,4 +1,4 @@ -<!DOCTYPE html> +<!-- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> @@ -78,4 +78,76 @@ <h5 class="">Exam results</h5> </div> </div> </body> +</html> --> +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + {% load static %} + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> +</head> +<body> + + <nav class="navbar navbar-light bg-light"> + <div class="container-fluid"> + <div class="navbar-header"> + <a class="navbar-brand" href="{% url 'onlinecourse:index' %}">Home</a> + </div> + <ul class="nav navbar-nav navbar-right"> + {% if user.is_authenticated %} + <li> + <a class="btn btn-link" href="#">{{ user.first_name }}({{ user.username }})</a> + <a class="btn btn-link" href="{% url 'onlinecourse:logout' %}">Logout</a> + </li> + {% else %} + <li> + <form class="form-inline" action="{% url 'onlinecourse:login' %}" method="post"> + {% 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" > + <button class="btn btn-primary" type="submit">Login</button> + <a class="btn btn-link" href="{% url 'onlinecourse:registration' %}">Sign Up</a> + </div> + </form> + </li> + {% endif %} + </ul> + </div> +</nav> + +<div class="container-fluid"> + {% if grade > 80 %} + <div class="alert alert-success"> + <b>Congratulations, {{user.username}}!</b>You have passed the exam and completed the course with score {{grade}} / 100</div> + {% else %} + <div class="alert alert-danger"><b>Failed </b></v>Sorry, {{user.username}}! You have failed exam with score {{grade}} / 100</div> + <a class="btn btn-link text-danger" href="{% url 'onlinecourse:course_details' course.id %}">Re-test</a> + {% endif %} + <div class="card-columns-vertical"> + <h5 class="">Exam results</h5> + {% for question in lesson.question_set.all %} + <h5 class = "card-header border rounded-top">{{question.question_content}}</h5> + <div class = "card-body border rounded-bottom align-top mt-1 pt-1"> + {% for choice in question.choice_set.all %} + {% if choice.pk in selected_ids and choice.is_correct %} + <div class = "text-success h-25 align-top">Correct Answer: {{choice.choice_text}}</div> + {% elif choice.pk in selected_ids and not choice.is_correct %} + <div class = "text-danger h-25">Incorrect Answer: {{choice.choice_text}}</div> + {% elif choice.pk not in selected_ids and choice.is_correct %} + <div class = "text-warning h-25">Not selected: {{choice.choice_text}}</div> + {% else %} + <p>{{choice.choice_text}}</p> + {% endif %} + {% endfor %} + </div> + {% endfor %} + </div> + </div> +</body> </html> +Footer +© 2022 GitHub, Inc. +Footer navigation +Terms +Privacy From 13a2837bd8733f68e5d1aa208297366da54c89d1 Mon Sep 17 00:00:00 2001 From: Behrooz <40363979+BehroozMoniri@users.noreply.github.com> Date: Sun, 31 Jul 2022 17:20:03 +0100 Subject: [PATCH 27/27] Update exam_result_bootstrap.html --- .../templates/onlinecourse/exam_result_bootstrap.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html b/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html index 8af098010..4e945a4ae 100644 --- a/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html +++ b/onlinecourse/templates/onlinecourse/exam_result_bootstrap.html @@ -79,6 +79,7 @@ <h5 class="">Exam results</h5> </div> </body> </html> --> + <!DOCTYPE html> <html lang="en"> <head> @@ -146,8 +147,3 @@ <h5 class = "card-header border rounded-top">{{question.question_content}}</h5> </div> </body> </html> -Footer -© 2022 GitHub, Inc. -Footer navigation -Terms -Privacy