@@ -133,6 +133,9 @@ AUDIT_LOG = logging.getLogger("audit")
|
||||
ReverifyInfo = namedtuple('ReverifyInfo', 'course_id course_name course_number date status display') # pylint: disable=invalid-name
|
||||
SETTING_CHANGE_INITIATED = 'edx.user.settings.change_initiated'
|
||||
|
||||
# Disable this warning because it doesn't make sense to completely refactor tests to appease Pylint
|
||||
# pylint: disable=logging-format-interpolation
|
||||
|
||||
|
||||
def csrf_token(context):
|
||||
"""A csrf token that can be included in a form."""
|
||||
@@ -296,12 +299,13 @@ def _cert_info(user, course_overview, cert_status, course_mode): # pylint: disa
|
||||
|
||||
default_status = 'processing'
|
||||
|
||||
default_info = {'status': default_status,
|
||||
'show_disabled_download_button': False,
|
||||
'show_download_url': False,
|
||||
'show_survey_button': False,
|
||||
'can_unenroll': True
|
||||
}
|
||||
default_info = {
|
||||
'status': default_status,
|
||||
'show_disabled_download_button': False,
|
||||
'show_download_url': False,
|
||||
'show_survey_button': False,
|
||||
'can_unenroll': True,
|
||||
}
|
||||
|
||||
if cert_status is None:
|
||||
return default_info
|
||||
@@ -500,9 +504,14 @@ def is_course_blocked(request, redeemed_registration_codes, course_key):
|
||||
u"User %s (%s) opted out of receiving emails from course %s",
|
||||
request.user.username,
|
||||
request.user.email,
|
||||
course_key
|
||||
course_key,
|
||||
)
|
||||
track.views.server_track(
|
||||
request,
|
||||
"change-email1-settings",
|
||||
{"receive_emails": "no", "course": course_key.to_deprecated_string()},
|
||||
page='dashboard',
|
||||
)
|
||||
track.views.server_track(request, "change-email1-settings", {"receive_emails": "no", "course": course_key.to_deprecated_string()}, page='dashboard')
|
||||
break
|
||||
|
||||
return blocked
|
||||
@@ -725,7 +734,7 @@ def _create_recent_enrollment_message(course_enrollments, course_modes): # pyli
|
||||
recently_enrolled_courses = _get_recently_enrolled_courses(course_enrollments)
|
||||
|
||||
if recently_enrolled_courses:
|
||||
messages = [
|
||||
enroll_messages = [
|
||||
{
|
||||
"course_id": enrollment.course_overview.id,
|
||||
"course_name": enrollment.course_overview.display_name,
|
||||
@@ -738,7 +747,7 @@ def _create_recent_enrollment_message(course_enrollments, course_modes): # pyli
|
||||
|
||||
return render_to_string(
|
||||
'enrollment/course_enrollment_message.html',
|
||||
{'course_enrollment_messages': messages, 'platform_name': platform_name}
|
||||
{'course_enrollment_messages': enroll_messages, 'platform_name': platform_name}
|
||||
)
|
||||
|
||||
|
||||
@@ -777,7 +786,11 @@ def _allow_donation(course_modes, course_id, enrollment):
|
||||
|
||||
"""
|
||||
donations_enabled = DonationConfiguration.current().enabled
|
||||
return donations_enabled and enrollment.mode in course_modes[course_id] and course_modes[course_id][enrollment.mode].min_price == 0
|
||||
return (
|
||||
donations_enabled and
|
||||
enrollment.mode in course_modes[course_id] and
|
||||
course_modes[course_id][enrollment.mode].min_price == 0
|
||||
)
|
||||
|
||||
|
||||
def _update_email_opt_in(request, org):
|
||||
@@ -1011,7 +1024,7 @@ def change_enrollment(request, check_access=True):
|
||||
enroll_mode = CourseMode.auto_enroll_mode(course_id, available_modes)
|
||||
if enroll_mode:
|
||||
CourseEnrollment.enroll(user, course_id, check_access=check_access, mode=enroll_mode)
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return HttpResponseBadRequest(_("Could not enroll"))
|
||||
|
||||
# If we have more than one course mode or professional ed is enabled,
|
||||
@@ -1073,32 +1086,43 @@ def login_user(request, error=""): # pylint: disable=too-many-statements,unused
|
||||
third_party_auth_successful = True
|
||||
except User.DoesNotExist:
|
||||
AUDIT_LOG.warning(
|
||||
u'Login failed - user with username {username} has no social auth with backend_name {backend_name}'.format(
|
||||
username=username, backend_name=backend_name))
|
||||
return HttpResponse(
|
||||
_("You've successfully logged into your {provider_name} account, but this account isn't linked with an {platform_name} account yet.").format(
|
||||
platform_name=platform_name, provider_name=requested_provider.name
|
||||
)
|
||||
+ "<br/><br/>" +
|
||||
_("Use your {platform_name} username and password to log into {platform_name} below, "
|
||||
"and then link your {platform_name} account with {provider_name} from your dashboard.").format(
|
||||
platform_name=platform_name, provider_name=requested_provider.name
|
||||
)
|
||||
+ "<br/><br/>" +
|
||||
_("If you don't have an {platform_name} account yet, "
|
||||
"click <strong>Register</strong> at the top of the page.").format(
|
||||
platform_name=platform_name),
|
||||
content_type="text/plain",
|
||||
status=403
|
||||
u"Login failed - user with username {username} has no social auth "
|
||||
"with backend_name {backend_name}".format(
|
||||
username=username, backend_name=backend_name)
|
||||
)
|
||||
message = _(
|
||||
"You've successfully logged into your {provider_name} account, "
|
||||
"but this account isn't linked with an {platform_name} account yet."
|
||||
).format(
|
||||
platform_name=platform_name,
|
||||
provider_name=requested_provider.name,
|
||||
)
|
||||
message += "<br/><br/>"
|
||||
message += _(
|
||||
"Use your {platform_name} username and password to log into {platform_name} below, "
|
||||
"and then link your {platform_name} account with {provider_name} from your dashboard."
|
||||
).format(
|
||||
platform_name=platform_name,
|
||||
provider_name=requested_provider.name,
|
||||
)
|
||||
message += "<br/><br/>"
|
||||
message += _(
|
||||
"If you don't have an {platform_name} account yet, "
|
||||
"click <strong>Register</strong> at the top of the page."
|
||||
).format(
|
||||
platform_name=platform_name
|
||||
)
|
||||
|
||||
return HttpResponse(message, content_type="text/plain", status=403)
|
||||
|
||||
else:
|
||||
|
||||
if 'email' not in request.POST or 'password' not in request.POST:
|
||||
return JsonResponse({
|
||||
"success": False,
|
||||
"value": _('There was an error receiving your login information. Please email us.'), # TODO: User error message
|
||||
}) # TODO: this should be status code 400 # pylint: disable=fixme
|
||||
# TODO: User error message
|
||||
"value": _('There was an error receiving your login information. Please email us.'),
|
||||
}) # TODO: this should be status code 400
|
||||
|
||||
email = request.POST['email']
|
||||
password = request.POST['password']
|
||||
@@ -1129,9 +1153,11 @@ def login_user(request, error=""): # pylint: disable=too-many-statements,unused
|
||||
user_found_by_email_lookup = user
|
||||
if user_found_by_email_lookup and LoginFailures.is_feature_enabled():
|
||||
if LoginFailures.is_user_locked_out(user_found_by_email_lookup):
|
||||
lockout_message = _('This account has been temporarily locked due '
|
||||
'to excessive login failures. Try again later.')
|
||||
return JsonResponse({
|
||||
"success": False,
|
||||
"value": _('This account has been temporarily locked due to excessive login failures. Try again later.'),
|
||||
"value": lockout_message,
|
||||
}) # TODO: this should be status code 429 # pylint: disable=fixme
|
||||
|
||||
# see if the user must reset his/her password due to any policy settings
|
||||
@@ -1239,7 +1265,8 @@ def login_user(request, error=""): # pylint: disable=too-many-statements,unused
|
||||
AUDIT_LOG.warning(u"Login failed - Account not active for user {0}, resending activation".format(username))
|
||||
|
||||
reactivation_email_for_user(user)
|
||||
not_activated_msg = _("This account has not been activated. We have sent another activation message. Please check your email for the activation instructions.")
|
||||
not_activated_msg = _("This account has not been activated. We have sent another activation "
|
||||
"message. Please check your email for the activation instructions.")
|
||||
return JsonResponse({
|
||||
"success": False,
|
||||
"value": not_activated_msg,
|
||||
@@ -1608,7 +1635,7 @@ def create_account_with_params(request, params):
|
||||
if settings.FEATURES.get('ENABLE_DISCUSSION_EMAIL_DIGEST'):
|
||||
try:
|
||||
enable_notifications(user)
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
log.exception("Enable discussion notifications failed for user {id}.".format(id=user.id))
|
||||
|
||||
dog_stats_api.increment("common.student.account_created")
|
||||
@@ -2010,9 +2037,9 @@ def password_reset(request):
|
||||
|
||||
|
||||
def password_reset_confirm_wrapper(
|
||||
request,
|
||||
uidb36=None,
|
||||
token=None,
|
||||
request,
|
||||
uidb36=None,
|
||||
token=None,
|
||||
):
|
||||
""" A wrapper around django.contrib.auth.views.password_reset_confirm.
|
||||
Needed because we want to set the user as active at this step.
|
||||
@@ -2046,6 +2073,8 @@ def password_reset_confirm_wrapper(
|
||||
num_distinct = settings.ADVANCED_SECURITY_CONFIG['MIN_DIFFERENT_STAFF_PASSWORDS_BEFORE_REUSE']
|
||||
else:
|
||||
num_distinct = settings.ADVANCED_SECURITY_CONFIG['MIN_DIFFERENT_STUDENT_PASSWORDS_BEFORE_REUSE']
|
||||
# Because of how ngettext is, splitting the following into shorter lines would be ugly.
|
||||
# pylint: disable=line-too-long
|
||||
err_msg = ungettext(
|
||||
"You are re-using a password that you have used recently. You must have {num} distinct password before reusing a previous password.",
|
||||
"You are re-using a password that you have used recently. You must have {num} distinct passwords before reusing a previous password.",
|
||||
@@ -2055,6 +2084,8 @@ def password_reset_confirm_wrapper(
|
||||
# also, check to see if passwords are getting reset too frequent
|
||||
if PasswordHistory.is_password_reset_too_soon(user):
|
||||
num_days = settings.ADVANCED_SECURITY_CONFIG['MIN_TIME_IN_DAYS_BETWEEN_ALLOWED_RESETS']
|
||||
# Because of how ngettext is, splitting the following into shorter lines would be ugly.
|
||||
# pylint: disable=line-too-long
|
||||
err_msg = ungettext(
|
||||
"You are resetting passwords too frequently. Due to security policies, {num} day must elapse between password resets.",
|
||||
"You are resetting passwords too frequently. Due to security policies, {num} days must elapse between password resets.",
|
||||
@@ -2287,18 +2318,28 @@ def change_email_settings(request):
|
||||
u"User %s (%s) opted in to receive emails from course %s",
|
||||
user.username,
|
||||
user.email,
|
||||
course_id
|
||||
course_id,
|
||||
)
|
||||
track.views.server_track(
|
||||
request,
|
||||
"change-email-settings",
|
||||
{"receive_emails": "yes", "course": course_id},
|
||||
page='dashboard',
|
||||
)
|
||||
track.views.server_track(request, "change-email-settings", {"receive_emails": "yes", "course": course_id}, page='dashboard')
|
||||
else:
|
||||
Optout.objects.get_or_create(user=user, course_id=course_key)
|
||||
log.info(
|
||||
u"User %s (%s) opted out of receiving emails from course %s",
|
||||
user.username,
|
||||
user.email,
|
||||
course_id
|
||||
course_id,
|
||||
)
|
||||
track.views.server_track(
|
||||
request,
|
||||
"change-email-settings",
|
||||
{"receive_emails": "no", "course": course_id},
|
||||
page='dashboard',
|
||||
)
|
||||
track.views.server_track(request, "change-email-settings", {"receive_emails": "no", "course": course_id}, page='dashboard')
|
||||
|
||||
return JsonResponse({"success": True})
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ of a variety of types.
|
||||
|
||||
Used by capa_problem.py
|
||||
"""
|
||||
|
||||
# TODO: Refactor this code and fix this issue.
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
# standard library imports
|
||||
import abc
|
||||
import cgi
|
||||
@@ -541,7 +542,7 @@ class LoncapaResponse(object):
|
||||
|
||||
# If we can't do that, create the <div> and set the message
|
||||
# as the text of the <div>
|
||||
except:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
response_msg_div = etree.Element('div')
|
||||
response_msg_div.text = str(response_msg)
|
||||
|
||||
@@ -1225,7 +1226,6 @@ class MultipleChoiceResponse(LoncapaResponse):
|
||||
i = 0
|
||||
for response in self.xml.xpath("choicegroup"):
|
||||
# Is Masking enabled? -- check for shuffle or answer-pool features
|
||||
ans_str = response.get("answer-pool")
|
||||
# Masking (self._has_mask) is off, to be re-enabled with a future PR.
|
||||
rtype = response.get('type')
|
||||
if rtype not in ["MultipleChoice"]:
|
||||
@@ -1240,12 +1240,15 @@ class MultipleChoiceResponse(LoncapaResponse):
|
||||
i += 1
|
||||
# If using the masked name, e.g. mask_0, save the regular name
|
||||
# to support unmasking later (for the logs).
|
||||
if self.has_mask():
|
||||
mask_name = "mask_" + str(mask_ids.pop())
|
||||
self._mask_dict[mask_name] = name
|
||||
choice.set("name", mask_name)
|
||||
else:
|
||||
choice.set("name", name)
|
||||
# Masking is currently disabled so this code is commented, as
|
||||
# the variable `mask_ids` is not defined. (the feature appears to not be fully implemented)
|
||||
# The original work for masking was done by Nick Parlante as part of the OLI Hinting feature.
|
||||
# if self.has_mask():
|
||||
# mask_name = "mask_" + str(mask_ids.pop())
|
||||
# self._mask_dict[mask_name] = name
|
||||
# choice.set("name", mask_name)
|
||||
# else:
|
||||
choice.set("name", name)
|
||||
|
||||
def late_transforms(self, problem):
|
||||
"""
|
||||
@@ -1338,12 +1341,13 @@ class MultipleChoiceResponse(LoncapaResponse):
|
||||
Given a masked name, e.g. mask_2, returns the regular name, e.g. choice_0.
|
||||
Fails with LoncapaProblemError if called on a response that is not masking.
|
||||
"""
|
||||
if not self.has_mask():
|
||||
_ = self.capa_system.i18n.ugettext
|
||||
# Translators: 'unmask_name' is a method name and should not be translated.
|
||||
msg = _("unmask_name called on response that is not masked")
|
||||
raise LoncapaProblemError(msg)
|
||||
return self._mask_dict[name]
|
||||
# if not self.has_mask():
|
||||
# _ = self.capa_system.i18n.ugettext
|
||||
# # Translators: 'unmask_name' is a method name and should not be translated.
|
||||
# msg = "unmask_name called on response that is not masked"
|
||||
# raise LoncapaProblemError(msg)
|
||||
# return self._mask_dict[name] # TODO: this is not defined
|
||||
raise NotImplementedError()
|
||||
|
||||
def unmask_order(self):
|
||||
"""
|
||||
@@ -1750,7 +1754,9 @@ class NumericalResponse(LoncapaResponse):
|
||||
student_float = evaluator({}, {}, student_answer)
|
||||
except UndefinedVariable as undef_var:
|
||||
raise StudentInputError(
|
||||
_(u"You may not use variables ({bad_variables}) in numerical problems.").format(bad_variables=undef_var.message)
|
||||
_(u"You may not use variables ({bad_variables}) in numerical problems.").format(
|
||||
bad_variables=undef_var.message,
|
||||
)
|
||||
)
|
||||
except ValueError as val_err:
|
||||
if 'factorial' in val_err.message:
|
||||
@@ -1802,13 +1808,17 @@ class NumericalResponse(LoncapaResponse):
|
||||
for inclusion, answer in zip(self.inclusion, self.answer_range):
|
||||
boundary = self.get_staff_ans(answer)
|
||||
if boundary.imag != 0:
|
||||
# Translators: This is an error message for a math problem. If the instructor provided a boundary
|
||||
# (end limit) for a variable that is a complex number (a + bi), this message displays.
|
||||
raise StudentInputError(_("There was a problem with the staff answer to this problem: complex boundary."))
|
||||
raise StudentInputError(
|
||||
# Translators: This is an error message for a math problem. If the instructor provided a
|
||||
# boundary (end limit) for a variable that is a complex number (a + bi), this message displays.
|
||||
_("There was a problem with the staff answer to this problem: complex boundary.")
|
||||
)
|
||||
if isnan(boundary):
|
||||
# Translators: This is an error message for a math problem. If the instructor did not provide
|
||||
# a boundary (end limit) for a variable, this message displays.
|
||||
raise StudentInputError(_("There was a problem with the staff answer to this problem: empty boundary."))
|
||||
raise StudentInputError(
|
||||
# Translators: This is an error message for a math problem. If the instructor did not
|
||||
# provide a boundary (end limit) for a variable, this message displays.
|
||||
_("There was a problem with the staff answer to this problem: empty boundary.")
|
||||
)
|
||||
boundaries.append(boundary.real)
|
||||
if compare_with_tolerance(
|
||||
student_float,
|
||||
@@ -2164,7 +2174,8 @@ class StringResponse(LoncapaResponse):
|
||||
|
||||
def get_answers(self):
|
||||
_ = self.capa_system.i18n.ugettext
|
||||
# Translators: Separator used in StringResponse to display multiple answers. Example: "Answer: Answer_1 or Answer_2 or Answer_3".
|
||||
# Translators: Separator used in StringResponse to display multiple answers.
|
||||
# Example: "Answer: Answer_1 or Answer_2 or Answer_3".
|
||||
separator = u' <b>{}</b> '.format(_('or'))
|
||||
return {self.answer_id: separator.join(self.correct_answer)}
|
||||
|
||||
@@ -2280,7 +2291,9 @@ class CustomResponse(LoncapaResponse):
|
||||
submission = [student_answers[k] for k in idset]
|
||||
except Exception as err:
|
||||
msg = u"[courseware.capa.responsetypes.customresponse] {message}\n idset = {idset}, error = {err}".format(
|
||||
message=_("error getting student answer from {student_answers}").format(student_answers=student_answers),
|
||||
message=_("error getting student answer from {student_answers}").format(
|
||||
student_answers=student_answers,
|
||||
),
|
||||
idset=idset,
|
||||
err=err
|
||||
)
|
||||
@@ -2392,20 +2405,20 @@ class CustomResponse(LoncapaResponse):
|
||||
random_seed=self.context['seed'],
|
||||
unsafely=self.capa_system.can_execute_unsafe_code(),
|
||||
)
|
||||
except Exception as err:
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
self._handle_exec_exception(err)
|
||||
|
||||
else:
|
||||
# self.code is not a string; it's a function we created earlier.
|
||||
|
||||
# this is an interface to the Tutor2 check functions
|
||||
fn = self.code
|
||||
tutor_cfn = self.code
|
||||
answer_given = submission[0] if (len(idset) == 1) else submission
|
||||
kwnames = self.xml.get("cfn_extra_args", "").split()
|
||||
kwargs = {n: self.context.get(n) for n in kwnames}
|
||||
log.debug(" submission = %s", submission)
|
||||
try:
|
||||
ret = fn(self.expect, answer_given, **kwargs)
|
||||
ret = tutor_cfn(self.expect, answer_given, **kwargs)
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
self._handle_exec_exception(err)
|
||||
log.debug(
|
||||
@@ -2928,15 +2941,17 @@ class CodeResponse(LoncapaResponse):
|
||||
|
||||
# Next, we need to check that the contents of the external grader message is safe for the LMS.
|
||||
# 1) Make sure that the message is valid XML (proper opening/closing tags)
|
||||
# 2) If it is not valid XML, make sure it is valid HTML. Note: html5lib parser will try to repair any broken HTML
|
||||
# For example: <aaa></bbb> will become <aaa/>.
|
||||
# 2) If it is not valid XML, make sure it is valid HTML.
|
||||
# Note: html5lib parser will try to repair any broken HTML
|
||||
# For example: <aaa></bbb> will become <aaa/>.
|
||||
msg = score_result['msg']
|
||||
|
||||
try:
|
||||
etree.fromstring(msg)
|
||||
except etree.XMLSyntaxError as _err:
|
||||
# If `html` contains attrs with no values, like `controls` in <audio controls src='smth'/>,
|
||||
# XML parser will raise exception, so wee fallback to html5parser, which will set empty "" values for such attrs.
|
||||
# XML parser will raise exception, so wee fallback to html5parser,
|
||||
# which will set empty "" values for such attrs.
|
||||
try:
|
||||
parsed = html5lib.parseFragment(msg, treebuilder='lxml', namespaceHTMLElements=False)
|
||||
except ValueError:
|
||||
@@ -3612,11 +3627,13 @@ class AnnotationResponse(LoncapaResponse):
|
||||
def _find_options(self, inputfield):
|
||||
"""Returns an array of dicts where each dict represents an option. """
|
||||
elements = inputfield.findall('./options/option')
|
||||
return [{
|
||||
return [
|
||||
{
|
||||
'id': index,
|
||||
'description': option.text,
|
||||
'choice': option.get('choice')
|
||||
} for (index, option) in enumerate(elements)]
|
||||
} for (index, option) in enumerate(elements)
|
||||
]
|
||||
|
||||
def _find_option_with_choice(self, inputfield, choice):
|
||||
"""Returns the option with the given choice value, otherwise None. """
|
||||
@@ -3663,10 +3680,11 @@ class ChoiceTextResponse(LoncapaResponse):
|
||||
human_name = _('Checkboxes With Text Input')
|
||||
tags = ['choicetextresponse']
|
||||
max_inputfields = 1
|
||||
allowed_inputfields = ['choicetextgroup',
|
||||
'checkboxtextgroup',
|
||||
'radiotextgroup'
|
||||
]
|
||||
allowed_inputfields = [
|
||||
'choicetextgroup',
|
||||
'checkboxtextgroup',
|
||||
'radiotextgroup',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.correct_inputs = {}
|
||||
@@ -3771,9 +3789,8 @@ class ChoiceTextResponse(LoncapaResponse):
|
||||
</radiotextgroup>
|
||||
"""
|
||||
|
||||
for index, choice in enumerate(
|
||||
self.xml.xpath('//*[@id=$id]//choice', id=self.xml.get('id'))
|
||||
):
|
||||
choices = self.xml.xpath('//*[@id=$id]//choice', id=self.xml.get('id'))
|
||||
for index, choice in enumerate(choices):
|
||||
# Set the name attribute for <choices>
|
||||
# "bc" is appended at the end to indicate that this is a
|
||||
# binary choice as opposed to a numtolerance_input, this convention
|
||||
|
||||
@@ -9,16 +9,17 @@ from .chemcalc import (
|
||||
chemical_equations_equal,
|
||||
)
|
||||
|
||||
import miller
|
||||
import chem.miller
|
||||
|
||||
local_debug = None
|
||||
LOCAL_DEBUG = None
|
||||
|
||||
|
||||
def log(s, output_type=None):
|
||||
if local_debug:
|
||||
print s
|
||||
def log(msg, output_type=None):
|
||||
"""Logging function for tests"""
|
||||
if LOCAL_DEBUG:
|
||||
print msg
|
||||
if output_type == 'html':
|
||||
f.write(s + '\n<br>\n')
|
||||
f.write(msg + '\n<br>\n')
|
||||
|
||||
|
||||
class Test_Compare_Equations(unittest.TestCase):
|
||||
@@ -132,10 +133,6 @@ class Test_Compare_Expressions(unittest.TestCase):
|
||||
self.assertFalse(compare_chemical_expression(
|
||||
"H2O(s) + CO2", "H2O+CO2"))
|
||||
|
||||
def test_compare_phases_not_ignored_explicitly(self):
|
||||
self.assertTrue(compare_chemical_expression(
|
||||
"H2O(s) + CO2", "H2O(s)+CO2", ignore_state=False))
|
||||
|
||||
# all in one cases
|
||||
def test_complex_additivity(self):
|
||||
self.assertTrue(compare_chemical_expression(
|
||||
@@ -223,247 +220,250 @@ class Test_Divide_Expressions(unittest.TestCase):
|
||||
|
||||
|
||||
class Test_Render_Equations(unittest.TestCase):
|
||||
|
||||
"""
|
||||
Tests to validate the HTML rendering of plaintext (input) equations
|
||||
"""
|
||||
# pylint: disable=line-too-long
|
||||
def test_render1(self):
|
||||
s = "H2O + CO2"
|
||||
out = render_to_html(s)
|
||||
test_string = "H2O + CO2"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">H<sub>2</sub>O+CO<sub>2</sub></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render_uncorrect_reaction(self):
|
||||
s = "O2C + OH2"
|
||||
out = render_to_html(s)
|
||||
test_string = "O2C + OH2"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">O<sub>2</sub>C+OH<sub>2</sub></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render2(self):
|
||||
s = "CO2 + H2O + Fe(OH)3"
|
||||
out = render_to_html(s)
|
||||
test_string = "CO2 + H2O + Fe(OH)3"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">CO<sub>2</sub>+H<sub>2</sub>O+Fe(OH)<sub>3</sub></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render3(self):
|
||||
s = "3H2O + 2CO2"
|
||||
out = render_to_html(s)
|
||||
test_string = "3H2O + 2CO2"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">3H<sub>2</sub>O+2CO<sub>2</sub></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render4(self):
|
||||
s = "H^+ + OH^-"
|
||||
out = render_to_html(s)
|
||||
test_string = "H^+ + OH^-"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">H<sup>+</sup>+OH<sup>-</sup></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render5(self):
|
||||
s = "Fe(OH)^2- + (OH)^-"
|
||||
out = render_to_html(s)
|
||||
test_string = "Fe(OH)^2- + (OH)^-"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">Fe(OH)<sup>2-</sup>+(OH)<sup>-</sup></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render6(self):
|
||||
s = "7/2H^+ + 3/5OH^-"
|
||||
out = render_to_html(s)
|
||||
test_string = "7/2H^+ + 3/5OH^-"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math"><sup>7</sup>⁄<sub>2</sub>H<sup>+</sup>+<sup>3</sup>⁄<sub>5</sub>OH<sup>-</sup></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render7(self):
|
||||
s = "5(H1H212)^70010- + 2H2O + 7/2HCl + H2O"
|
||||
out = render_to_html(s)
|
||||
test_string = "5(H1H212)^70010- + 2H2O + 7/2HCl + H2O"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">5(H<sub>1</sub>H<sub>212</sub>)<sup>70010-</sup>+2H<sub>2</sub>O+<sup>7</sup>⁄<sub>2</sub>HCl+H<sub>2</sub>O</span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render8(self):
|
||||
s = "H2O(s) + CO2"
|
||||
out = render_to_html(s)
|
||||
test_string = "H2O(s) + CO2"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">H<sub>2</sub>O(s)+CO<sub>2</sub></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render9(self):
|
||||
s = "5[Ni(NH3)4]^2+ + 5/2SO4^2-"
|
||||
out = render_to_html(s)
|
||||
test_string = "5[Ni(NH3)4]^2+ + 5/2SO4^2-"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">5[Ni(NH<sub>3</sub>)<sub>4</sub>]<sup>2+</sup>+<sup>5</sup>⁄<sub>2</sub>SO<sub>4</sub><sup>2-</sup></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render_error(self):
|
||||
s = "5.2H20"
|
||||
out = render_to_html(s)
|
||||
test_string = "5.2H20"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math"><span class="inline-error inline">5.2H20</span></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render_simple_brackets(self):
|
||||
s = "(Ar)"
|
||||
out = render_to_html(s)
|
||||
test_string = "(Ar)"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">(Ar)</span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render_eq1(self):
|
||||
s = "H^+ + OH^- -> H2O"
|
||||
out = render_to_html(s)
|
||||
test_string = "H^+ + OH^- -> H2O"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">H<sup>+</sup>+OH<sup>-</sup>\u2192H<sub>2</sub>O</span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render_eq2(self):
|
||||
s = "H^+ + OH^- <-> H2O"
|
||||
out = render_to_html(s)
|
||||
test_string = "H^+ + OH^- <-> H2O"
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math">H<sup>+</sup>+OH<sup>-</sup>\u2194H<sub>2</sub>O</span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
def test_render_eq3(self):
|
||||
s = "H^+ + OH^- <= H2O" # unsupported arrow
|
||||
out = render_to_html(s)
|
||||
test_string = "H^+ + OH^- <= H2O" # unsupported arrow
|
||||
out = render_to_html(test_string)
|
||||
correct = u'<span class="math"><span class="inline-error inline">H^+ + OH^- <= H2O</span></span>'
|
||||
log(out + ' ------- ' + correct, 'html')
|
||||
self.assertEqual(out, correct)
|
||||
|
||||
|
||||
class Test_Crystallography_Miller(unittest.TestCase):
|
||||
''' Tests for crystallography grade function.'''
|
||||
|
||||
"""Tests for crystallography grade function."""
|
||||
# pylint: disable=line-too-long
|
||||
def test_empty_points(self):
|
||||
user_input = '{"lattice": "bcc", "points": []}'
|
||||
self.assertFalse(miller.grade(user_input, {'miller': '(2,2,2)', 'lattice': 'bcc'}))
|
||||
self.assertFalse(chem.miller.grade(user_input, {'miller': '(2,2,2)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_only_one_point(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.50", "0.00", "0.00"]]}'
|
||||
self.assertFalse(miller.grade(user_input, {'miller': '(2,2,2)', 'lattice': 'bcc'}))
|
||||
self.assertFalse(chem.miller.grade(user_input, {'miller': '(2,2,2)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_only_two_points(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.50", "0.00", "0.00"], ["0.00", "0.50", "0.00"]]}'
|
||||
self.assertFalse(miller.grade(user_input, {'miller': '(2,2,2)', 'lattice': 'bcc'}))
|
||||
self.assertFalse(chem.miller.grade(user_input, {'miller': '(2,2,2)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_1(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.50", "0.00", "0.00"], ["0.00", "0.50", "0.00"], ["0.00", "0.00", "0.50"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(2,2,2)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(2,2,2)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_2(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["1.00", "0.00", "0.00"], ["0.00", "1.00", "0.00"], ["0.00", "0.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(1,1,1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(1,1,1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_3(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["1.00", "0.50", "1.00"], ["1.00", "1.00", "0.50"], ["0.50", "1.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(2,2,2)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(2,2,2)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_4(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.33", "1.00", "0.00"], ["0.00", "0.664", "0.00"], ["0.00", "1.00", "0.33"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(-3, 3, -3)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(-3, 3, -3)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_5(self):
|
||||
""" return true only in case points coordinates are exact.
|
||||
But if they transform to closest 0.05 value it is not true"""
|
||||
user_input = '{"lattice": "bcc", "points": [["0.33", "1.00", "0.00"], ["0.00", "0.33", "0.00"], ["0.00", "1.00", "0.33"]]}'
|
||||
self.assertFalse(miller.grade(user_input, {'miller': '(-6,3,-6)', 'lattice': 'bcc'}))
|
||||
self.assertFalse(chem.miller.grade(user_input, {'miller': '(-6,3,-6)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_6(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "0.25", "0.00"], ["0.25", "0.00", "0.00"], ["0.00", "0.00", "0.25"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(4,4,4)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(4,4,4)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_7(self): # goes throug origin
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "1.00", "0.00"], ["1.00", "0.00", "0.00"], ["0.50", "1.00", "0.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(0,0,-1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(0,0,-1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_8(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "1.00", "0.50"], ["1.00", "0.00", "0.50"], ["0.50", "1.00", "0.50"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(0,0,2)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(0,0,2)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_9(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["1.00", "0.00", "1.00"], ["0.00", "1.00", "1.00"], ["1.00", "0.00", "0.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(1,1,0)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(1,1,0)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_10(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["1.00", "0.00", "1.00"], ["0.00", "0.00", "0.00"], ["0.00", "1.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(1,1,-1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(1,1,-1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_11(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["1.00", "0.00", "0.50"], ["1.00", "1.00", "0.00"], ["0.00", "1.00", "0.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(0,1,2)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(0,1,2)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_12(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["1.00", "0.00", "0.50"], ["0.00", "0.00", "0.50"], ["1.00", "1.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(0,1,-2)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(0,1,-2)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_13(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.50", "0.00", "0.00"], ["0.50", "1.00", "0.00"], ["0.00", "0.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(2,0,1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(2,0,1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_14(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "0.00", "0.00"], ["0.00", "0.00", "1.00"], ["0.50", "1.00", "0.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(2,-1,0)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(2,-1,0)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_15(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "0.00", "0.00"], ["1.00", "1.00", "0.00"], ["0.00", "1.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(1,-1,1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(1,-1,1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_16(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["1.00", "0.00", "0.00"], ["0.00", "1.00", "0.00"], ["1.00", "1.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(1,1,-1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(1,1,-1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_17(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "0.00", "0.00"], ["1.00", "0.00", "1.00"], ["1.00", "1.00", "0.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(-1,1,1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(-1,1,1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_18(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "0.00", "0.00"], ["1.00", "1.00", "0.00"], ["0.00", "1.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(1,-1,1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(1,-1,1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_19(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "0.00", "0.00"], ["1.00", "1.00", "0.00"], ["0.00", "0.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(-1,1,0)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(-1,1,0)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_20(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["1.00", "0.00", "0.00"], ["1.00", "1.00", "0.00"], ["0.00", "0.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(1,0,1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(1,0,1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_21(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "0.00", "0.00"], ["0.00", "1.00", "0.00"], ["1.00", "0.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(-1,0,1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(-1,0,1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_22(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "1.00", "0.00"], ["1.00", "1.00", "0.00"], ["0.00", "0.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(0,1,1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(0,1,1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_23(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "0.00", "0.00"], ["1.00", "0.00", "0.00"], ["1.00", "1.00", "1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(0,-1,1)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(0,-1,1)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_24(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.66", "0.00", "0.00"], ["0.00", "0.66", "0.00"], ["0.00", "0.00", "0.66"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(3,3,3)', 'lattice': 'bcc'}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(3,3,3)', 'lattice': 'bcc'}))
|
||||
|
||||
def test_25(self):
|
||||
user_input = u'{"lattice":"","points":[["0.00","0.00","0.01"],["1.00","1.00","0.01"],["0.00","1.00","1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(1,-1,1)', 'lattice': ''}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(1,-1,1)', 'lattice': ''}))
|
||||
|
||||
def test_26(self):
|
||||
user_input = u'{"lattice":"","points":[["0.00","0.01","0.00"],["1.00","0.00","0.00"],["0.00","0.00","1.00"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(0,-1,0)', 'lattice': ''}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(0,-1,0)', 'lattice': ''}))
|
||||
|
||||
def test_27(self):
|
||||
""" rounding to 0.35"""
|
||||
user_input = u'{"lattice":"","points":[["0.33","0.00","0.00"],["0.00","0.33","0.00"],["0.00","0.00","0.33"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(3,3,3)', 'lattice': ''}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(3,3,3)', 'lattice': ''}))
|
||||
|
||||
def test_28(self):
|
||||
""" rounding to 0.30"""
|
||||
user_input = u'{"lattice":"","points":[["0.30","0.00","0.00"],["0.00","0.30","0.00"],["0.00","0.00","0.30"]]}'
|
||||
self.assertTrue(miller.grade(user_input, {'miller': '(10,10,10)', 'lattice': ''}))
|
||||
self.assertTrue(chem.miller.grade(user_input, {'miller': '(10,10,10)', 'lattice': ''}))
|
||||
|
||||
def test_wrong_lattice(self):
|
||||
user_input = '{"lattice": "bcc", "points": [["0.00", "0.00", "0.00"], ["1.00", "0.00", "0.00"], ["1.00", "1.00", "1.00"]]}'
|
||||
self.assertFalse(miller.grade(user_input, {'miller': '(3,3,3)', 'lattice': 'fcc'}))
|
||||
self.assertFalse(chem.miller.grade(user_input, {'miller': '(3,3,3)', 'lattice': 'fcc'}))
|
||||
|
||||
|
||||
def suite():
|
||||
@@ -478,7 +478,7 @@ def suite():
|
||||
return unittest.TestSuite(suites)
|
||||
|
||||
if __name__ == "__main__":
|
||||
local_debug = True
|
||||
LOCAL_DEBUG = True
|
||||
with codecs.open('render.html', 'w', encoding='utf-8') as f:
|
||||
unittest.TextTestRunner(verbosity=2).run(suite())
|
||||
# open render.html to look at rendered equations
|
||||
|
||||
@@ -20,6 +20,9 @@ class CourseOutlineItem(object):
|
||||
"""
|
||||
A mixin class for any :class:`PageObject` shown in a course outline.
|
||||
"""
|
||||
# Note there are a few pylint disable=no-member occurances in this class, because
|
||||
# it was written assuming it is going to be a mixin to a PageObject and will have functions
|
||||
# such as self.wait_for_ajax, which doesn't exist on a generic `object`.
|
||||
BODY_SELECTOR = None
|
||||
EDIT_BUTTON_SELECTOR = '.xblock-field-value-edit'
|
||||
NAME_SELECTOR = '.item-title'
|
||||
@@ -33,7 +36,7 @@ class CourseOutlineItem(object):
|
||||
# Check for the existence of a locator so that errors when navigating to the course outline page don't show up
|
||||
# as errors in the repr method instead.
|
||||
try:
|
||||
return "{}(<browser>, {!r})".format(self.__class__.__name__, self.locator)
|
||||
return "{}(<browser>, {!r})".format(self.__class__.__name__, self.locator) # pylint: disable=no-member
|
||||
except AttributeError:
|
||||
return "{}(<browser>)".format(self.__class__.__name__)
|
||||
|
||||
@@ -43,6 +46,7 @@ class CourseOutlineItem(object):
|
||||
"""
|
||||
# If the item doesn't have a body selector or locator, then it can't be bounded
|
||||
# This happens in the context of the CourseOutlinePage
|
||||
# pylint: disable=no-member
|
||||
if self.BODY_SELECTOR and hasattr(self, 'locator'):
|
||||
return '{}[data-locator="{}"] {}'.format(
|
||||
self.BODY_SELECTOR,
|
||||
@@ -57,7 +61,7 @@ class CourseOutlineItem(object):
|
||||
"""
|
||||
Returns the display name of this object.
|
||||
"""
|
||||
name_element = self.q(css=self._bounded_selector(self.NAME_SELECTOR)).first
|
||||
name_element = self.q(css=self._bounded_selector(self.NAME_SELECTOR)).first # pylint: disable=no-member
|
||||
if name_element:
|
||||
return name_element.text[0]
|
||||
else:
|
||||
@@ -68,14 +72,14 @@ class CourseOutlineItem(object):
|
||||
"""
|
||||
Returns True if the item has a status message, False otherwise.
|
||||
"""
|
||||
return self.q(css=self._bounded_selector(self.STATUS_MESSAGE_SELECTOR)).first.visible
|
||||
return self.q(css=self._bounded_selector(self.STATUS_MESSAGE_SELECTOR)).first.visible # pylint: disable=no-member
|
||||
|
||||
@property
|
||||
def status_message(self):
|
||||
"""
|
||||
Returns the status message of this item.
|
||||
"""
|
||||
return self.q(css=self._bounded_selector(self.STATUS_MESSAGE_SELECTOR)).text[0]
|
||||
return self.q(css=self._bounded_selector(self.STATUS_MESSAGE_SELECTOR)).text[0] # pylint: disable=no-member
|
||||
|
||||
@property
|
||||
def has_staff_lock_warning(self):
|
||||
@@ -85,13 +89,13 @@ class CourseOutlineItem(object):
|
||||
@property
|
||||
def is_staff_only(self):
|
||||
""" Returns True if the visiblity state of this item is staff only (has a black sidebar) """
|
||||
return "is-staff-only" in self.q(css=self._bounded_selector(''))[0].get_attribute("class")
|
||||
return "is-staff-only" in self.q(css=self._bounded_selector(''))[0].get_attribute("class") # pylint: disable=no-member
|
||||
|
||||
def edit_name(self):
|
||||
"""
|
||||
Puts the item's name into editable form.
|
||||
"""
|
||||
self.q(css=self._bounded_selector(self.EDIT_BUTTON_SELECTOR)).first.click()
|
||||
self.q(css=self._bounded_selector(self.EDIT_BUTTON_SELECTOR)).first.click() # pylint: disable=no-member
|
||||
|
||||
def enter_name(self, new_name):
|
||||
"""
|
||||
@@ -105,12 +109,13 @@ class CourseOutlineItem(object):
|
||||
"""
|
||||
self.edit_name()
|
||||
set_input_value_and_save(self, self._bounded_selector(self.NAME_INPUT_SELECTOR), new_name)
|
||||
self.wait_for_ajax()
|
||||
self.wait_for_ajax() # pylint: disable=no-member
|
||||
|
||||
def finalize_name(self):
|
||||
"""
|
||||
Presses ENTER, saving the value of the display name for this item.
|
||||
"""
|
||||
# pylint: disable=no-member
|
||||
self.q(css=self._bounded_selector(self.NAME_INPUT_SELECTOR)).results[0].send_keys(Keys.ENTER)
|
||||
self.wait_for_ajax()
|
||||
|
||||
@@ -126,29 +131,42 @@ class CourseOutlineItem(object):
|
||||
"""
|
||||
Return whether this outline item's display name is in its editable form.
|
||||
"""
|
||||
# pylint: disable=no-member
|
||||
return "is-editing" in self.q(
|
||||
css=self._bounded_selector(self.NAME_FIELD_WRAPPER_SELECTOR)
|
||||
)[0].get_attribute("class")
|
||||
|
||||
def edit(self):
|
||||
self.q(css=self._bounded_selector(self.CONFIGURATION_BUTTON_SELECTOR)).first.click()
|
||||
"""
|
||||
Puts the item into editable form.
|
||||
"""
|
||||
self.q(css=self._bounded_selector(self.CONFIGURATION_BUTTON_SELECTOR)).first.click() # pylint: disable=no-member
|
||||
modal = CourseOutlineModal(self)
|
||||
EmptyPromise(lambda: modal.is_shown(), 'Modal is shown.')
|
||||
EmptyPromise(lambda: modal.is_shown(), 'Modal is shown.') # pylint: disable=unnecessary-lambda
|
||||
return modal
|
||||
|
||||
@property
|
||||
def release_date(self):
|
||||
element = self.q(css=self._bounded_selector(".status-release-value"))
|
||||
"""
|
||||
Returns the release date from the page. Date is "mm/dd/yyyy" string.
|
||||
"""
|
||||
element = self.q(css=self._bounded_selector(".status-release-value")) # pylint: disable=no-member
|
||||
return element.first.text[0] if element.present else None
|
||||
|
||||
@property
|
||||
def due_date(self):
|
||||
element = self.q(css=self._bounded_selector(".status-grading-date"))
|
||||
"""
|
||||
Returns the due date from the page. Date is "mm/dd/yyyy" string.
|
||||
"""
|
||||
element = self.q(css=self._bounded_selector(".status-grading-date")) # pylint: disable=no-member
|
||||
return element.first.text[0] if element.present else None
|
||||
|
||||
@property
|
||||
def policy(self):
|
||||
element = self.q(css=self._bounded_selector(".status-grading-value"))
|
||||
"""
|
||||
Select the grading format with `value` in the drop-down list.
|
||||
"""
|
||||
element = self.q(css=self._bounded_selector(".status-grading-value")) # pylint: disable=no-member
|
||||
return element.first.text[0] if element.present else None
|
||||
|
||||
def publish(self):
|
||||
@@ -157,7 +175,7 @@ class CourseOutlineItem(object):
|
||||
"""
|
||||
click_css(self, self._bounded_selector('.action-publish'), require_notification=False)
|
||||
modal = CourseOutlineModal(self)
|
||||
EmptyPromise(lambda: modal.is_shown(), 'Modal is shown.')
|
||||
EmptyPromise(lambda: modal.is_shown(), 'Modal is shown.') # pylint: disable=unnecessary-lambda
|
||||
modal.publish()
|
||||
|
||||
@property
|
||||
@@ -165,7 +183,7 @@ class CourseOutlineItem(object):
|
||||
"""
|
||||
Returns the link for publishing a unit.
|
||||
"""
|
||||
return self.q(css=self._bounded_selector('.action-publish')).first
|
||||
return self.q(css=self._bounded_selector('.action-publish')).first # pylint: disable=no-member
|
||||
|
||||
|
||||
class CourseOutlineContainer(CourseOutlineItem):
|
||||
@@ -186,6 +204,7 @@ class CourseOutlineContainer(CourseOutlineItem):
|
||||
if not child_class:
|
||||
child_class = self.CHILD_CLASS
|
||||
|
||||
# pylint: disable=no-member
|
||||
return child_class(
|
||||
self.browser,
|
||||
self.q(css=child_class.BODY_SELECTOR).filter(
|
||||
@@ -200,6 +219,7 @@ class CourseOutlineContainer(CourseOutlineItem):
|
||||
"""
|
||||
if not child_class:
|
||||
child_class = self.CHILD_CLASS
|
||||
# pylint: disable=no-member
|
||||
return self.q(css=self._bounded_selector(child_class.BODY_SELECTOR)).map(
|
||||
lambda el: child_class(self.browser, el.get_attribute('data-locator'))).results
|
||||
|
||||
@@ -227,10 +247,13 @@ class CourseOutlineContainer(CourseOutlineItem):
|
||||
"""
|
||||
Toggle the expansion of this subsection.
|
||||
"""
|
||||
|
||||
# pylint: disable=no-member
|
||||
self.browser.execute_script("jQuery.fx.off = true;")
|
||||
|
||||
def subsection_expanded():
|
||||
"""
|
||||
Returns whether or not this subsection is expanded.
|
||||
"""
|
||||
add_button = self.q(css=self._bounded_selector(self.ADD_BUTTON_SELECTOR)).first.results
|
||||
return add_button and add_button[0].is_displayed()
|
||||
|
||||
@@ -253,7 +276,7 @@ class CourseOutlineContainer(CourseOutlineItem):
|
||||
"""
|
||||
Return whether this outline item is currently collapsed.
|
||||
"""
|
||||
return "is-collapsed" in self.q(css=self._bounded_selector('')).first.attrs("class")[0]
|
||||
return "is-collapsed" in self.q(css=self._bounded_selector('')).first.attrs("class")[0] # pylint: disable=no-member
|
||||
|
||||
|
||||
class CourseOutlineChild(PageObject, CourseOutlineItem):
|
||||
@@ -742,6 +765,9 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
|
||||
|
||||
|
||||
class CourseOutlineModal(object):
|
||||
"""
|
||||
Page object specifically for a modal window on the course outline page.
|
||||
"""
|
||||
MODAL_SELECTOR = ".wrapper-modal-window"
|
||||
|
||||
def __init__(self, page):
|
||||
@@ -754,26 +780,47 @@ class CourseOutlineModal(object):
|
||||
return " ".join([self.MODAL_SELECTOR, selector])
|
||||
|
||||
def is_shown(self):
|
||||
"""
|
||||
Return whether or not the modal defined by self.MODAL_SELECTOR is shown.
|
||||
"""
|
||||
return self.page.q(css=self.MODAL_SELECTOR).present
|
||||
|
||||
def find_css(self, selector):
|
||||
"""
|
||||
Find the given css selector on the page.
|
||||
"""
|
||||
return self.page.q(css=self._bounded_selector(selector))
|
||||
|
||||
def click(self, selector, index=0):
|
||||
"""
|
||||
Perform a Click action on the given selector.
|
||||
"""
|
||||
self.find_css(selector).nth(index).click()
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Click the save action button, and wait for the ajax call to return.
|
||||
"""
|
||||
self.click(".action-save")
|
||||
self.page.wait_for_ajax()
|
||||
|
||||
def publish(self):
|
||||
"""
|
||||
Click the publish action button, and wait for the ajax call to return.
|
||||
"""
|
||||
self.click(".action-publish")
|
||||
self.page.wait_for_ajax()
|
||||
|
||||
def cancel(self):
|
||||
"""
|
||||
Click the cancel action button.
|
||||
"""
|
||||
self.click(".action-cancel")
|
||||
|
||||
def has_release_date(self):
|
||||
"""
|
||||
Check if the input box for the release date exists in the subsection's settings window
|
||||
"""
|
||||
return self.find_css("#start_date").present
|
||||
|
||||
def has_release_time(self):
|
||||
@@ -783,6 +830,9 @@ class CourseOutlineModal(object):
|
||||
return self.find_css("#start_time").present
|
||||
|
||||
def has_due_date(self):
|
||||
"""
|
||||
Check if the input box for the due date exists in the subsection's settings window
|
||||
"""
|
||||
return self.find_css("#due_date").present
|
||||
|
||||
def has_due_time(self):
|
||||
@@ -792,6 +842,9 @@ class CourseOutlineModal(object):
|
||||
return self.find_css("#due_time").present
|
||||
|
||||
def has_policy(self):
|
||||
"""
|
||||
Check if the input for the grading policy is present.
|
||||
"""
|
||||
return self.find_css("#grading_type").present
|
||||
|
||||
def set_date(self, property_name, input_selector, date):
|
||||
@@ -806,7 +859,7 @@ class CourseOutlineModal(object):
|
||||
current_month, current_year = datetime.datetime.today().month, datetime.datetime.today().year
|
||||
date_diff = 12 * (year - current_year) + month - current_month
|
||||
selector = "a.ui-datepicker-{}".format('next' if date_diff > 0 else 'prev')
|
||||
for i in xrange(abs(date_diff)):
|
||||
for __ in xrange(abs(date_diff)):
|
||||
self.page.q(css=selector).click()
|
||||
self.page.q(css="a.ui-state-default").nth(day - 1).click() # set day
|
||||
self.page.wait_for_element_invisibility("#ui-datepicker-div", "datepicker should be closed")
|
||||
@@ -826,12 +879,15 @@ class CourseOutlineModal(object):
|
||||
|
||||
@property
|
||||
def release_date(self):
|
||||
"""
|
||||
Returns the unit's release date. Date is "mm/dd/yyyy" string.
|
||||
"""
|
||||
return self.find_css("#start_date").first.attrs('value')[0]
|
||||
|
||||
@release_date.setter
|
||||
def release_date(self, date):
|
||||
"""
|
||||
Date is "mm/dd/yyyy" string.
|
||||
Sets the unit's release date to `date`. Date is "mm/dd/yyyy" string.
|
||||
"""
|
||||
self.set_date('release_date', "#start_date", date)
|
||||
|
||||
@@ -851,12 +907,15 @@ class CourseOutlineModal(object):
|
||||
|
||||
@property
|
||||
def due_date(self):
|
||||
"""
|
||||
Returns the due date from the page. Date is "mm/dd/yyyy" string.
|
||||
"""
|
||||
return self.find_css("#due_date").first.attrs('value')[0]
|
||||
|
||||
@due_date.setter
|
||||
def due_date(self, date):
|
||||
"""
|
||||
Date is "mm/dd/yyyy" string.
|
||||
Sets the due date for the unit. Date is "mm/dd/yyyy" string.
|
||||
"""
|
||||
self.set_date('due_date', "#due_date", date)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# TODO: Is this file still used? If so it should be refactored and tests added.
|
||||
# pylint: disable=line-too-long, invalid-name
|
||||
"""
|
||||
Embeds web videos using URLs. For instance, if a URL to an youtube video is
|
||||
found in the text submitted to markdown and it isn't enclosed in parenthesis
|
||||
@@ -132,8 +133,8 @@ try:
|
||||
# Markdown 2.1.0 changed from 2.0.3. We try importing the new version first,
|
||||
# but import the 2.0.3 version if it fails
|
||||
from markdown.util import etree
|
||||
except: # pylint: disable=bare-except
|
||||
from markdown import etree
|
||||
except ImportError:
|
||||
from markdown import etree # pylint: disable=no-name-in-module
|
||||
|
||||
|
||||
version = "0.1.6"
|
||||
@@ -164,7 +165,8 @@ class VideoExtension(markdown.Extension):
|
||||
for key, value in configs:
|
||||
self.setConfig(key, value)
|
||||
|
||||
def add_inline(self, md, name, klass, re):
|
||||
def add_inline(self, md, name, klass, re): # pylint: disable=invalid-name
|
||||
"""Adds the inline link"""
|
||||
pattern = klass(re)
|
||||
pattern.md = md
|
||||
pattern.ext = self
|
||||
@@ -192,6 +194,7 @@ class VideoExtension(markdown.Extension):
|
||||
class Bliptv(markdown.inlinepatterns.Pattern):
|
||||
def handleMatch(self, m):
|
||||
url = 'http://blip.tv/scripts/flash/showplayer.swf?file=http://blip.tv/file/get/%s' % m.group('bliptvfile')
|
||||
# pylint: disable=no-member
|
||||
width = self.ext.config['bliptv_width'][0]
|
||||
height = self.ext.config['bliptv_height'][0]
|
||||
return flash_object(url, width, height)
|
||||
@@ -200,6 +203,7 @@ class Bliptv(markdown.inlinepatterns.Pattern):
|
||||
class Dailymotion(markdown.inlinepatterns.Pattern):
|
||||
def handleMatch(self, m):
|
||||
url = 'http://www.dailymotion.com/swf/%s' % m.group('dailymotionid').split('/')[-1]
|
||||
# pylint: disable=no-member
|
||||
width = self.ext.config['dailymotion_width'][0]
|
||||
height = self.ext.config['dailymotion_height'][0]
|
||||
return flash_object(url, width, height)
|
||||
@@ -209,6 +213,7 @@ class Gametrailers(markdown.inlinepatterns.Pattern):
|
||||
def handleMatch(self, m):
|
||||
url = 'http://www.gametrailers.com/remote_wrap.php?mid=%s' % \
|
||||
m.group('gametrailersid').split('/')[-1]
|
||||
# pylint: disable=no-member
|
||||
width = self.ext.config['gametrailers_width'][0]
|
||||
height = self.ext.config['gametrailers_height'][0]
|
||||
return flash_object(url, width, height)
|
||||
@@ -217,6 +222,7 @@ class Gametrailers(markdown.inlinepatterns.Pattern):
|
||||
class Metacafe(markdown.inlinepatterns.Pattern):
|
||||
def handleMatch(self, m):
|
||||
url = 'http://www.metacafe.com/fplayer/%s.swf' % m.group('metacafeid')
|
||||
# pylint: disable=no-member
|
||||
width = self.ext.config['metacafe_width'][0]
|
||||
height = self.ext.config['metacafe_height'][0]
|
||||
return flash_object(url, width, height)
|
||||
@@ -225,6 +231,7 @@ class Metacafe(markdown.inlinepatterns.Pattern):
|
||||
class Veoh(markdown.inlinepatterns.Pattern):
|
||||
def handleMatch(self, m):
|
||||
url = 'http://www.veoh.com/videodetails2.swf?permalinkId=%s' % m.group('veohid')
|
||||
# pylint: disable=no-member
|
||||
width = self.ext.config['veoh_width'][0]
|
||||
height = self.ext.config['veoh_height'][0]
|
||||
return flash_object(url, width, height)
|
||||
@@ -233,6 +240,7 @@ class Veoh(markdown.inlinepatterns.Pattern):
|
||||
class Vimeo(markdown.inlinepatterns.Pattern):
|
||||
def handleMatch(self, m):
|
||||
url = 'http://vimeo.com/moogaloop.swf?clip_id=%s&server=vimeo.com' % m.group('vimeoid')
|
||||
# pylint: disable=no-member
|
||||
width = self.ext.config['vimeo_width'][0]
|
||||
height = self.ext.config['vimeo_height'][0]
|
||||
return flash_object(url, width, height)
|
||||
@@ -241,6 +249,7 @@ class Vimeo(markdown.inlinepatterns.Pattern):
|
||||
class Yahoo(markdown.inlinepatterns.Pattern):
|
||||
def handleMatch(self, m):
|
||||
url = "http://d.yimg.com/static.video.yahoo.com/yep/YV_YEP.swf?ver=2.2.40"
|
||||
# pylint: disable=no-member
|
||||
width = self.ext.config['yahoo_width'][0]
|
||||
height = self.ext.config['yahoo_height'][0]
|
||||
obj = flash_object(url, width, height)
|
||||
@@ -255,6 +264,7 @@ class Yahoo(markdown.inlinepatterns.Pattern):
|
||||
class Youtube(markdown.inlinepatterns.Pattern):
|
||||
def handleMatch(self, m):
|
||||
url = 'http://www.youtube.com/v/%s' % m.group('youtubeargs')
|
||||
# pylint: disable=no-member
|
||||
width = self.ext.config['youtube_width'][0]
|
||||
height = self.ext.config['youtube_height'][0]
|
||||
return flash_object(url, width, height)
|
||||
|
||||
@@ -51,7 +51,7 @@ from instructor_task.tasks_helper import (
|
||||
TASK_LOG = logging.getLogger('edx.celery.task')
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
@task(base=BaseInstructorTask) # pylint: disable=not-callable
|
||||
def rescore_problem(entry_id, xmodule_instance_args):
|
||||
"""Rescores a problem in a course, for all students or one specific student.
|
||||
|
||||
@@ -82,7 +82,7 @@ def rescore_problem(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, visit_fcn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
@task(base=BaseInstructorTask) # pylint: disable=not-callable
|
||||
def reset_problem_attempts(entry_id, xmodule_instance_args):
|
||||
"""Resets problem attempts to zero for a particular problem for all students in a course.
|
||||
|
||||
@@ -104,7 +104,7 @@ def reset_problem_attempts(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, visit_fcn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
@task(base=BaseInstructorTask) # pylint: disable=not-callable
|
||||
def delete_problem_state(entry_id, xmodule_instance_args):
|
||||
"""Deletes problem state entirely for all students on a particular problem in a course.
|
||||
|
||||
@@ -126,7 +126,7 @@ def delete_problem_state(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, visit_fcn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
@task(base=BaseInstructorTask) # pylint: disable=not-callable
|
||||
def send_bulk_course_email(entry_id, _xmodule_instance_args):
|
||||
"""Sends emails to recipients enrolled in a course.
|
||||
|
||||
@@ -147,7 +147,7 @@ def send_bulk_course_email(entry_id, _xmodule_instance_args):
|
||||
return run_main_task(entry_id, visit_fcn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY)
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY) # pylint: disable=not-callable
|
||||
def calculate_problem_responses_csv(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Compute student answers to a given problem and upload the CSV to
|
||||
@@ -159,7 +159,7 @@ def calculate_problem_responses_csv(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY)
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY) # pylint: disable=not-callable
|
||||
def calculate_grades_csv(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Grade a course and push the results to an S3 bucket for download.
|
||||
@@ -175,7 +175,7 @@ def calculate_grades_csv(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY)
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY) # pylint: disable=not-callable
|
||||
def calculate_problem_grade_report(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Generate a CSV for a course containing all students' problem
|
||||
@@ -192,7 +192,7 @@ def calculate_problem_grade_report(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY)
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY) # pylint: disable=not-callable
|
||||
def calculate_students_features_csv(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Compute student profile information for a course and upload the
|
||||
@@ -204,7 +204,7 @@ def calculate_students_features_csv(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
@task(base=BaseInstructorTask) # pylint: disable=not-callable
|
||||
def enrollment_report_features_csv(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Compute student profile information for a course and upload the
|
||||
@@ -216,7 +216,7 @@ def enrollment_report_features_csv(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
@task(base=BaseInstructorTask) # pylint: disable=not-callable
|
||||
def exec_summary_report_csv(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Compute executive summary report for a course and upload the
|
||||
@@ -228,7 +228,7 @@ def exec_summary_report_csv(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
@task(base=BaseInstructorTask) # pylint: disable=not-callable
|
||||
def course_survey_report_csv(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Compute the survey report for a course and upload the
|
||||
@@ -240,7 +240,7 @@ def course_survey_report_csv(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
@task(base=BaseInstructorTask) # pylint: disable=not-callable
|
||||
def proctored_exam_results_csv(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Compute proctored exam results report for a course and upload the
|
||||
@@ -251,7 +251,7 @@ def proctored_exam_results_csv(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY)
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY) # pylint: disable=not-callable
|
||||
def calculate_may_enroll_csv(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Compute information about invited students who have not enrolled
|
||||
@@ -264,7 +264,7 @@ def calculate_may_enroll_csv(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY)
|
||||
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY) # pylint: disable=not-callable
|
||||
def generate_certificates(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Grade students and generate certificates.
|
||||
@@ -280,7 +280,7 @@ def generate_certificates(entry_id, xmodule_instance_args):
|
||||
return run_main_task(entry_id, task_fn, action_name)
|
||||
|
||||
|
||||
@task(base=BaseInstructorTask)
|
||||
@task(base=BaseInstructorTask) # pylint: disable=not-callable
|
||||
def cohort_students(entry_id, xmodule_instance_args):
|
||||
"""
|
||||
Cohort students in bulk, and upload the results.
|
||||
|
||||
@@ -11,7 +11,7 @@ set -e
|
||||
###############################################################################
|
||||
|
||||
# Violations thresholds for failing the build
|
||||
export PYLINT_THRESHOLD=4999
|
||||
export PYLINT_THRESHOLD=4700
|
||||
export JSHINT_THRESHOLD=9080
|
||||
|
||||
doCheckVars() {
|
||||
|
||||
Reference in New Issue
Block a user