Commit ee4ab205 by Sarina Canelake

Merge pull request #10986 from edx/sc/pylint

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