Commit 749cb4ee by Sarina Canelake

Merge pull request #4031 from edx/sarina/other-ok-fixes

Sarina/other ok fixes
parents 305c126e bf6c342d
...@@ -22,7 +22,7 @@ from selenium.common.exceptions import ( ...@@ -22,7 +22,7 @@ from selenium.common.exceptions import (
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait 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 = { REQUIREJS_WAIT = {
...@@ -306,6 +306,25 @@ def css_has_text(css_selector, text, index=0, strip=False): ...@@ -306,6 +306,25 @@ def css_has_text(css_selector, text, index=0, strip=False):
@world.absorb @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): def css_has_value(css_selector, value, index=0):
""" """
Return a boolean indicating whether the element with Return a boolean indicating whether the element with
......
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 certificates.models import GeneratedCertificate
from django.contrib.auth.models import User from django.contrib.auth.models import User
from optparse import make_option from optparse import make_option
from django.conf import settings from django.conf import settings
from opaque_keys import InvalidKeyError
from xmodule.course_module import CourseDescriptor 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 xmodule.modulestore.django import modulestore
from django.db.models import Count from django.db.models import Count
...@@ -12,7 +19,7 @@ class Command(BaseCommand): ...@@ -12,7 +19,7 @@ class Command(BaseCommand):
help = """ 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 This command does not do anything other than report the current
certificate status. certificate status.
...@@ -34,81 +41,89 @@ class Command(BaseCommand): ...@@ -34,81 +41,89 @@ class Command(BaseCommand):
dest='course', dest='course',
default=None, default=None,
help='Only generate for COURSE_ID'), 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): def handle(self, *args, **options):
# Find all courses that have ended # Find all courses that have ended
if options['course']: 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: else:
ended_courses = self._ended_courses() raise CommandError("You must specify a course")
cert_data = {} cert_data = {}
for course_id in ended_courses: # find students who are active
# number of enrolled students = downloadable + notpassing
# find students who are active print "Looking up certificate states for {0}".format(options['course'])
# enrolled students are always downloable + notpassing enrolled_current = User.objects.filter(
print "Looking up certificate states for {0}".format(course_id) courseenrollment__course_id=course_id,
enrolled_current = User.objects.filter( courseenrollment__is_active=True
courseenrollment__course_id=course_id, )
courseenrollment__is_active=True) enrolled_total = User.objects.filter(
enrolled_total = User.objects.filter( courseenrollment__course_id=course_id
courseenrollment__course_id=course_id) )
verified_enrolled = GeneratedCertificate.objects.filter( verified_enrolled = GeneratedCertificate.objects.filter(
course_id__exact=course_id, mode__exact='verified') course_id__exact=course_id, mode__exact='verified'
honor_enrolled = GeneratedCertificate.objects.filter( )
course_id__exact=course_id, mode__exact='honor') honor_enrolled = GeneratedCertificate.objects.filter(
audit_enrolled = GeneratedCertificate.objects.filter( course_id__exact=course_id, mode__exact='honor'
course_id__exact=course_id, mode__exact='audit') )
audit_enrolled = GeneratedCertificate.objects.filter(
cert_data[course_id] = {'enrolled_current': enrolled_current.count(), course_id__exact=course_id, mode__exact='audit'
'enrolled_total': enrolled_total.count(), )
'verified_enrolled': verified_enrolled.count(),
'honor_enrolled': honor_enrolled.count(), cert_data[course_id] = {
'audit_enrolled': audit_enrolled.count()} 'enrolled_current': enrolled_current.count(),
'enrolled_total': enrolled_total.count(),
status_tally = GeneratedCertificate.objects.filter( 'verified_enrolled': verified_enrolled.count(),
course_id__exact=course_id).values('status').annotate( 'honor_enrolled': honor_enrolled.count(),
dcount=Count('status')) 'audit_enrolled': audit_enrolled.count()
cert_data[course_id].update( }
{status['status']: status['dcount']
for status in status_tally}) status_tally = GeneratedCertificate.objects.filter(
course_id__exact=course_id
mode_tally = GeneratedCertificate.objects.filter( ).values('status').annotate(
course_id__exact=course_id, dcount=Count('status')
status__exact='downloadable').values('mode').annotate( )
dcount=Count('mode'))
cert_data[course_id].update( cert_data[course_id].update(
{mode['mode']: mode['dcount'] {status['status']: status['dcount']
for mode in mode_tally}) 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}
)
# all states we have seen far all courses # all states we have seen far all courses
status_headings = sorted(set( status_headings = sorted(set(
[status for course in cert_data [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 the heading for the report
print "{:>26}".format("course ID"), print "{:>26}".format("course ID"),
print ' '.join(["{:>16}".format(heading) print ' '.join(["{:>16}".format(heading)
for heading in status_headings]) for heading in status_headings]
)
# print the report # print the report
for course_id in cert_data: print "{0:>26}".format(course_id.to_deprecated_string()),
print "{0:>26}".format(course_id[0:24]), for heading in status_headings:
for heading in status_headings: if heading in cert_data[course_id]:
if heading in cert_data[course_id]: print "{:>16}".format(cert_data[course_id][heading]),
print "{:>16}".format(cert_data[course_id][heading]), else:
else: print " " * 16,
print " " * 16, print
print
# pylint: disable=C0111
# pylint: disable=W0621
from lettuce import world, steps 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 common import i_am_registered_for_the_course, visit_scenario_item
from problems_setup import add_problem_to_course, answer_problem from problems_setup import add_problem_to_course, answer_problem
...@@ -90,6 +92,7 @@ class ConditionalSteps(object): ...@@ -90,6 +92,7 @@ class ConditionalSteps(object):
assert_true(world.css_visible('.hidden-contents')) assert_true(world.css_visible('.hidden-contents'))
else: else:
assert_true(world.is_css_not_present('.hidden-contents')) 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): def answer_poll(self, step, answer):
r' I answer the conditioned poll "([^"]*)"$' r' I answer the conditioned poll "([^"]*)"$'
...@@ -116,4 +119,4 @@ class ConditionalSteps(object): ...@@ -116,4 +119,4 @@ class ConditionalSteps(object):
return return
ConditionalSteps() ConditionalSteps()
\ No newline at end of file
...@@ -5,11 +5,11 @@ from django.core.urlresolvers import reverse ...@@ -5,11 +5,11 @@ from django.core.urlresolvers import reverse
def _message(reqm, message): def _message(reqm, message):
return message.format(link="<a href={url}>{url_name}</a>".format( return message.format(link="<a href={url}>{url_name}</a>".format(
url = reverse('jump_to', kwargs=dict(course_id=reqm.course_id.to_deprecated_string(), 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)) url_name = reqm.display_name_with_default))
%> %>
% if message: % if message:
% for reqm in module.required_modules: % for reqm in module.required_modules:
<p>${_message(reqm, message)}</p> <p class="conditional-message">${_message(reqm, message)}</p>
% endfor % endfor
% endif % endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment