From e4852d1508d32a1c851bc04cebc3f41058ea5de3 Mon Sep 17 00:00:00 2001 From: pkulkark Date: Mon, 18 Oct 2021 18:07:14 +0530 Subject: [PATCH 1/5] feat: Add progress bar views This adds new views to return progress bars for course-level and chapter-level. --- .../chapter_completion_progress_bar.html | 4 ++ .../templates/completion_progress_bar.html | 4 ++ completion_aggregator/urls.py | 5 +- completion_aggregator/views.py | 54 +++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 completion_aggregator/templates/chapter_completion_progress_bar.html create mode 100644 completion_aggregator/templates/completion_progress_bar.html create mode 100644 completion_aggregator/views.py diff --git a/completion_aggregator/templates/chapter_completion_progress_bar.html b/completion_aggregator/templates/chapter_completion_progress_bar.html new file mode 100644 index 00000000..810f62ef --- /dev/null +++ b/completion_aggregator/templates/chapter_completion_progress_bar.html @@ -0,0 +1,4 @@ +
+ +
 
+
diff --git a/completion_aggregator/templates/completion_progress_bar.html b/completion_aggregator/templates/completion_progress_bar.html new file mode 100644 index 00000000..9c912361 --- /dev/null +++ b/completion_aggregator/templates/completion_progress_bar.html @@ -0,0 +1,4 @@ +
+ + {{completion_percentage}}% +
diff --git a/completion_aggregator/urls.py b/completion_aggregator/urls.py index d36755cc..63a5923b 100644 --- a/completion_aggregator/urls.py +++ b/completion_aggregator/urls.py @@ -5,10 +5,11 @@ from __future__ import absolute_import, division, print_function, unicode_literals from django.conf.urls import include, url -from django.views.generic import TemplateView +from . import views urlpatterns = [ url(r'^v1/', include('completion_aggregator.api.v1.urls', namespace='completion_api_v1')), url(r'^v0/', include('completion_aggregator.api.v0.urls', namespace='completion_api_v0')), - url(r'^$', TemplateView.as_view(template_name="completion_aggregator/base.html")), + url(r'^progress_bar/(?P[\w.@+:-]+)/$', views.CompletionProgressBarView.as_view(), name="completion-progress-bar"), + url(r'^progress_bar/(?P[\w.@+:-]+)/(?P.+)/$', views.CompletionProgressBarView.as_view(), name="completion-progress-bar"), ] diff --git a/completion_aggregator/views.py b/completion_aggregator/views.py new file mode 100644 index 00000000..4eb1aace --- /dev/null +++ b/completion_aggregator/views.py @@ -0,0 +1,54 @@ +""" +completion_aggregator App progress bar view +""" +from __future__ import absolute_import, unicode_literals + +from django.conf import settings +from django.contrib.auth.mixins import LoginRequiredMixin +from django.shortcuts import render +from django.views.decorators.clickjacking import xframe_options_exempt +from django.views.generic import TemplateView +from xblockutils.resources import ResourceLoader + +from .api.v1.views import CompletionDetailView + +loader = ResourceLoader(__name__) + + +class CompletionProgressBarView(LoginRequiredMixin, TemplateView): + """ + View to display the progress bar of a student in a course + """ + @xframe_options_exempt + def get(self, request, course_key, chapter_id=None): + """ + Fetch progress and render the template. + """ + username = request.user.username + completion_percentage = 0 + if chapter_id is not None: + new_req = request.GET.copy() + new_req['requested_fields'] = "chapter" + request.GET = new_req + completion_resp = CompletionDetailView.as_view()(request, course_key).data + if completion_resp: + results = completion_resp.get('results') + for user_completion_dict in results: + if user_completion_dict.get('username') == username: + if chapter_id is not None: + chapters = user_completion_dict['chapter'] + for chapter in chapters: + block_id = chapter['block_key'].split('@')[-1] + if block_id == chapter_id: + completion_percentage = float(chapter['completion']['percent']) * 100 + else: + completion_percentage = float(user_completion_dict['completion']['percent']) * 100 + + if chapter_id is not None: + return render(request, 'chapter_completion_progress_bar.html', { + 'chapter_completion_percentage': completion_percentage, + }) + else: + return render(request, 'completion_progress_bar.html', { + 'completion_percentage': completion_percentage, + }) From 5c22fdb52f842bdb0b82d1f95e14bd2ac08c69a6 Mon Sep 17 00:00:00 2001 From: pkulkark Date: Fri, 22 Oct 2021 16:20:30 +0530 Subject: [PATCH 2/5] fix: refactor based on review comments --- .../chapter_completion_progress_bar.html | 2 +- completion_aggregator/views.py | 49 ++++++++++++------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/completion_aggregator/templates/chapter_completion_progress_bar.html b/completion_aggregator/templates/chapter_completion_progress_bar.html index 810f62ef..d37d1cbf 100644 --- a/completion_aggregator/templates/chapter_completion_progress_bar.html +++ b/completion_aggregator/templates/chapter_completion_progress_bar.html @@ -1,4 +1,4 @@
-
 
+
 
diff --git a/completion_aggregator/views.py b/completion_aggregator/views.py index 4eb1aace..f181873e 100644 --- a/completion_aggregator/views.py +++ b/completion_aggregator/views.py @@ -19,12 +19,32 @@ class CompletionProgressBarView(LoginRequiredMixin, TemplateView): """ View to display the progress bar of a student in a course """ + def __get_user_completion(self, username, chapter_id, results): + """ + Return the user completion percentage, using the completion response. + + In case the user completion cannot be returned as a result of missing user completion, we return None, + indicating its absence. + """ + user_completion = next(filter(lambda r: r.get('username') == username, results), None) + + # No completion returned, hence we cannot get the percentage either + # Indicate no user completion by returning None + if not user_completion: + return None + + if chapter_id: + chapters = user_completion['chapter'] + chapter = next(filter(lambda c: c['block_key'].split('@')[-1] == chapter_id, chapters), None) + + completion_kind = chapter if chapter_id else user_completion + return float(completion_kind['completion']['percent']) * 100 + @xframe_options_exempt def get(self, request, course_key, chapter_id=None): """ Fetch progress and render the template. """ - username = request.user.username completion_percentage = 0 if chapter_id is not None: new_req = request.GET.copy() @@ -33,22 +53,13 @@ def get(self, request, course_key, chapter_id=None): completion_resp = CompletionDetailView.as_view()(request, course_key).data if completion_resp: results = completion_resp.get('results') - for user_completion_dict in results: - if user_completion_dict.get('username') == username: - if chapter_id is not None: - chapters = user_completion_dict['chapter'] - for chapter in chapters: - block_id = chapter['block_key'].split('@')[-1] - if block_id == chapter_id: - completion_percentage = float(chapter['completion']['percent']) * 100 - else: - completion_percentage = float(user_completion_dict['completion']['percent']) * 100 + user_completion_percentage = self.__get_user_completion(request.user.username, chapter_id, results) - if chapter_id is not None: - return render(request, 'chapter_completion_progress_bar.html', { - 'chapter_completion_percentage': completion_percentage, - }) - else: - return render(request, 'completion_progress_bar.html', { - 'completion_percentage': completion_percentage, - }) + if user_completion_percentage: + completion_percentage = user_completion_percentage + + template = 'chapter_completion_progress_bar.html' if chapter_id is not None else 'completion_progress_bar.html' + return render(request, template, { + 'completion_percentage': completion_percentage, + }) + From f5ae7ff96eaff95ad1dec6081edd25e4ac9b10ec Mon Sep 17 00:00:00 2001 From: pkulkark Date: Mon, 25 Oct 2021 14:44:41 +0530 Subject: [PATCH 3/5] fix: move internal function position --- completion_aggregator/views.py | 44 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/completion_aggregator/views.py b/completion_aggregator/views.py index f181873e..6de22c04 100644 --- a/completion_aggregator/views.py +++ b/completion_aggregator/views.py @@ -19,27 +19,6 @@ class CompletionProgressBarView(LoginRequiredMixin, TemplateView): """ View to display the progress bar of a student in a course """ - def __get_user_completion(self, username, chapter_id, results): - """ - Return the user completion percentage, using the completion response. - - In case the user completion cannot be returned as a result of missing user completion, we return None, - indicating its absence. - """ - user_completion = next(filter(lambda r: r.get('username') == username, results), None) - - # No completion returned, hence we cannot get the percentage either - # Indicate no user completion by returning None - if not user_completion: - return None - - if chapter_id: - chapters = user_completion['chapter'] - chapter = next(filter(lambda c: c['block_key'].split('@')[-1] == chapter_id, chapters), None) - - completion_kind = chapter if chapter_id else user_completion - return float(completion_kind['completion']['percent']) * 100 - @xframe_options_exempt def get(self, request, course_key, chapter_id=None): """ @@ -53,7 +32,7 @@ def get(self, request, course_key, chapter_id=None): completion_resp = CompletionDetailView.as_view()(request, course_key).data if completion_resp: results = completion_resp.get('results') - user_completion_percentage = self.__get_user_completion(request.user.username, chapter_id, results) + user_completion_percentage = self._get_user_completion(request.user.username, chapter_id, results) if user_completion_percentage: completion_percentage = user_completion_percentage @@ -62,4 +41,23 @@ def get(self, request, course_key, chapter_id=None): return render(request, template, { 'completion_percentage': completion_percentage, }) - + + def _get_user_completion(self, username, chapter_id, results): + """ + Return the user completion percentage, using the completion response. + In case the user completion cannot be returned as a result of missing user completion, we return None, + indicating its absence. + """ + user_completion = next(filter(lambda r: r.get('username') == username, results), None) + + # No completion returned, hence we cannot get the percentage either + # Indicate no user completion by returning None + if not user_completion: + return None + + if chapter_id: + chapters = user_completion['chapter'] + chapter = next(filter(lambda c: c['block_key'].split('@')[-1] == chapter_id, chapters), None) + + completion_kind = chapter if chapter_id else user_completion + return float(completion_kind['completion']['percent']) * 100 From cf899265659e154bcdaaf43a6ec425bc87ad005f Mon Sep 17 00:00:00 2001 From: pkulkark Date: Wed, 27 Oct 2021 13:57:42 +0530 Subject: [PATCH 4/5] fix: Add styling to html elements --- .../chapter_completion_progress_bar.html | 13 +++++++++++-- .../templates/completion_progress_bar.html | 17 ++++++++++++++--- completion_aggregator/views.py | 2 +- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/completion_aggregator/templates/chapter_completion_progress_bar.html b/completion_aggregator/templates/chapter_completion_progress_bar.html index d37d1cbf..748c50ac 100644 --- a/completion_aggregator/templates/chapter_completion_progress_bar.html +++ b/completion_aggregator/templates/chapter_completion_progress_bar.html @@ -1,4 +1,13 @@ +{% load i18n %}
- -
 
+ + 0% +
+
+ {{completion_percentage}}% +
+
+ 100%
diff --git a/completion_aggregator/templates/completion_progress_bar.html b/completion_aggregator/templates/completion_progress_bar.html index 9c912361..300ec883 100644 --- a/completion_aggregator/templates/completion_progress_bar.html +++ b/completion_aggregator/templates/completion_progress_bar.html @@ -1,4 +1,15 @@ -
- - {{completion_percentage}}% +{% load i18n %} + +
+ + 0% +
+
+ {{completion_percentage}}% +
+
+ 100%
+ diff --git a/completion_aggregator/views.py b/completion_aggregator/views.py index 6de22c04..0ac433e5 100644 --- a/completion_aggregator/views.py +++ b/completion_aggregator/views.py @@ -60,4 +60,4 @@ def _get_user_completion(self, username, chapter_id, results): chapter = next(filter(lambda c: c['block_key'].split('@')[-1] == chapter_id, chapters), None) completion_kind = chapter if chapter_id else user_completion - return float(completion_kind['completion']['percent']) * 100 + return round(float(completion_kind['completion']['percent']) * 100, 2) From ebe81ad567f5e51ce464a86d9403a95e09a0054f Mon Sep 17 00:00:00 2001 From: pkulkark Date: Tue, 2 Nov 2021 16:22:29 +0530 Subject: [PATCH 5/5] fix: update username in request to aviod permission denied --- completion_aggregator/views.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/completion_aggregator/views.py b/completion_aggregator/views.py index 0ac433e5..0f8b2afe 100644 --- a/completion_aggregator/views.py +++ b/completion_aggregator/views.py @@ -25,14 +25,15 @@ def get(self, request, course_key, chapter_id=None): Fetch progress and render the template. """ completion_percentage = 0 + new_req = request.GET.copy() + new_req['username'] = request.user.username if chapter_id is not None: - new_req = request.GET.copy() new_req['requested_fields'] = "chapter" - request.GET = new_req + request.GET = new_req completion_resp = CompletionDetailView.as_view()(request, course_key).data if completion_resp: results = completion_resp.get('results') - user_completion_percentage = self._get_user_completion(request.user.username, chapter_id, results) + user_completion_percentage = self._get_user_completion(chapter_id, results) if user_completion_percentage: completion_percentage = user_completion_percentage @@ -42,13 +43,16 @@ def get(self, request, course_key, chapter_id=None): 'completion_percentage': completion_percentage, }) - def _get_user_completion(self, username, chapter_id, results): + def _get_user_completion(self, chapter_id, results): """ Return the user completion percentage, using the completion response. In case the user completion cannot be returned as a result of missing user completion, we return None, indicating its absence. """ - user_completion = next(filter(lambda r: r.get('username') == username, results), None) + if not results: + return None + + user_completion = next(filter(lambda r: r, results), None) # No completion returned, hence we cannot get the percentage either # Indicate no user completion by returning None @@ -60,4 +64,8 @@ def _get_user_completion(self, username, chapter_id, results): chapter = next(filter(lambda c: c['block_key'].split('@')[-1] == chapter_id, chapters), None) completion_kind = chapter if chapter_id else user_completion - return round(float(completion_kind['completion']['percent']) * 100, 2) + + if not completion_kind: + return None + + return round(float(completion_kind['completion']['percent']) * 100)