diff --git a/common/djangoapps/terrain/ui_helpers.py b/common/djangoapps/terrain/ui_helpers.py index 38e984e41a..7260f70314 100644 --- a/common/djangoapps/terrain/ui_helpers.py +++ b/common/djangoapps/terrain/ui_helpers.py @@ -22,7 +22,7 @@ from selenium.common.exceptions import ( from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait -from nose.tools import assert_true # pylint: disable=E0611 +from nose.tools import assert_true # pylint: disable=no-name-in-module REQUIREJS_WAIT = { @@ -305,6 +305,25 @@ def css_has_text(css_selector, text, index=0, strip=False): return actual_text == text +@world.absorb +def css_contains_text(css_selector, partial_text, index=0): + """ + Return a boolean indicating whether the element with `css_selector` + contains `partial_text`. + + If there are multiple elements matching the css selector, + use `index` to indicate which one. + """ + # If we're expecting a non-empty string, give the page + # a chance to fill in text fields. + if partial_text: + wait_for(lambda _: css_text(css_selector, index=index)) + + actual_text = css_text(css_selector, index=index) + + return partial_text in actual_text + + @world.absorb def css_has_value(css_selector, value, index=0): """ diff --git a/lms/djangoapps/certificates/management/commands/gen_cert_report.py b/lms/djangoapps/certificates/management/commands/gen_cert_report.py index 018e5e592b..3c37069753 100644 --- a/lms/djangoapps/certificates/management/commands/gen_cert_report.py +++ b/lms/djangoapps/certificates/management/commands/gen_cert_report.py @@ -1,9 +1,16 @@ -from django.core.management.base import BaseCommand +""" +Generate a report of certificate statuses +""" + +from django.core.management.base import BaseCommand, CommandError from certificates.models import GeneratedCertificate from django.contrib.auth.models import User from optparse import make_option from django.conf import settings +from opaque_keys import InvalidKeyError from xmodule.course_module import CourseDescriptor +from xmodule.modulestore.keys import CourseKey +from xmodule.modulestore.locations import SlashSeparatedCourseKey from xmodule.modulestore.django import modulestore from django.db.models import Count @@ -12,7 +19,7 @@ class Command(BaseCommand): help = """ - Generate a certificate status report for all courses that have ended. + Generate a certificate status report for a given course. This command does not do anything other than report the current certificate status. @@ -34,81 +41,89 @@ class Command(BaseCommand): dest='course', default=None, help='Only generate for COURSE_ID'), - ) - - def _ended_courses(self): - for course_id in [course # all courses in COURSE_LISTINGS - for sub in settings.COURSE_LISTINGS - for course in settings.COURSE_LISTINGS[sub]]: - course = modulestore().get_course(course_id) - if course.has_ended(): - yield course_id + ) def handle(self, *args, **options): # Find all courses that have ended if options['course']: - ended_courses = [options['course']] + try: + course_id = CourseKey.from_string(options['course']) + except InvalidKeyError: + print("Course id {} could not be parsed as a CourseKey; falling back to SSCK.from_dep_str".format(options['course'])) + course_id = SlashSeparatedCourseKey.from_deprecated_string(options['course']) else: - ended_courses = self._ended_courses() + raise CommandError("You must specify a course") cert_data = {} - for course_id in ended_courses: + # find students who are active + # number of enrolled students = downloadable + notpassing + print "Looking up certificate states for {0}".format(options['course']) + enrolled_current = User.objects.filter( + courseenrollment__course_id=course_id, + courseenrollment__is_active=True + ) + enrolled_total = User.objects.filter( + courseenrollment__course_id=course_id + ) + verified_enrolled = GeneratedCertificate.objects.filter( + course_id__exact=course_id, mode__exact='verified' + ) + honor_enrolled = GeneratedCertificate.objects.filter( + course_id__exact=course_id, mode__exact='honor' + ) + audit_enrolled = GeneratedCertificate.objects.filter( + course_id__exact=course_id, mode__exact='audit' + ) - # find students who are active - # enrolled students are always downloable + notpassing - print "Looking up certificate states for {0}".format(course_id) - enrolled_current = User.objects.filter( - courseenrollment__course_id=course_id, - courseenrollment__is_active=True) - enrolled_total = User.objects.filter( - courseenrollment__course_id=course_id) - verified_enrolled = GeneratedCertificate.objects.filter( - course_id__exact=course_id, mode__exact='verified') - honor_enrolled = GeneratedCertificate.objects.filter( - course_id__exact=course_id, mode__exact='honor') - audit_enrolled = GeneratedCertificate.objects.filter( - course_id__exact=course_id, mode__exact='audit') + cert_data[course_id] = { + 'enrolled_current': enrolled_current.count(), + 'enrolled_total': enrolled_total.count(), + 'verified_enrolled': verified_enrolled.count(), + 'honor_enrolled': honor_enrolled.count(), + 'audit_enrolled': audit_enrolled.count() + } - cert_data[course_id] = {'enrolled_current': enrolled_current.count(), - 'enrolled_total': enrolled_total.count(), - 'verified_enrolled': verified_enrolled.count(), - 'honor_enrolled': honor_enrolled.count(), - 'audit_enrolled': audit_enrolled.count()} + status_tally = GeneratedCertificate.objects.filter( + course_id__exact=course_id + ).values('status').annotate( + dcount=Count('status') + ) - status_tally = GeneratedCertificate.objects.filter( - course_id__exact=course_id).values('status').annotate( - dcount=Count('status')) - cert_data[course_id].update( - {status['status']: status['dcount'] - for status in status_tally}) + cert_data[course_id].update( + {status['status']: status['dcount'] + for status in status_tally}) - mode_tally = GeneratedCertificate.objects.filter( - course_id__exact=course_id, - status__exact='downloadable').values('mode').annotate( - dcount=Count('mode')) - cert_data[course_id].update( - {mode['mode']: mode['dcount'] - for mode in mode_tally}) + mode_tally = GeneratedCertificate.objects.filter( + course_id__exact=course_id, + status__exact='downloadable' + ).values('mode').annotate( + dcount=Count('mode') + ) + cert_data[course_id].update( + {mode['mode']: mode['dcount'] + for mode in mode_tally} + ) # all states we have seen far all courses status_headings = sorted(set( [status for course in cert_data - for status in cert_data[course]])) + for status in cert_data[course]]) + ) # print the heading for the report print "{:>26}".format("course ID"), print ' '.join(["{:>16}".format(heading) - for heading in status_headings]) + for heading in status_headings] + ) # print the report - for course_id in cert_data: - print "{0:>26}".format(course_id[0:24]), - for heading in status_headings: - if heading in cert_data[course_id]: - print "{:>16}".format(cert_data[course_id][heading]), - else: - print " " * 16, - print + print "{0:>26}".format(course_id.to_deprecated_string()), + for heading in status_headings: + if heading in cert_data[course_id]: + print "{:>16}".format(cert_data[course_id][heading]), + else: + print " " * 16, + print diff --git a/lms/djangoapps/courseware/features/conditional.py b/lms/djangoapps/courseware/features/conditional.py index db6b0436cd..4ec593feb8 100644 --- a/lms/djangoapps/courseware/features/conditional.py +++ b/lms/djangoapps/courseware/features/conditional.py @@ -1,6 +1,8 @@ +# pylint: disable=C0111 +# pylint: disable=W0621 from lettuce import world, steps -from nose.tools import assert_in, assert_equals, assert_true +from nose.tools import assert_in, assert_true # pylint: disable=no-name-in-module from common import i_am_registered_for_the_course, visit_scenario_item from problems_setup import add_problem_to_course, answer_problem @@ -90,6 +92,7 @@ class ConditionalSteps(object): assert_true(world.css_visible('.hidden-contents')) else: assert_true(world.is_css_not_present('.hidden-contents')) + assert_true(world.css_contains_text('.conditional-message', 'must be attempted before this will become visible.')) # sarina def answer_poll(self, step, answer): r' I answer the conditioned poll "([^"]*)"$' @@ -116,4 +119,4 @@ class ConditionalSteps(object): return -ConditionalSteps() \ No newline at end of file +ConditionalSteps() diff --git a/lms/templates/conditional_module.html b/lms/templates/conditional_module.html index ff37a3521b..fa4b7f3a68 100644 --- a/lms/templates/conditional_module.html +++ b/lms/templates/conditional_module.html @@ -5,11 +5,11 @@ from django.core.urlresolvers import reverse def _message(reqm, message): return message.format(link="{url_name}".format( url = reverse('jump_to', kwargs=dict(course_id=reqm.course_id.to_deprecated_string(), - location=reqm.location.url())), + location=reqm.location.to_deprecated_string())), url_name = reqm.display_name_with_default)) %> % if message: % for reqm in module.required_modules: -
${_message(reqm, message)}
+ % endfor % endif