From 5bc0b4864a558363945c0cb701024b4898c3b9fb Mon Sep 17 00:00:00 2001 From: Victor Shnayder Date: Sat, 17 Nov 2012 15:24:23 -0500 Subject: [PATCH] moving cert link and survey logic into views, adding tests. In-progress. --- common/djangoapps/student/tests.py | 41 +++++++++++----- common/djangoapps/student/views.py | 77 ++++++++++++++++++++++++++++-- 2 files changed, 102 insertions(+), 16 deletions(-) diff --git a/common/djangoapps/student/tests.py b/common/djangoapps/student/tests.py index cde95153fd..8a46b2e458 100644 --- a/common/djangoapps/student/tests.py +++ b/common/djangoapps/student/tests.py @@ -6,11 +6,14 @@ Replace this with more appropriate tests for your application. """ import logging from datetime import datetime +from hashlib import sha1 from django.test import TestCase +from mock import patch, Mock from nose.plugins.skip import SkipTest from .models import User, UserProfile, CourseEnrollment, replicate_user, USER_FIELDS_TO_COPY +import .views COURSE_1 = 'edX/toy/2012_Fall' COURSE_2 = 'edx/full/6.002_Spring_2012' @@ -55,7 +58,7 @@ class ReplicationTest(TestCase): # This hasattr lameness is here because we don't want this test to be # triggered when we're being run by CMS tests (Askbot doesn't exist # there, so the test will fail). - # + # # seen_response_count isn't a field we care about, so it shouldn't have # been copied over. if hasattr(portal_user, 'seen_response_count'): @@ -74,7 +77,7 @@ class ReplicationTest(TestCase): # During this entire time, the user data should never have made it over # to COURSE_2 - self.assertRaises(User.DoesNotExist, + self.assertRaises(User.DoesNotExist, User.objects.using(COURSE_2).get, id=portal_user.id) @@ -108,19 +111,19 @@ class ReplicationTest(TestCase): # Grab all the copies we expect course_user = User.objects.using(COURSE_1).get(id=portal_user.id) self.assertEquals(portal_user, course_user) - self.assertRaises(User.DoesNotExist, + self.assertRaises(User.DoesNotExist, User.objects.using(COURSE_2).get, id=portal_user.id) course_enrollment = CourseEnrollment.objects.using(COURSE_1).get(id=portal_enrollment.id) self.assertEquals(portal_enrollment, course_enrollment) - self.assertRaises(CourseEnrollment.DoesNotExist, + self.assertRaises(CourseEnrollment.DoesNotExist, CourseEnrollment.objects.using(COURSE_2).get, id=portal_enrollment.id) course_user_profile = UserProfile.objects.using(COURSE_1).get(id=portal_user_profile.id) self.assertEquals(portal_user_profile, course_user_profile) - self.assertRaises(UserProfile.DoesNotExist, + self.assertRaises(UserProfile.DoesNotExist, UserProfile.objects.using(COURSE_2).get, id=portal_user_profile.id) @@ -174,30 +177,44 @@ class ReplicationTest(TestCase): portal_user.save() portal_user_profile.gender = 'm' portal_user_profile.save() - - # Grab all the copies we expect, and make sure it doesn't end up in + + # Grab all the copies we expect, and make sure it doesn't end up in # places we don't expect. course_user = User.objects.using(COURSE_1).get(id=portal_user.id) self.assertEquals(portal_user, course_user) - self.assertRaises(User.DoesNotExist, + self.assertRaises(User.DoesNotExist, User.objects.using(COURSE_2).get, id=portal_user.id) course_enrollment = CourseEnrollment.objects.using(COURSE_1).get(id=portal_enrollment.id) self.assertEquals(portal_enrollment, course_enrollment) - self.assertRaises(CourseEnrollment.DoesNotExist, + self.assertRaises(CourseEnrollment.DoesNotExist, CourseEnrollment.objects.using(COURSE_2).get, id=portal_enrollment.id) course_user_profile = UserProfile.objects.using(COURSE_1).get(id=portal_user_profile.id) self.assertEquals(portal_user_profile, course_user_profile) - self.assertRaises(UserProfile.DoesNotExist, + self.assertRaises(UserProfile.DoesNotExist, UserProfile.objects.using(COURSE_2).get, id=portal_user_profile.id) +class CourseEndingTest(TestCase): + """Test things related to course endings: certificates, surveys, etc""" + def test_process_survey_link(self): + username = "fred" + id = sha1(username) + link1 = "http://www.mysurvey.com" + self.assertEqual(process_survey_link(link1), link1) + link2 = "http://www.mysurvey.com?unique={UNIQUE_ID}" + link2_expected = "http://www.mysurvey.com?unique={UNIQUE_ID}".format(UNIQUE_ID=id) + self.assertEqual(views.process_survey_link(link2), link2_expected) + def test_cert_info(self): + user = Mock(username="fred") + survey_url = "http://a_survey.com" + course = Mock(end_of_course_survey_url=survey_url) + cert_status = None - - + self.assertEqual(views._cert_info(user, course, None), {'status': 'processing'}) diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index e7562f83d0..2ebb98da7a 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -39,6 +39,8 @@ from xmodule.modulestore.exceptions import ItemNotFoundError from datetime import date from collections import namedtuple +from hashlib import sha1 + from courseware.courses import get_courses_by_university from courseware.access import has_access @@ -107,9 +109,9 @@ def get_date_for_press(publish_date): # strip off extra months, and just use the first: date = re.sub(multimonth_pattern, ", ", publish_date) if re.search(day_pattern, date): - date = datetime.datetime.strptime(date, "%B %d, %Y") - else: - date = datetime.datetime.strptime(date, "%B, %Y") + date = datetime.datetime.strptime(date, "%B %d, %Y") + else: + date = datetime.datetime.strptime(date, "%B, %Y") return date def press(request): @@ -127,6 +129,73 @@ def press(request): return render_to_response('static_templates/press.html', {'articles': articles}) +def process_survey_link(survey_link, user): + """ + If {UNIQUE_ID} appears in the link, replace it with a unique id for the user. + Currently, this is sha1(user.username). Otherwise, return survey_link. + """ + to_replace = '{UNIQUE_ID}' + if to_replace in survey_link: + unique_id = sha1(user.username) + return survey_link.replace(to_replace, unique_id) + + return survey_link + + +def cert_info(user, course): + """ + Get the certificate info needed to render the dashboard section for the given + student and course. Returns a dictionary with keys: + + 'status': one of 'generating', 'ready', 'notpassing', 'processing' + 'show_download_url': bool + 'download_url': url, only present if show_download_url is True + 'show_survey_button': bool + 'survey_url': url, only if show_survey_button is True + 'grade': if status is not 'processing' + """ + if not course.has_ended(): + return {} + + return _cert_info(user, course, certificate_status_for_student(user, course.id)) + +def _cert_info(user, course, cert_status): + """ + Implements the logic for cert_info -- split out for testing. + """ + default_status = 'processing' + if cert_status is None: + return {'status': default_status} + + # simplify the status for the template using this lookup table + template_state = { + CertificateStatuses.generating: 'generating', + CertificateStatuses.regenerating: 'generating', + CertificateStatuses.downloadable: 'ready', + CertificateStatuses.notpassing: 'notpassing', + } + + status = template_state.get(cert_status['status'], default_status) + + d = {'status': status, + 'show_download_url': status in ('generating', 'ready'),} + + if (status in ('generating', 'ready', 'not-available') and + course.end_of_course_survey_url is not None): + d.update({ + 'show_survey_button': True, + 'survey_url': process_survey_link(course.end_of_course_survey_url, user)}) + else: + d['show_survey_button'] = False + + if template_state == 'ready': + d['download_url'] = cert_status['download_url'] + + if template_state in 'generating', 'ready', 'notpassing': + d['grade'] = cert_status['grade'] + + return d + @login_required @ensure_csrf_cookie def dashboard(request): @@ -163,7 +232,7 @@ def dashboard(request): # TODO: workaround to not have to zip courses and certificates in the template # since before there is a migration to certificates if settings.MITX_FEATURES.get('CERTIFICATES_ENABLED'): - cert_statuses = { course.id: certificate_status_for_student(request.user, course.id) for course in courses} + cert_statuses = { course.id: cert_info(request.user, course) for course in courses} else: cert_statuses = {}