Commit a883a9cd by Sarina Canelake

Merge pull request #5925 from stvstnfrd/pep8/continuation

Fix continuation-related PEP8 issues
parents 7b3602e3 5e1c5df3
...@@ -286,8 +286,15 @@ def _do_studio_prompt_action(intent, action): ...@@ -286,8 +286,15 @@ def _do_studio_prompt_action(intent, action):
Wait for a studio prompt to appear and press the specified action button Wait for a studio prompt to appear and press the specified action button
See cms/static/js/views/feedback_prompt.js for implementation See cms/static/js/views/feedback_prompt.js for implementation
""" """
assert intent in ['warning', 'error', 'confirmation', 'announcement', assert intent in [
'step-required', 'help', 'mini'] 'warning',
'error',
'confirmation',
'announcement',
'step-required',
'help',
'mini',
]
assert action in ['primary', 'secondary'] assert action in ['primary', 'secondary']
world.wait_for_present('div.wrapper-prompt.is-shown#prompt-{}'.format(intent)) world.wait_for_present('div.wrapper-prompt.is-shown#prompt-{}'.format(intent))
......
...@@ -52,19 +52,13 @@ def see_a_multi_step_component(step, category): ...@@ -52,19 +52,13 @@ def see_a_multi_step_component(step, category):
world.wait_for(lambda _: len(world.css_find(selector)) == len(step.hashes)) world.wait_for(lambda _: len(world.css_find(selector)) == len(step.hashes))
for idx, step_hash in enumerate(step.hashes): for idx, step_hash in enumerate(step.hashes):
if category == 'HTML': if category == 'HTML':
html_matcher = { html_matcher = {
'Text': 'Text': '\n \n',
'\n \n', 'Announcement': '<p> Words of encouragement! This is a short note that most students will read. </p>',
'Announcement': 'Zooming Image': '<h2>ZOOMING DIAGRAMS</h2>',
'<p> Words of encouragement! This is a short note that most students will read. </p>', 'E-text Written in LaTeX': '<h2>Example: E-text page</h2>',
'Zooming Image': 'Raw HTML': '<p>This template is similar to the Text template. The only difference is',
'<h2>ZOOMING DIAGRAMS</h2>',
'E-text Written in LaTeX':
'<h2>Example: E-text page</h2>',
'Raw HTML':
'<p>This template is similar to the Text template. The only difference is',
} }
actual_html = world.css_html(selector, index=idx) actual_html = world.css_html(selector, index=idx)
assert_in(html_matcher[step_hash['Component']], actual_html) assert_in(html_matcher[step_hash['Component']], actual_html)
......
...@@ -93,8 +93,10 @@ def click_component_from_menu(category, component_type, is_advanced): ...@@ -93,8 +93,10 @@ def click_component_from_menu(category, component_type, is_advanced):
""" """
if is_advanced: if is_advanced:
# Sometimes this click does not work if you go too fast. # Sometimes this click does not work if you go too fast.
world.retry_on_exception(_click_advanced, world.retry_on_exception(
ignored_exceptions=AssertionError) _click_advanced,
ignored_exceptions=AssertionError,
)
# Retry this in case the list is empty because you tried too fast. # Retry this in case the list is empty because you tried too fast.
link = world.retry_on_exception( link = world.retry_on_exception(
......
...@@ -24,7 +24,8 @@ def i_export_the_course(step): ...@@ -24,7 +24,8 @@ def i_export_the_course(step):
@step('I edit and enter bad XML$') @step('I edit and enter bad XML$')
def i_enter_bad_xml(step): def i_enter_bad_xml(step):
enter_xml_in_advanced_problem(step, enter_xml_in_advanced_problem(
step,
"""<problem><h1>Smallest Canvas</h1> """<problem><h1>Smallest Canvas</h1>
<p>You want to make the smallest canvas you can.</p> <p>You want to make the smallest canvas you can.</p>
<multiplechoiceresponse> <multiplechoiceresponse>
......
...@@ -55,10 +55,11 @@ class InternationalizationTest(ModuleStoreTestCase): ...@@ -55,10 +55,11 @@ class InternationalizationTest(ModuleStoreTestCase):
self.client = AjaxEnabledTestClient() self.client = AjaxEnabledTestClient()
self.client.login(username=self.uname, password=self.password) self.client.login(username=self.uname, password=self.password)
resp = self.client.get_html('/course/', resp = self.client.get_html(
{}, '/course/',
HTTP_ACCEPT_LANGUAGE='en' {},
) HTTP_ACCEPT_LANGUAGE='en',
)
self.assertContains(resp, self.assertContains(resp,
'<h1 class="page-header">My Courses</h1>', '<h1 class="page-header">My Courses</h1>',
......
...@@ -32,13 +32,14 @@ class TestCourseAccess(ModuleStoreTestCase): ...@@ -32,13 +32,14 @@ class TestCourseAccess(ModuleStoreTestCase):
# create a course via the view handler which has a different strategy for permissions than the factory # create a course via the view handler which has a different strategy for permissions than the factory
self.course_key = SlashSeparatedCourseKey('myu', 'mydept.mycourse', 'myrun') self.course_key = SlashSeparatedCourseKey('myu', 'mydept.mycourse', 'myrun')
course_url = reverse_url('course_handler') course_url = reverse_url('course_handler')
self.client.ajax_post(course_url, self.client.ajax_post(
course_url,
{ {
'org': self.course_key.org, 'org': self.course_key.org,
'number': self.course_key.course, 'number': self.course_key.course,
'display_name': 'My favorite course', 'display_name': 'My favorite course',
'run': self.course_key.run, 'run': self.course_key.run,
} },
) )
self.users = self._create_users() self.users = self._create_users()
......
...@@ -190,8 +190,8 @@ def _upload_asset(request, course_key): ...@@ -190,8 +190,8 @@ def _upload_asset(request, course_key):
# first let's see if a thumbnail can be created # first let's see if a thumbnail can be created
(thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail( (thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail(
content, content,
tempfile_path=tempfile_path tempfile_path=tempfile_path,
) )
# delete cached thumbnail even if one couldn't be created this time (else # delete cached thumbnail even if one couldn't be created this time (else
......
...@@ -387,10 +387,13 @@ def course_listing(request): ...@@ -387,10 +387,13 @@ def course_listing(request):
'run': uca.course_key.run, 'run': uca.course_key.run,
'is_failed': True if uca.state == CourseRerunUIStateManager.State.FAILED else False, 'is_failed': True if uca.state == CourseRerunUIStateManager.State.FAILED else False,
'is_in_progress': True if uca.state == CourseRerunUIStateManager.State.IN_PROGRESS else False, 'is_in_progress': True if uca.state == CourseRerunUIStateManager.State.IN_PROGRESS else False,
'dismiss_link': 'dismiss_link': reverse_course_url(
reverse_course_url('course_notifications_handler', uca.course_key, kwargs={ 'course_notifications_handler',
uca.course_key,
kwargs={
'action_state_id': uca.id, 'action_state_id': uca.id,
}) if uca.state == CourseRerunUIStateManager.State.FAILED else '' },
) if uca.state == CourseRerunUIStateManager.State.FAILED else ''
} }
# remove any courses in courses that are also in the in_process_course_actions list # remove any courses in courses that are also in the in_process_course_actions list
...@@ -456,10 +459,13 @@ def course_index(request, course_key): ...@@ -456,10 +459,13 @@ def course_index(request, course_key):
'rerun_notification_id': current_action.id if current_action else None, 'rerun_notification_id': current_action.id if current_action else None,
'course_release_date': course_release_date, 'course_release_date': course_release_date,
'settings_url': settings_url, 'settings_url': settings_url,
'notification_dismiss_url': 'notification_dismiss_url': reverse_course_url(
reverse_course_url('course_notifications_handler', current_action.course_key, kwargs={ 'course_notifications_handler',
current_action.course_key,
kwargs={
'action_state_id': current_action.id, 'action_state_id': current_action.id,
}) if current_action else None, },
) if current_action else None,
}) })
......
...@@ -14,23 +14,24 @@ class CourseMetadata(object): ...@@ -14,23 +14,24 @@ class CourseMetadata(object):
# The list of fields that wouldn't be shown in Advanced Settings. # The list of fields that wouldn't be shown in Advanced Settings.
# Should not be used directly. Instead the filtered_list method should be used if the field needs to be filtered # Should not be used directly. Instead the filtered_list method should be used if the field needs to be filtered
# depending on the feature flag. # depending on the feature flag.
FILTERED_LIST = ['xml_attributes', FILTERED_LIST = [
'start', 'xml_attributes',
'end', 'start',
'enrollment_start', 'end',
'enrollment_end', 'enrollment_start',
'tabs', 'enrollment_end',
'graceperiod', 'tabs',
'checklists', 'graceperiod',
'show_timezone', 'checklists',
'format', 'show_timezone',
'graded', 'format',
'hide_from_toc', 'graded',
'pdf_textbooks', 'hide_from_toc',
'user_partitions', 'pdf_textbooks',
'name', # from xblock 'user_partitions',
'tags', # from xblock 'name', # from xblock
'visible_to_staff_only' 'tags', # from xblock
'visible_to_staff_only',
] ]
@classmethod @classmethod
......
...@@ -104,18 +104,25 @@ js_info_dict = { ...@@ -104,18 +104,25 @@ js_info_dict = {
'packages': ('openassessment',), 'packages': ('openassessment',),
} }
urlpatterns += patterns('', urlpatterns += patterns(
'',
# Serve catalog of localized strings to be rendered by Javascript # Serve catalog of localized strings to be rendered by Javascript
url(r'^i18n.js$', 'django.views.i18n.javascript_catalog', js_info_dict), url(r'^i18n.js$', 'django.views.i18n.javascript_catalog', js_info_dict),
) )
if settings.FEATURES.get('ENABLE_EXPORT_GIT'): if settings.FEATURES.get('ENABLE_EXPORT_GIT'):
urlpatterns += (url(r'^export_git/{}$'.format(settings.COURSE_KEY_PATTERN), urlpatterns += (url(
'contentstore.views.export_git', name='export_git'),) r'^export_git/{}$'.format(
settings.COURSE_KEY_PATTERN,
),
'contentstore.views.export_git',
name='export_git',
),)
if settings.FEATURES.get('ENABLE_SERVICE_STATUS'): if settings.FEATURES.get('ENABLE_SERVICE_STATUS'):
urlpatterns += patterns('', urlpatterns += patterns(
'',
url(r'^status/', include('service_status.urls')), url(r'^status/', include('service_status.urls')),
) )
......
...@@ -219,9 +219,11 @@ def get_cohort(user, course_key): ...@@ -219,9 +219,11 @@ def get_cohort(user, course_key):
return None return None
try: try:
return CourseUserGroup.objects.get(course_id=course_key, return CourseUserGroup.objects.get(
group_type=CourseUserGroup.COHORT, course_id=course_key,
users__id=user.id) group_type=CourseUserGroup.COHORT,
users__id=user.id,
)
except CourseUserGroup.DoesNotExist: except CourseUserGroup.DoesNotExist:
# Didn't find the group. We'll go on to create one if needed. # Didn't find the group. We'll go on to create one if needed.
pass pass
......
...@@ -24,8 +24,11 @@ class CourseUserGroup(models.Model): ...@@ -24,8 +24,11 @@ class CourseUserGroup(models.Model):
# Note: groups associated with particular runs of a course. E.g. Fall 2012 and Spring # Note: groups associated with particular runs of a course. E.g. Fall 2012 and Spring
# 2013 versions of 6.00x will have separate groups. # 2013 versions of 6.00x will have separate groups.
course_id = CourseKeyField(max_length=255, db_index=True, course_id = CourseKeyField(
help_text="Which course is this group associated with?") max_length=255,
db_index=True,
help_text="Which course is this group associated with?",
)
# For now, only have group type 'cohort', but adding a type field to support # For now, only have group type 'cohort', but adding a type field to support
# things like 'question_discussion', 'friends', 'off-line-class', etc # things like 'question_discussion', 'friends', 'off-line-class', etc
......
...@@ -67,7 +67,7 @@ class Role(models.Model): ...@@ -67,7 +67,7 @@ class Role(models.Model):
def inherit_permissions(self, role): # TODO the name of this method is a little bit confusing, def inherit_permissions(self, role): # TODO the name of this method is a little bit confusing,
# since it's one-off and doesn't handle inheritance later # since it's one-off and doesn't handle inheritance later
if role.course_id and role.course_id != self.course_id: if role.course_id and role.course_id != self.course_id:
logging.warning("%s cannot inherit permissions from %s due to course_id inconsistency", \ logging.warning("%s cannot inherit permissions from %s due to course_id inconsistency",
self, role) self, role)
for per in role.permissions.all(): for per in role.permissions.all():
self.add_permission(per) self.add_permission(per)
......
...@@ -46,8 +46,10 @@ class CountryMiddlewareTests(TestCase): ...@@ -46,8 +46,10 @@ class CountryMiddlewareTests(TestCase):
return ip_dict.get(ip_addr, 'US') return ip_dict.get(ip_addr, 'US')
def test_country_code_added(self): def test_country_code_added(self):
request = self.request_factory.get('/somewhere', request = self.request_factory.get(
HTTP_X_FORWARDED_FOR='117.79.83.1') '/somewhere',
HTTP_X_FORWARDED_FOR='117.79.83.1',
)
request.user = self.authenticated_user request.user = self.authenticated_user
self.session_middleware.process_request(request) self.session_middleware.process_request(request)
# No country code exists before request. # No country code exists before request.
...@@ -59,8 +61,10 @@ class CountryMiddlewareTests(TestCase): ...@@ -59,8 +61,10 @@ class CountryMiddlewareTests(TestCase):
self.assertEqual('117.79.83.1', request.session.get('ip_address')) self.assertEqual('117.79.83.1', request.session.get('ip_address'))
def test_ip_address_changed(self): def test_ip_address_changed(self):
request = self.request_factory.get('/somewhere', request = self.request_factory.get(
HTTP_X_FORWARDED_FOR='4.0.0.0') '/somewhere',
HTTP_X_FORWARDED_FOR='4.0.0.0',
)
request.user = self.anonymous_user request.user = self.anonymous_user
self.session_middleware.process_request(request) self.session_middleware.process_request(request)
request.session['country_code'] = 'CN' request.session['country_code'] = 'CN'
...@@ -71,8 +75,10 @@ class CountryMiddlewareTests(TestCase): ...@@ -71,8 +75,10 @@ class CountryMiddlewareTests(TestCase):
self.assertEqual('4.0.0.0', request.session.get('ip_address')) self.assertEqual('4.0.0.0', request.session.get('ip_address'))
def test_ip_address_is_not_changed(self): def test_ip_address_is_not_changed(self):
request = self.request_factory.get('/somewhere', request = self.request_factory.get(
HTTP_X_FORWARDED_FOR='117.79.83.1') '/somewhere',
HTTP_X_FORWARDED_FOR='117.79.83.1',
)
request.user = self.anonymous_user request.user = self.anonymous_user
self.session_middleware.process_request(request) self.session_middleware.process_request(request)
request.session['country_code'] = 'CN' request.session['country_code'] = 'CN'
...@@ -83,8 +89,10 @@ class CountryMiddlewareTests(TestCase): ...@@ -83,8 +89,10 @@ class CountryMiddlewareTests(TestCase):
self.assertEqual('117.79.83.1', request.session.get('ip_address')) self.assertEqual('117.79.83.1', request.session.get('ip_address'))
def test_same_country_different_ip(self): def test_same_country_different_ip(self):
request = self.request_factory.get('/somewhere', request = self.request_factory.get(
HTTP_X_FORWARDED_FOR='117.79.83.100') '/somewhere',
HTTP_X_FORWARDED_FOR='117.79.83.100',
)
request.user = self.anonymous_user request.user = self.anonymous_user
self.session_middleware.process_request(request) self.session_middleware.process_request(request)
request.session['country_code'] = 'CN' request.session['country_code'] = 'CN'
......
...@@ -7,8 +7,7 @@ from django.core.cache import get_cache ...@@ -7,8 +7,7 @@ from django.core.cache import get_cache
class Command(NoArgsCommand): class Command(NoArgsCommand):
help = \ help = 'Import the specified data directory into the default ModuleStore'
'''Import the specified data directory into the default ModuleStore'''
def handle_noargs(self, **options): def handle_noargs(self, **options):
staticfiles_cache = get_cache('staticfiles') staticfiles_cache = get_cache('staticfiles')
......
...@@ -47,7 +47,7 @@ class TestStatus(TestCase): ...@@ -47,7 +47,7 @@ class TestStatus(TestCase):
(global_only, "Hello, Globe", "Hello, Globe", "Hello, Globe"), (global_only, "Hello, Globe", "Hello, Globe", "Hello, Globe"),
(toy_only, None, "A toy story", None), (toy_only, None, "A toy story", None),
(global_and_toy, "Hello, Globe", "Hello, Globe<br>A toy story", "Hello, Globe"), (global_and_toy, "Hello, Globe", "Hello, Globe<br>A toy story", "Hello, Globe"),
] ]
def setUp(self): def setUp(self):
""" """
......
...@@ -18,14 +18,13 @@ from student.models import UserProfile ...@@ -18,14 +18,13 @@ from student.models import UserProfile
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = """Exports all users and user profiles.
'''Exports all users and user profiles.
Caveat: Should be looked over before any run Caveat: Should be looked over before any run
for schema changes. for schema changes.
Current version grabs user_keys from Current version grabs user_keys from
django.contrib.auth.models.User and up_keys django.contrib.auth.models.User and up_keys
from student.userprofile. ''' from student.userprofile."""
def handle(self, *args, **options): def handle(self, *args, **options):
users = list(User.objects.all()) users = list(User.objects.all())
......
...@@ -39,14 +39,13 @@ def import_user(u): ...@@ -39,14 +39,13 @@ def import_user(u):
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = """Exports all users and user profiles.
'''Exports all users and user profiles.
Caveat: Should be looked over before any run Caveat: Should be looked over before any run
for schema changes. for schema changes.
Current version grabs user_keys from Current version grabs user_keys from
django.contrib.auth.models.User and up_keys django.contrib.auth.models.User and up_keys
from student.userprofile. ''' from student.userprofile."""
def handle(self, *args, **options): def handle(self, *args, **options):
extracted = json.load(open('transfer_users.txt')) extracted = json.load(open('transfer_users.txt'))
......
...@@ -21,7 +21,7 @@ class Command(BaseCommand): ...@@ -21,7 +21,7 @@ class Command(BaseCommand):
dest='remove', dest='remove',
default=False, default=False,
help='Remove the user from the group instead of adding it'), help='Remove the user from the group instead of adding it'),
) )
args = '<user|email> <group>' args = '<user|email> <group>'
help = 'Add a user to a group' help = 'Add a user to a group'
......
...@@ -6,6 +6,7 @@ from student.models import UserTestGroup ...@@ -6,6 +6,7 @@ from student.models import UserTestGroup
import random import random
import sys import sys
import datetime import datetime
from textwrap import dedent
import json import json
from pytz import UTC from pytz import UTC
...@@ -25,16 +26,15 @@ def group_from_value(groups, v): ...@@ -25,16 +26,15 @@ def group_from_value(groups, v):
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = dedent("""\
''' Assign users to test groups. Takes a list Assign users to test groups. Takes a list of groups:
of groups: a:0.3,b:0.4,c:0.3 file.txt "Testing something"
a:0.3,b:0.4,c:0.3 file.txt "Testing something" Will assign each user to group a, b, or c with
Will assign each user to group a, b, or c with probability 0.3, 0.4, 0.3. Probabilities must
probability 0.3, 0.4, 0.3. Probabilities must add up to 1.
add up to 1.
Will log what happened to file.txt.
Will log what happened to file.txt. """)
'''
def handle(self, *args, **options): def handle(self, *args, **options):
if len(args) != 3: if len(args) != 3:
......
...@@ -3,8 +3,7 @@ from django.contrib.auth.models import User ...@@ -3,8 +3,7 @@ from django.contrib.auth.models import User
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = 'Extract an e-mail list of all active students.'
''' Extract an e-mail list of all active students. '''
def handle(self, *args, **options): def handle(self, *args, **options):
#text = open(args[0]).read() #text = open(args[0]).read()
......
...@@ -12,7 +12,7 @@ class Command(BaseCommand): ...@@ -12,7 +12,7 @@ class Command(BaseCommand):
dest='unset', dest='unset',
default=False, default=False,
help='Set is_staff to False instead of True'), help='Set is_staff to False instead of True'),
) )
args = '<user|email> [user|email ...]>' args = '<user|email> [user|email ...]>'
help = """ help = """
......
...@@ -8,10 +8,7 @@ import lms.lib.comment_client as cc ...@@ -8,10 +8,7 @@ import lms.lib.comment_client as cc
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = 'Sync all user ids, usernames, and emails to the discussion service'
'''
Sync all user ids, usernames, and emails to the discussion
service'''
def handle(self, *args, **options): def handle(self, *args, **options):
for user in User.objects.all().iterator(): for user in User.objects.all().iterator():
......
...@@ -7,26 +7,25 @@ from student.models import UserProfile ...@@ -7,26 +7,25 @@ from student.models import UserProfile
class Command(BaseCommand): class Command(BaseCommand):
help = \ help = """Extract full user information into a JSON file.
''' Extract full user information into a JSON file. Pass a single filename."""
Pass a single filename.'''
def handle(self, *args, **options): def handle(self, *args, **options):
f = open(args[0], 'w') file_output = open(args[0], 'w')
#text = open(args[0]).read()
#subject = open(args[1]).read()
users = User.objects.all() users = User.objects.all()
l = [] data_list = []
for user in users: for user in users:
up = UserProfile.objects.get(user=user) profile = UserProfile.objects.get(user=user)
d = {'username': user.username, data = {
'email': user.email, 'username': user.username,
'is_active': user.is_active, 'email': user.email,
'joined': user.date_joined.isoformat(), 'is_active': user.is_active,
'name': up.name, 'joined': user.date_joined.isoformat(),
'language': up.language, 'name': profile.name,
'location': up.location} 'language': profile.language,
l.append(d) 'location': profile.location,
json.dump(l, f) }
f.close() data_list.append(data)
json.dump(data_list, file_output)
file_output.close()
...@@ -24,15 +24,13 @@ class UserStandingMiddleware(object): ...@@ -24,15 +24,13 @@ class UserStandingMiddleware(object):
else: else:
if user_account.account_status == UserStanding.ACCOUNT_DISABLED: if user_account.account_status == UserStanding.ACCOUNT_DISABLED:
msg = _( msg = _(
'Your account has been disabled. If you believe ' 'Your account has been disabled. If you believe '
'this was done in error, please contact us at ' 'this was done in error, please contact us at '
'{link_start}{support_email}{link_end}' '{support_email}'
).format( ).format(
support_email=settings.DEFAULT_FEEDBACK_EMAIL, support_email=u'<a href="mailto:{address}?subject={subject_line}">{address}</a>'.format(
link_start=u'<a href="mailto:{address}?subject={subject_line}">'.format( address=settings.DEFAULT_FEEDBACK_EMAIL,
address=settings.DEFAULT_FEEDBACK_EMAIL, subject_line=_('Disabled Account'),
subject_line=_('Disabled Account'), ),
), )
link_end=u'</a>'
)
return HttpResponseForbidden(msg) return HttpResponseForbidden(msg)
...@@ -25,7 +25,7 @@ class AutoAuthEnabledTestCase(UrlResetMixin, TestCase): ...@@ -25,7 +25,7 @@ class AutoAuthEnabledTestCase(UrlResetMixin, TestCase):
(COURSE_ID_SPLIT, SlashSeparatedCourseKey.from_deprecated_string(COURSE_ID_SPLIT)), (COURSE_ID_SPLIT, SlashSeparatedCourseKey.from_deprecated_string(COURSE_ID_SPLIT)),
(COURSE_ID_MONGO, CourseLocator.from_string(COURSE_ID_MONGO)), (COURSE_ID_MONGO, CourseLocator.from_string(COURSE_ID_MONGO)),
(COURSE_ID_SPLIT, CourseLocator.from_string(COURSE_ID_SPLIT)), (COURSE_ID_SPLIT, CourseLocator.from_string(COURSE_ID_SPLIT)),
) )
@patch.dict("django.conf.settings.FEATURES", {"AUTOMATIC_AUTH_FOR_TESTING": True}) @patch.dict("django.conf.settings.FEATURES", {"AUTOMATIC_AUTH_FOR_TESTING": True})
def setUp(self): def setUp(self):
......
...@@ -170,12 +170,14 @@ class TestPasswordPolicy(TestCase): ...@@ -170,12 +170,14 @@ class TestPasswordPolicy(TestCase):
response = self.client.post(self.url, self.url_params) response = self.client.post(self.url, self.url_params)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
obj = json.loads(response.content) obj = json.loads(response.content)
errstring = ("Password: Must be more complex (" errstring = (
"Password: Must be more complex ("
"must contain 3 or more uppercase characters, " "must contain 3 or more uppercase characters, "
"must contain 3 or more digits, " "must contain 3 or more digits, "
"must contain 3 or more punctuation characters, " "must contain 3 or more punctuation characters, "
"must contain 3 or more unique words" "must contain 3 or more unique words"
")") ")"
)
self.assertEqual(obj['value'], errstring) self.assertEqual(obj['value'], errstring)
@patch.dict("django.conf.settings.PASSWORD_COMPLEXITY", { @patch.dict("django.conf.settings.PASSWORD_COMPLEXITY", {
......
...@@ -81,7 +81,7 @@ class StubCommentsServiceHandler(StubHttpRequestHandler): ...@@ -81,7 +81,7 @@ class StubCommentsServiceHandler(StubHttpRequestHandler):
"collection": user_threads, "collection": user_threads,
"page": page, "page": page,
"num_pages": num_pages "num_pages": num_pages
}) })
else: else:
self.send_response(404, content="404 Not Found") self.send_response(404, content="404 Not Found")
......
...@@ -124,7 +124,7 @@ class StubLtiHandler(StubHttpRequestHandler): ...@@ -124,7 +124,7 @@ class StubLtiHandler(StubHttpRequestHandler):
'Content-Type': 'application/xml', 'Content-Type': 'application/xml',
'X-Requested-With': 'XMLHttpRequest', 'X-Requested-With': 'XMLHttpRequest',
'Authorization': self._oauth_sign(url, data) 'Authorization': self._oauth_sign(url, data)
} }
# Send request ignoring verifirecation of SSL certificate # Send request ignoring verifirecation of SSL certificate
response = requests.post(url, data=data, headers=headers, verify=False) response = requests.post(url, data=data, headers=headers, verify=False)
......
...@@ -526,8 +526,7 @@ class StubOraService(StubHttpService): ...@@ -526,8 +526,7 @@ class StubOraService(StubHttpService):
'num_graded': self.DUMMY_DATA['problem_list_num_graded'], 'num_graded': self.DUMMY_DATA['problem_list_num_graded'],
'num_pending': self.DUMMY_DATA['problem_list_num_pending'], 'num_pending': self.DUMMY_DATA['problem_list_num_pending'],
'num_required': self.DUMMY_DATA['problem_list_num_required'] 'num_required': self.DUMMY_DATA['problem_list_num_required']
} for location, name in self.problems.items() } for location, name in self.problems.items()]
]
def register_problem(self, location, name): def register_problem(self, location, name):
""" """
......
...@@ -187,7 +187,8 @@ class StubOraServiceTest(unittest.TestCase): ...@@ -187,7 +187,8 @@ class StubOraServiceTest(unittest.TestCase):
params={'course_id': 'test course'} params={'course_id': 'test course'}
) )
self._assert_response(response, self._assert_response(
response,
{'version': 1, 'success': True, 'problem_list': []} {'version': 1, 'success': True, 'problem_list': []}
) )
......
...@@ -59,8 +59,10 @@ class TrackMiddleware(object): ...@@ -59,8 +59,10 @@ class TrackMiddleware(object):
if string in get_dict: if string in get_dict:
get_dict[string] = '*' * 8 get_dict[string] = '*' * 8
event = {'GET': dict(get_dict), event = {
'POST': dict(post_dict)} 'GET': dict(get_dict),
'POST': dict(post_dict),
}
# TODO: Confirm no large file uploads # TODO: Confirm no large file uploads
event = json.dumps(event) event = json.dumps(event)
......
...@@ -694,7 +694,7 @@ class LoncapaProblem(object): ...@@ -694,7 +694,7 @@ class LoncapaProblem(object):
return return
if (problemtree.tag == 'script' and problemtree.get('type') if (problemtree.tag == 'script' and problemtree.get('type')
and 'javascript' in problemtree.get('type')): and 'javascript' in problemtree.get('type')):
# leave javascript intact. # leave javascript intact.
return deepcopy(problemtree) return deepcopy(problemtree)
......
...@@ -24,15 +24,20 @@ class TagRegistry(object): ...@@ -24,15 +24,20 @@ class TagRegistry(object):
if len(cls.tags) == 0: if len(cls.tags) == 0:
raise ValueError("No tags specified for class {0}".format(cls.__name__)) raise ValueError("No tags specified for class {0}".format(cls.__name__))
for t in cls.tags: for tag in cls.tags:
if t in self._mapping: if tag in self._mapping:
other_cls = self._mapping[t] other_cls = self._mapping[tag]
if cls == other_cls: if cls == other_cls:
# registering the same class multiple times seems silly, but ok # registering the same class multiple times seems silly, but ok
continue continue
raise ValueError("Tag {0} already registered by class {1}." raise ValueError(
" Can't register for class {2}" "Tag {0} already registered by class {1}."
.format(t, other_cls.__name__, cls.__name__)) " Can't register for class {2}".format(
tag,
other_cls.__name__,
cls.__name__,
)
)
# Ok, should be good to change state now. # Ok, should be good to change state now.
for t in cls.tags: for t in cls.tags:
......
...@@ -2120,8 +2120,11 @@ class CodeResponse(LoncapaResponse): ...@@ -2120,8 +2120,11 @@ class CodeResponse(LoncapaResponse):
parsed = False parsed = False
if not parsed: if not parsed:
log.error("Unable to parse external grader message as valid" log.error(
" XML: score_msg['msg']=%s", msg) "Unable to parse external grader message as valid"
" XML: score_msg['msg']=%s",
msg,
)
return fail return fail
return (True, score_result['correct'], score_result['score'], msg) return (True, score_result['correct'], score_result['score'], msg)
......
...@@ -156,7 +156,8 @@ def sanitize_html(html_code): ...@@ -156,7 +156,8 @@ def sanitize_html(html_code):
'audio': ['controls', 'autobuffer', 'autoplay', 'src'], 'audio': ['controls', 'autobuffer', 'autoplay', 'src'],
'img': ['src', 'width', 'height', 'class'] 'img': ['src', 'width', 'height', 'class']
}) })
output = bleach.clean(html_code, output = bleach.clean(
html_code,
tags=bleach.ALLOWED_TAGS + ['div', 'p', 'audio', 'pre', 'img', 'span'], tags=bleach.ALLOWED_TAGS + ['div', 'p', 'audio', 'pre', 'img', 'span'],
styles=['white-space'], styles=['white-space'],
attributes=attributes attributes=attributes
......
...@@ -339,8 +339,12 @@ def divide_chemical_expression(s1, s2, ignore_state=False): ...@@ -339,8 +339,12 @@ def divide_chemical_expression(s1, s2, ignore_state=False):
if treedic['1 phases'] != treedic['2 phases']: if treedic['1 phases'] != treedic['2 phases']:
return False return False
if any(map(lambda x, y: x / y - treedic['1 factors'][0] / treedic['2 factors'][0], if any(
treedic['1 factors'], treedic['2 factors'])): [
x / y - treedic['1 factors'][0] / treedic['2 factors'][0]
for (x, y) in zip(treedic['1 factors'], treedic['2 factors'])
]
):
# factors are not proportional # factors are not proportional
return False return False
else: else:
......
...@@ -2,8 +2,12 @@ import codecs ...@@ -2,8 +2,12 @@ import codecs
from fractions import Fraction from fractions import Fraction
import unittest import unittest
from .chemcalc import (compare_chemical_expression, divide_chemical_expression, from .chemcalc import (
render_to_html, chemical_equations_equal) compare_chemical_expression,
divide_chemical_expression,
render_to_html,
chemical_equations_equal,
)
import miller import miller
...@@ -36,8 +40,12 @@ class Test_Compare_Equations(unittest.TestCase): ...@@ -36,8 +40,12 @@ class Test_Compare_Equations(unittest.TestCase):
self.assertTrue(chemical_equations_equal('H2 + O2 -> H2O2', self.assertTrue(chemical_equations_equal('H2 + O2 -> H2O2',
'2O2 + 2H2 -> 2H2O2')) '2O2 + 2H2 -> 2H2O2'))
self.assertFalse(chemical_equations_equal('2H2 + O2 -> H2O2', self.assertFalse(
'2O2 + 2H2 -> 2H2O2')) chemical_equations_equal(
'2H2 + O2 -> H2O2',
'2O2 + 2H2 -> 2H2O2',
)
)
def test_different_arrows(self): def test_different_arrows(self):
self.assertTrue(chemical_equations_equal('H2 + O2 -> H2O2', self.assertTrue(chemical_equations_equal('H2 + O2 -> H2O2',
...@@ -50,8 +58,13 @@ class Test_Compare_Equations(unittest.TestCase): ...@@ -50,8 +58,13 @@ class Test_Compare_Equations(unittest.TestCase):
self.assertTrue(chemical_equations_equal('H2 + O2 -> H2O2', self.assertTrue(chemical_equations_equal('H2 + O2 -> H2O2',
'2O2 + 2H2 -> 2H2O2')) '2O2 + 2H2 -> 2H2O2'))
self.assertFalse(chemical_equations_equal('H2 + O2 -> H2O2', self.assertFalse(
'2O2 + 2H2 -> 2H2O2', exact=True)) chemical_equations_equal(
'H2 + O2 -> H2O2',
'2O2 + 2H2 -> 2H2O2',
exact=True,
)
)
# order still doesn't matter # order still doesn't matter
self.assertTrue(chemical_equations_equal('H2 + O2 -> H2O2', self.assertTrue(chemical_equations_equal('H2 + O2 -> H2O2',
......
...@@ -98,7 +98,7 @@ class PositionsCompare(list): ...@@ -98,7 +98,7 @@ class PositionsCompare(list):
return False return False
if (isinstance(self[0], (list, int, float)) and if (isinstance(self[0], (list, int, float)) and
isinstance(other[0], (list, int, float))): isinstance(other[0], (list, int, float))):
return self.coordinate_positions_compare(other) return self.coordinate_positions_compare(other)
elif (isinstance(self[0], (unicode, str)) and elif (isinstance(self[0], (unicode, str)) and
...@@ -352,8 +352,10 @@ class DragAndDrop(object): ...@@ -352,8 +352,10 @@ class DragAndDrop(object):
# correct_answer entries. If the draggable is mentioned in at least one # correct_answer entries. If the draggable is mentioned in at least one
# correct_answer entry, the value is False. # correct_answer entry, the value is False.
# default to consider every user answer excess until proven otherwise. # default to consider every user answer excess until proven otherwise.
self.excess_draggables = dict((users_draggable.keys()[0], True) self.excess_draggables = dict(
for users_draggable in user_answer) (users_draggable.keys()[0], True)
for users_draggable in user_answer
)
# Convert nested `user_answer` to flat format. # Convert nested `user_answer` to flat format.
user_answer = flat_user_answer(user_answer) user_answer = flat_user_answer(user_answer)
...@@ -368,7 +370,8 @@ class DragAndDrop(object): ...@@ -368,7 +370,8 @@ class DragAndDrop(object):
if draggable_name in answer['draggables']: if draggable_name in answer['draggables']:
user_groups_data.append(draggable_name) user_groups_data.append(draggable_name)
user_positions_data.append( user_positions_data.append(
draggable_dict[draggable_name]) draggable_dict[draggable_name]
)
# proved that this is not excess # proved that this is not excess
self.excess_draggables[draggable_name] = False self.excess_draggables[draggable_name] = False
......
...@@ -15,9 +15,10 @@ _ = lambda text: text ...@@ -15,9 +15,10 @@ _ = lambda text: text
class AnnotatableFields(object): class AnnotatableFields(object):
data = String(help=_("XML data for the annotation"), scope=Scope.content, data = String(
default=textwrap.dedent( help=_("XML data for the annotation"),
"""\ scope=Scope.content,
default=textwrap.dedent("""
<annotatable> <annotatable>
<instructions> <instructions>
<p>Enter your (optional) instructions for the exercise in HTML format.</p> <p>Enter your (optional) instructions for the exercise in HTML format.</p>
...@@ -33,7 +34,8 @@ class AnnotatableFields(object): ...@@ -33,7 +34,8 @@ class AnnotatableFields(object):
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. <annotation title="My title" body="My comment" highlight="yellow" problem="0">Ut sodales laoreet est, egestas gravida felis egestas nec.</annotation> Aenean at volutpat erat. Cras commodo viverra nibh in aliquam.</p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. <annotation title="My title" body="My comment" highlight="yellow" problem="0">Ut sodales laoreet est, egestas gravida felis egestas nec.</annotation> Aenean at volutpat erat. Cras commodo viverra nibh in aliquam.</p>
<p>Nulla facilisi. <annotation body="Basic annotation example." problem="1">Pellentesque id vestibulum libero.</annotation> Suspendisse potenti. Morbi scelerisque nisi vitae felis dictum mattis. Nam sit amet magna elit. Nullam volutpat cursus est, sit amet sagittis odio vulputate et. Curabitur euismod, orci in vulputate imperdiet, augue lorem tempor purus, id aliquet augue turpis a est. Aenean a sagittis libero. Praesent fringilla pretium magna, non condimentum risus elementum nec. Pellentesque faucibus elementum pharetra. Pellentesque vitae metus eros.</p> <p>Nulla facilisi. <annotation body="Basic annotation example." problem="1">Pellentesque id vestibulum libero.</annotation> Suspendisse potenti. Morbi scelerisque nisi vitae felis dictum mattis. Nam sit amet magna elit. Nullam volutpat cursus est, sit amet sagittis odio vulputate et. Curabitur euismod, orci in vulputate imperdiet, augue lorem tempor purus, id aliquet augue turpis a est. Aenean a sagittis libero. Praesent fringilla pretium magna, non condimentum risus elementum nec. Pellentesque faucibus elementum pharetra. Pellentesque vitae metus eros.</p>
</annotatable> </annotatable>
""")) """)
)
display_name = String( display_name = String(
display_name=_("Display Name"), display_name=_("Display Name"),
help=_("Display name for this module"), help=_("Display name for this module"),
......
...@@ -259,10 +259,12 @@ class CombinedOpenEndedFields(object): ...@@ -259,10 +259,12 @@ class CombinedOpenEndedFields(object):
scope=Scope.settings scope=Scope.settings
) )
extended_due = Date( extended_due = Date(
help=_("Date that this problem is due by for a particular student. This " help=_(
"can be set by an instructor, and will override the global due " "Date that this problem is due by for a particular student. This "
"date if it is set to a date that is later than the global due " "can be set by an instructor, and will override the global due "
"date."), "date if it is set to a date that is later than the global due "
"date."
),
default=None, default=None,
scope=Scope.user_state, scope=Scope.user_state,
) )
...@@ -315,9 +317,11 @@ class CombinedOpenEndedFields(object): ...@@ -315,9 +317,11 @@ class CombinedOpenEndedFields(object):
) )
peer_grade_finished_submissions_when_none_pending = Boolean( peer_grade_finished_submissions_when_none_pending = Boolean(
display_name=_('Allow "overgrading" of peer submissions'), display_name=_('Allow "overgrading" of peer submissions'),
help=_("EXPERIMENTAL FEATURE. Allow students to peer grade submissions that already have the requisite number of graders, " help=_(
"but ONLY WHEN all submissions they are eligible to grade already have enough graders. " "EXPERIMENTAL FEATURE. Allow students to peer grade submissions that already have the requisite number of graders, "
"This is intended for use when settings for `Required Peer Grading` > `Peer Graders per Response`"), "but ONLY WHEN all submissions they are eligible to grade already have enough graders. "
"This is intended for use when settings for `Required Peer Grading` > `Peer Graders per Response`"
),
default=False, default=False,
scope=Scope.settings, scope=Scope.settings,
) )
......
...@@ -39,7 +39,7 @@ def make_error_tracker(): ...@@ -39,7 +39,7 @@ def make_error_tracker():
# don't display irrelevant gunicorn sync error # don't display irrelevant gunicorn sync error
if (('python2.7/site-packages/gunicorn/workers/sync.py' in exc_str) and if (('python2.7/site-packages/gunicorn/workers/sync.py' in exc_str) and
('[Errno 11] Resource temporarily unavailable' in exc_str)): ('[Errno 11] Resource temporarily unavailable' in exc_str)):
exc_str = '' exc_str = ''
errors.append((msg, exc_str)) errors.append((msg, exc_str))
......
...@@ -359,8 +359,10 @@ class AssignmentFormatGrader(CourseGrader): ...@@ -359,8 +359,10 @@ class AssignmentFormatGrader(CourseGrader):
# if there is only one entry in a section, suppress the existing individual entry and the average, # if there is only one entry in a section, suppress the existing individual entry and the average,
# and just display a single entry for the section. That way it acts automatically like a # and just display a single entry for the section. That way it acts automatically like a
# SingleSectionGrader. # SingleSectionGrader.
total_detail = u"{section_type} = {percent:.0%}".format(percent=total_percent, total_detail = u"{section_type} = {percent:.0%}".format(
section_type=self.section_type) percent=total_percent,
section_type=self.section_type,
)
total_label = u"{short_label}".format(short_label=self.short_label) total_label = u"{short_label}".format(short_label=self.short_label)
breakdown = [{'percent': total_percent, 'label': total_label, breakdown = [{'percent': total_percent, 'label': total_label,
'detail': total_detail, 'category': self.category, 'prominent': True}, ] 'detail': total_detail, 'category': self.category, 'prominent': True}, ]
......
...@@ -19,7 +19,8 @@ _ = lambda text: text ...@@ -19,7 +19,8 @@ _ = lambda text: text
class AnnotatableFields(object): class AnnotatableFields(object):
""" Fields for `ImageModule` and `ImageDescriptor`. """ """ Fields for `ImageModule` and `ImageDescriptor`. """
data = String(help=_("XML data for the annotation"), data = String(
help=_("XML data for the annotation"),
scope=Scope.content, scope=Scope.content,
default=textwrap.dedent("""\ default=textwrap.dedent("""\
<annotatable> <annotatable>
......
...@@ -23,9 +23,12 @@ class MakoModuleDescriptor(XModuleDescriptor): ...@@ -23,9 +23,12 @@ class MakoModuleDescriptor(XModuleDescriptor):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MakoModuleDescriptor, self).__init__(*args, **kwargs) super(MakoModuleDescriptor, self).__init__(*args, **kwargs)
if getattr(self.runtime, 'render_template', None) is None: if getattr(self.runtime, 'render_template', None) is None:
raise TypeError('{runtime} must have a render_template function' raise TypeError(
' in order to use a MakoDescriptor'.format( '{runtime} must have a render_template function'
runtime=self.runtime)) ' in order to use a MakoDescriptor'.format(
runtime=self.runtime,
)
)
def get_context(self): def get_context(self):
""" """
......
...@@ -152,16 +152,21 @@ class MongoConnection(object): ...@@ -152,16 +152,21 @@ class MongoConnection(object):
original_version (str or ObjectID): The id of a structure original_version (str or ObjectID): The id of a structure
block_key (BlockKey): The id of the block in question block_key (BlockKey): The id of the block in question
""" """
return [structure_from_mongo(structure) for structure in self.structures.find({ return [
'original_version': original_version, structure_from_mongo(structure)
'blocks': { for structure in self.structures.find({
'$elemMatch': { 'original_version': original_version,
'block_id': block_key.id, 'blocks': {
'block_type': block_key.type, '$elemMatch': {
'edit_info.update_version': {'$exists': True}, 'block_id': block_key.id,
} 'block_type': block_key.type,
} 'edit_info.update_version': {
})] '$exists': True,
},
},
},
})
]
def insert_structure(self, structure): def insert_structure(self, structure):
""" """
......
...@@ -75,9 +75,16 @@ def get_dummy_course(start, announcement=None, is_new=None, advertised_start=Non ...@@ -75,9 +75,16 @@ def get_dummy_course(start, announcement=None, is_new=None, advertised_start=Non
<html url_name="h" display_name="H">Two houses, ...</html> <html url_name="h" display_name="H">Two houses, ...</html>
</chapter> </chapter>
</course> </course>
'''.format(org=ORG, course=COURSE, start=start, is_new=is_new, '''.format(
announcement=announcement, advertised_start=advertised_start, end=end, org=ORG,
certs=certs) course=COURSE,
start=start,
is_new=is_new,
announcement=announcement,
advertised_start=advertised_start,
end=end,
certs=certs,
)
return system.process_xml(start_xml) return system.process_xml(start_xml)
......
...@@ -319,7 +319,7 @@ class PeerGradingModuleLinkedTest(unittest.TestCase, DummyModulestore): ...@@ -319,7 +319,7 @@ class PeerGradingModuleLinkedTest(unittest.TestCase, DummyModulestore):
'use_for_single_location': True, 'use_for_single_location': True,
'link_to_location': self.coe_location.to_deprecated_string(), 'link_to_location': self.coe_location.to_deprecated_string(),
'graded': True, 'graded': True,
}) })
@property @property
def scope_ids(self): def scope_ids(self):
......
...@@ -17,7 +17,8 @@ _ = lambda text: text ...@@ -17,7 +17,8 @@ _ = lambda text: text
class AnnotatableFields(object): class AnnotatableFields(object):
"""Fields for `TextModule` and `TextDescriptor`.""" """Fields for `TextModule` and `TextDescriptor`."""
data = String(help=_("XML data for the annotation"), data = String(
help=_("XML data for the annotation"),
scope=Scope.content, scope=Scope.content,
default=textwrap.dedent("""\ default=textwrap.dedent("""\
<annotatable> <annotatable>
......
...@@ -19,7 +19,8 @@ _ = lambda text: text ...@@ -19,7 +19,8 @@ _ = lambda text: text
class AnnotatableFields(object): class AnnotatableFields(object):
""" Fields for `VideoModule` and `VideoDescriptor`. """ """ Fields for `VideoModule` and `VideoDescriptor`. """
data = String(help=_("XML data for the annotation"), data = String(
help=_("XML data for the annotation"),
scope=Scope.content, scope=Scope.content,
default=textwrap.dedent("""\ default=textwrap.dedent("""\
<annotatable> <annotatable>
......
...@@ -202,7 +202,7 @@ class XModuleMixin(XBlockMixin): ...@@ -202,7 +202,7 @@ class XModuleMixin(XBlockMixin):
# an XModule context, not an XModuleDescriptor context, # an XModule context, not an XModuleDescriptor context,
# so we should use the xmodule_runtime (ModuleSystem) as the runtime. # so we should use the xmodule_runtime (ModuleSystem) as the runtime.
if (not isinstance(self, (XModule, XModuleDescriptor)) and if (not isinstance(self, (XModule, XModuleDescriptor)) and
self.xmodule_runtime is not None): self.xmodule_runtime is not None):
return PureSystem(self.xmodule_runtime, self._runtime) return PureSystem(self.xmodule_runtime, self._runtime)
else: else:
return self._runtime return self._runtime
......
...@@ -176,8 +176,13 @@ htmlhelp_basename = 'MathJaxdoc' ...@@ -176,8 +176,13 @@ htmlhelp_basename = 'MathJaxdoc'
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]). # (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [ latex_documents = [
('index', 'MathJax.tex', u'MathJax Documentation', (
u'Davide Cervone, Casey Stark, Robert Miner, Paul Topping', 'manual'), 'index',
'MathJax.tex',
u'MathJax Documentation',
u'Davide Cervone, Casey Stark, Robert Miner, Paul Topping',
'manual',
),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
......
...@@ -147,7 +147,8 @@ class CourseNavPage(PageObject): ...@@ -147,7 +147,8 @@ class CourseNavPage(PageObject):
# It *would* make sense to always get the HTML, but unfortunately # It *would* make sense to always get the HTML, but unfortunately
# the open tab has some child <span> tags that we don't want. # the open tab has some child <span> tags that we don't want.
return self.q( return self.q(
css=subsection_css).map( css=subsection_css
).map(
lambda el: el.text.strip().split('\n')[0] if el.is_displayed() else el.get_attribute('innerHTML').strip() lambda el: el.text.strip().split('\n')[0] if el.is_displayed() else el.get_attribute('innerHTML').strip()
).results ).results
......
...@@ -33,8 +33,14 @@ class ORAComponentTest(StudioCourseTest): ...@@ -33,8 +33,14 @@ class ORAComponentTest(StudioCourseTest):
XBlockFixtureDesc('chapter', 'Test Section').add_children( XBlockFixtureDesc('chapter', 'Test Section').add_children(
XBlockFixtureDesc('sequential', 'Test Subsection').add_children( XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
XBlockFixtureDesc('vertical', 'Test Unit').add_children( XBlockFixtureDesc('vertical', 'Test Unit').add_children(
XBlockFixtureDesc('combinedopenended', "Peer Problem", XBlockFixtureDesc(
data=load_data_str('ora_peer_problem.xml'), metadata={'graded': True}), 'combinedopenended',
"Peer Problem",
data=load_data_str('ora_peer_problem.xml'),
metadata={
'graded': True,
},
),
XBlockFixtureDesc('peergrading', 'Peer Module'), XBlockFixtureDesc('peergrading', 'Peer Module'),
) )
) )
......
...@@ -68,19 +68,39 @@ class OpenResponseTest(UniqueCourseTest): ...@@ -68,19 +68,39 @@ class OpenResponseTest(UniqueCourseTest):
XBlockFixtureDesc('chapter', 'Test Section').add_children( XBlockFixtureDesc('chapter', 'Test Section').add_children(
XBlockFixtureDesc('sequential', 'Test Subsection').add_children( XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
XBlockFixtureDesc('combinedopenended', 'Self-Assessed', XBlockFixtureDesc(
data=load_data_str('ora_self_problem.xml'), metadata={'graded': True}), 'combinedopenended',
'Self-Assessed',
XBlockFixtureDesc('combinedopenended', 'AI-Assessed', data=load_data_str('ora_self_problem.xml'),
data=load_data_str('ora_ai_problem.xml'), metadata={'graded': True}), metadata={
'graded': True,
XBlockFixtureDesc('combinedopenended', self.peer_problem_name, },
data=load_data_str('ora_peer_problem.xml'), metadata={'graded': True}), ),
XBlockFixtureDesc(
'combinedopenended',
'AI-Assessed',
data=load_data_str('ora_ai_problem.xml'),
metadata={
'graded': True,
},
),
XBlockFixtureDesc(
'combinedopenended',
self.peer_problem_name,
data=load_data_str('ora_peer_problem.xml'),
metadata={
'graded': True,
},
),
# This is the interface a student can use to grade his/her peers # This is the interface a student can use to grade his/her peers
XBlockFixtureDesc('peergrading', 'Peer Module'), XBlockFixtureDesc('peergrading', 'Peer Module'),
))).install() )
)
).install()
# Configure the XQueue stub's response for the text we will submit # Configure the XQueue stub's response for the text we will submit
# The submission text is unique so we can associate each response with a particular test case. # The submission text is unique so we can associate each response with a particular test case.
......
...@@ -76,7 +76,7 @@ def cleanup(filepath, remove_meta): ...@@ -76,7 +76,7 @@ def cleanup(filepath, remove_meta):
print "WARNING: {0} has both slug and url_name".format(node) print "WARNING: {0} has both slug and url_name".format(node)
if ('url_name' in attrs and 'filename' in attrs and if ('url_name' in attrs and 'filename' in attrs and
len(attrs) == 2 and attrs['url_name'] == attrs['filename']): len(attrs) == 2 and attrs['url_name'] == attrs['filename']):
# This is a pointer tag in disguise. Get rid of the filename. # This is a pointer tag in disguise. Get rid of the filename.
print 'turning {0}.{1} into a pointer tag'.format(node.tag, attrs['url_name']) print 'turning {0}.{1} into a pointer tag'.format(node.tag, attrs['url_name'])
del attrs['filename'] del attrs['filename']
......
...@@ -47,8 +47,14 @@ sys.path.append(root / "lms/djangoapps") ...@@ -47,8 +47,14 @@ sys.path.append(root / "lms/djangoapps")
sys.path.append(root / "lms/lib") sys.path.append(root / "lms/lib")
sys.path.append(root / "cms/djangoapps") sys.path.append(root / "cms/djangoapps")
sys.path.append(root / "cms/lib") sys.path.append(root / "cms/lib")
sys.path.insert(0, os.path.abspath(os.path.normpath(os.path.dirname(__file__) sys.path.insert(
+ '/../../../'))) 0,
os.path.abspath(
os.path.normpath(
os.path.dirname(__file__) + '/../../../'
)
)
)
sys.path.append('.') sys.path.append('.')
# django configuration - careful here # django configuration - careful here
...@@ -134,7 +140,7 @@ MOCK_MODULES = [ ...@@ -134,7 +140,7 @@ MOCK_MODULES = [
'yaml', 'yaml',
'webob', 'webob',
'webob.multidict', 'webob.multidict',
] ]
if on_rtd: if on_rtd:
for mod_name in MOCK_MODULES: for mod_name in MOCK_MODULES:
......
...@@ -39,8 +39,14 @@ sys.path.append(root / "lms/djangoapps/mobile_api/course_info") ...@@ -39,8 +39,14 @@ sys.path.append(root / "lms/djangoapps/mobile_api/course_info")
sys.path.append(root / "lms/djangoapps/mobile_api/users") sys.path.append(root / "lms/djangoapps/mobile_api/users")
sys.path.append(root / "lms/djangoapps/mobile_api/video_outlines") sys.path.append(root / "lms/djangoapps/mobile_api/video_outlines")
sys.path.insert(0, os.path.abspath(os.path.normpath(os.path.dirname(__file__) sys.path.insert(
+ '/../../../'))) 0,
os.path.abspath(
os.path.normpath(
os.path.dirname(__file__) + '/../../../'
)
)
)
sys.path.append('.') sys.path.append('.')
# django configuration - careful here # django configuration - careful here
...@@ -126,7 +132,7 @@ MOCK_MODULES = [ ...@@ -126,7 +132,7 @@ MOCK_MODULES = [
'yaml', 'yaml',
'webob', 'webob',
'webob.multidict', 'webob.multidict',
] ]
if on_rtd: if on_rtd:
for mod_name in MOCK_MODULES: for mod_name in MOCK_MODULES:
......
...@@ -196,21 +196,26 @@ htmlhelp_basename = 'edxdoc' ...@@ -196,21 +196,26 @@ htmlhelp_basename = 'edxdoc'
# -- Options for LaTeX output -------------------------------------------------- # -- Options for LaTeX output --------------------------------------------------
latex_elements = { latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper', #'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt'). # The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt', #'pointsize': '10pt',
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
#'preamble': '', #'preamble': '',
} }
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]). # (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [ latex_documents = [
('index', 'getting_started.tex', u'edX Studio Documentation', (
u'EdX Doc Team', 'manual'), 'index',
'getting_started.tex',
u'edX Studio Documentation',
u'EdX Doc Team',
'manual',
),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
...@@ -253,9 +258,15 @@ man_pages = [ ...@@ -253,9 +258,15 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
('index', 'getting_started', u'getting_started Documentation', (
u'EdX Doc Team', 'getting_started', 'One line description of project.', 'index',
'Miscellaneous'), 'getting_started',
u'getting_started Documentation',
u'EdX Doc Team',
'getting_started',
'One line description of project.',
'Miscellaneous',
),
] ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.
......
...@@ -154,7 +154,7 @@ def _has_access_course_desc(user, action, course): ...@@ -154,7 +154,7 @@ def _has_access_course_desc(user, action, course):
# if using registration method to restrict (say shibboleth) # if using registration method to restrict (say shibboleth)
if settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain: if settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain:
if user is not None and user.is_authenticated() and \ if user is not None and user.is_authenticated() and \
ExternalAuthMap.objects.filter(user=user, external_domain=course.enrollment_domain): ExternalAuthMap.objects.filter(user=user, external_domain=course.enrollment_domain):
debug("Allow: external_auth of " + course.enrollment_domain) debug("Allow: external_auth of " + course.enrollment_domain)
reg_method_ok = True reg_method_ok = True
else: else:
......
...@@ -32,7 +32,7 @@ def create_cert_course(): ...@@ -32,7 +32,7 @@ def create_cert_course():
min_price=16, min_price=16,
suggested_prices='32,64,128', suggested_prices='32,64,128',
currency='usd', currency='usd',
) )
def register(): def register():
......
...@@ -260,8 +260,10 @@ def _grade(student, request, course, keep_raw_scores): ...@@ -260,8 +260,10 @@ def _grade(student, request, course, keep_raw_scores):
if graded_total.possible > 0: if graded_total.possible > 0:
format_scores.append(graded_total) format_scores.append(graded_total)
else: else:
log.info("Unable to grade a section with a total possible score of zero. " + log.info(
str(section_descriptor.location)) "Unable to grade a section with a total possible score of zero. " +
str(section_descriptor.location)
)
totaled_scores[section_format] = format_scores totaled_scores[section_format] = format_scores
......
...@@ -54,10 +54,11 @@ class StudentModule(models.Model): ...@@ -54,10 +54,11 @@ class StudentModule(models.Model):
## Grade, and are we done? ## Grade, and are we done?
grade = models.FloatField(null=True, blank=True, db_index=True) grade = models.FloatField(null=True, blank=True, db_index=True)
max_grade = models.FloatField(null=True, blank=True) max_grade = models.FloatField(null=True, blank=True)
DONE_TYPES = (('na', 'NOT_APPLICABLE'), DONE_TYPES = (
('f', 'FINISHED'), ('na', 'NOT_APPLICABLE'),
('i', 'INCOMPLETE'), ('f', 'FINISHED'),
) ('i', 'INCOMPLETE'),
)
done = models.CharField(max_length=8, choices=DONE_TYPES, default='na', db_index=True) done = models.CharField(max_length=8, choices=DONE_TYPES, default='na', db_index=True)
created = models.DateTimeField(auto_now_add=True, db_index=True) created = models.DateTimeField(auto_now_add=True, db_index=True)
......
class completion(object):
def __init__(self, **d):
self.dict = dict({'duration_total': 0,
'duration_watched': 0,
'done': True,
'questions_correct': 0,
'questions_incorrect': 0,
'questions_total': 0})
if d:
self.dict.update(d)
def __getitem__(self, key):
return self.dict[key]
def __setitem__(self, key, value):
self.dict[key] = value
def __add__(self, other):
result = dict(self.dict)
for item in ['duration_total',
'duration_watched',
'done',
'questions_correct',
'questions_incorrect',
'questions_total']:
result[item] = result[item] + other.dict[item]
return completion(**result)
def __contains__(self, key):
return key in dict
def __repr__(self):
return repr(self.dict)
if __name__ == '__main__':
dict1 = completion(duration_total=5)
dict2 = completion(duration_total=7)
print dict1 + dict2
...@@ -27,6 +27,8 @@ class LoginEnrollmentTestCase(TestCase): ...@@ -27,6 +27,8 @@ class LoginEnrollmentTestCase(TestCase):
Provides support for user creation, Provides support for user creation,
activation, login, and course enrollment. activation, login, and course enrollment.
""" """
user = None
def setup_user(self): def setup_user(self):
""" """
Create a user account, activate, and log in. Create a user account, activate, and log in.
...@@ -34,8 +36,11 @@ class LoginEnrollmentTestCase(TestCase): ...@@ -34,8 +36,11 @@ class LoginEnrollmentTestCase(TestCase):
self.email = 'foo@test.com' self.email = 'foo@test.com'
self.password = 'bar' self.password = 'bar'
self.username = 'test' self.username = 'test'
self.user = self.create_account(self.username, self.user = self.create_account(
self.email, self.password) self.username,
self.email,
self.password,
)
self.activate_user(self.email) self.activate_user(self.email)
self.login(self.email, self.password) self.login(self.email, self.password)
......
...@@ -50,8 +50,10 @@ class TestNavigation(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -50,8 +50,10 @@ class TestNavigation(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.tabssection = ItemFactory.create(parent=self.chapterchrome, self.tabssection = ItemFactory.create(parent=self.chapterchrome,
display_name='tabs', display_name='tabs',
chrome='tabs') chrome='tabs')
self.defaultchromesection = ItemFactory.create(parent=self.chapterchrome, self.defaultchromesection = ItemFactory.create(
display_name='defaultchrome') parent=self.chapterchrome,
display_name='defaultchrome',
)
self.fullchromesection = ItemFactory.create(parent=self.chapterchrome, self.fullchromesection = ItemFactory.create(parent=self.chapterchrome,
display_name='fullchrome', display_name='fullchrome',
chrome='accordion,tabs') chrome='accordion,tabs')
......
from django.test import TestCase
from courseware import progress
from mock import MagicMock
class ProgessTests(TestCase):
def setUp(self):
self.d = dict({'duration_total': 0,
'duration_watched': 0,
'done': True,
'questions_correct': 4,
'questions_incorrect': 0,
'questions_total': 0})
self.c = progress.completion()
self.c2 = progress.completion()
self.c2.dict = dict({'duration_total': 0,
'duration_watched': 0,
'done': True,
'questions_correct': 2,
'questions_incorrect': 1,
'questions_total': 0})
self.cplusc2 = dict({'duration_total': 0,
'duration_watched': 0,
'done': True,
'questions_correct': 2,
'questions_incorrect': 1,
'questions_total': 0})
self.oth = dict({'duration_total': 0,
'duration_watched': 0,
'done': True,
'questions_correct': 4,
'questions_incorrect': 0,
'questions_total': 7})
self.x = MagicMock()
self.x.dict = self.oth
self.d_oth = {'duration_total': 0,
'duration_watched': 0,
'done': True,
'questions_correct': 4,
'questions_incorrect': 0,
'questions_total': 7}
def test_getitem(self):
self.assertEqual(self.c.__getitem__('duration_watched'), 0)
def test_setitem(self):
self.c.__setitem__('questions_correct', 4)
self.assertEqual(str(self.c), str(self.d))
def test_repr(self):
self.assertEqual(self.c.__repr__(), str(progress.completion()))
...@@ -773,8 +773,8 @@ def course_about(request, course_id): ...@@ -773,8 +773,8 @@ def course_about(request, course_id):
'invitation_only': invitation_only, 'invitation_only': invitation_only,
'active_reg_button': active_reg_button, 'active_reg_button': active_reg_button,
'is_shib_course': is_shib_course, 'is_shib_course': is_shib_course,
# We do not want to display the internal courseware header, which is used when the course is found in the # We do not want to display the internal courseware header, which is used when the course is found in the
# context. This value is therefor explicitly set to render the appropriate header. # context. This value is therefor explicitly set to render the appropriate header.
'disable_courseware_header': True, 'disable_courseware_header': True,
'is_shopping_cart_enabled': _is_shopping_cart_enabled, 'is_shopping_cart_enabled': _is_shopping_cart_enabled,
'cart_link': reverse('shoppingcart.views.show_cart'), 'cart_link': reverse('shoppingcart.views.show_cart'),
......
...@@ -301,8 +301,13 @@ def queue_subtasks_for_query(entry, action_name, create_subtask_fcn, item_querys ...@@ -301,8 +301,13 @@ def queue_subtasks_for_query(entry, action_name, create_subtask_fcn, item_querys
subtask_id_list = [str(uuid4()) for _ in range(total_num_subtasks)] subtask_id_list = [str(uuid4()) for _ in range(total_num_subtasks)]
# Update the InstructorTask with information about the subtasks we've defined. # Update the InstructorTask with information about the subtasks we've defined.
TASK_LOG.info("Task %s: updating InstructorTask %s with subtask info for %s subtasks to process %s items.", TASK_LOG.info(
task_id, entry.id, total_num_subtasks, total_num_items) # pylint: disable=E1101 "Task %s: updating InstructorTask %s with subtask info for %s subtasks to process %s items.",
task_id,
entry.id,
total_num_subtasks,
total_num_items,
) # pylint: disable=E1101
progress = initialize_subtask_info(entry, action_name, total_num_items, subtask_id_list) progress = initialize_subtask_info(entry, action_name, total_num_items, subtask_id_list)
# Construct a generator that will return the recipients to use for each subtask. # Construct a generator that will return the recipients to use for each subtask.
...@@ -317,8 +322,12 @@ def queue_subtasks_for_query(entry, action_name, create_subtask_fcn, item_querys ...@@ -317,8 +322,12 @@ def queue_subtasks_for_query(entry, action_name, create_subtask_fcn, item_querys
) )
# Now create the subtasks, and start them running. # Now create the subtasks, and start them running.
TASK_LOG.info("Task %s: creating %s subtasks to process %s items.", TASK_LOG.info(
task_id, total_num_subtasks, total_num_items) "Task %s: creating %s subtasks to process %s items.",
task_id,
total_num_subtasks,
total_num_items,
)
num_subtasks = 0 num_subtasks = 0
for item_list in item_list_generator: for item_list in item_list_generator:
subtask_id = subtask_id_list[num_subtasks] subtask_id = subtask_id_list[num_subtasks]
......
...@@ -28,8 +28,11 @@ class MyCompleter(object): # Custom completer ...@@ -28,8 +28,11 @@ class MyCompleter(object): # Custom completer
def complete(self, text, state): def complete(self, text, state):
if state == 0: # on first trigger, build possible matches if state == 0: # on first trigger, build possible matches
if text: # cache matches (entries that start with entered text) if text: # cache matches (entries that start with entered text)
self.matches = [s for s in self.options self.matches = [
if s and s.startswith(text)] option
for option in self.options
if option and option.startswith(text)
]
else: # no text entered, all matches possible else: # no text entered, all matches possible
self.matches = self.options[:] self.matches = self.options[:]
...@@ -115,13 +118,14 @@ class Command(BaseCommand): ...@@ -115,13 +118,14 @@ class Command(BaseCommand):
if make_eamap: if make_eamap:
credentials = "/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client CA v1/CN=%s/emailAddress=%s" % (name, email) credentials = "/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client CA v1/CN=%s/emailAddress=%s" % (name, email)
eamap = ExternalAuthMap(external_id=email, eamap = ExternalAuthMap(
external_email=email, external_id=email,
external_domain=mit_domain, external_email=email,
external_name=name, external_domain=mit_domain,
internal_password=password, external_name=name,
external_credentials=json.dumps(credentials), internal_password=password,
) external_credentials=json.dumps(credentials),
)
eamap.user = user eamap.user = user
eamap.dtsignup = datetime.datetime.now(UTC) eamap.dtsignup = datetime.datetime.now(UTC)
eamap.save() eamap.save()
......
...@@ -158,11 +158,15 @@ def combined_notifications(course, user): ...@@ -158,11 +158,15 @@ def combined_notifications(course, user):
try: try:
#Get the notifications from the grading controller #Get the notifications from the grading controller
notifications = controller_qs.check_combined_notifications(course.id, student_id, user_is_staff, notifications = controller_qs.check_combined_notifications(
last_time_viewed) course.id,
student_id,
user_is_staff,
last_time_viewed,
)
if notifications.get('success'): if notifications.get('success'):
if (notifications.get('staff_needs_to_grade') or if (notifications.get('staff_needs_to_grade') or
notifications.get('student_needs_to_peer_grade')): notifications.get('student_needs_to_peer_grade')):
pending_grading = True pending_grading = True
except: except:
#Non catastrophic error, so no real action #Non catastrophic error, so no real action
...@@ -194,8 +198,12 @@ def set_value_in_cache(student_id, course_id, notification_type, value): ...@@ -194,8 +198,12 @@ def set_value_in_cache(student_id, course_id, notification_type, value):
def create_key_name(student_id, course_id, notification_type): def create_key_name(student_id, course_id, notification_type):
key_name = u"{prefix}{type}_{course}_{student}".format(prefix=KEY_PREFIX, type=notification_type, course=course_id, key_name = u"{prefix}{type}_{course}_{student}".format(
student=student_id) prefix=KEY_PREFIX,
type=notification_type,
course=course_id,
student=student_id,
)
return key_name return key_name
......
...@@ -57,15 +57,25 @@ class MockStaffGradingService(object): ...@@ -57,15 +57,25 @@ class MockStaffGradingService(object):
def get_problem_list(self, course_id, grader_id): def get_problem_list(self, course_id, grader_id):
self.cnt += 1 self.cnt += 1
return {'success': True, return {
'problem_list': [ 'success': True,
json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo1', 'problem_list': [
'problem_name': "Problem 1", 'num_graded': 3, 'num_pending': 5, json.dumps({
'min_for_ml': 10}), 'location': 'i4x://MITx/3.091x/problem/open_ended_demo1',
json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo2', 'problem_name': "Problem 1",
'problem_name': "Problem 2", 'num_graded': 1, 'num_pending': 5, 'num_graded': 3,
'min_for_ml': 10}) 'num_pending': 5,
]} 'min_for_ml': 10,
}),
json.dumps({
'location': 'i4x://MITx/3.091x/problem/open_ended_demo2',
'problem_name': "Problem 2",
'num_graded': 1,
'num_pending': 5,
'min_for_ml': 10,
}),
],
}
def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores, def save_grade(self, course_id, grader_id, submission_id, score, feedback, skipped, rubric_scores,
submission_flagged): submission_flagged):
......
...@@ -14,14 +14,18 @@ class TestPaverBokChoy(unittest.TestCase): ...@@ -14,14 +14,18 @@ class TestPaverBokChoy(unittest.TestCase):
def _expected_command(self, expected_text_append): def _expected_command(self, expected_text_append):
if expected_text_append: if expected_text_append:
expected_text_append = "/" + expected_text_append expected_text_append = "/" + expected_text_append
expected_statement = ("DEFAULT_STORE=None SCREENSHOT_DIR='{repo_dir}/test_root/log' " expected_statement = (
"BOK_CHOY_HAR_DIR='{repo_dir}/test_root/log/hars' " "DEFAULT_STORE=None SCREENSHOT_DIR='{repo_dir}/test_root/log' "
"SELENIUM_DRIVER_LOG_DIR='{repo_dir}/test_root/log' " "BOK_CHOY_HAR_DIR='{repo_dir}/test_root/log/hars' "
"nosetests {repo_dir}/common/test/acceptance/tests{exp_text} " "SELENIUM_DRIVER_LOG_DIR='{repo_dir}/test_root/log' "
"--with-xunit " "nosetests {repo_dir}/common/test/acceptance/tests{exp_text} "
"--xunit-file={repo_dir}/reports/bok_choy/xunit.xml " "--with-xunit "
"--verbosity=2 ".format(repo_dir=REPO_DIR, "--xunit-file={repo_dir}/reports/bok_choy/xunit.xml "
exp_text=expected_text_append)) "--verbosity=2 ".format(
repo_dir=REPO_DIR,
exp_text=expected_text_append,
)
)
return expected_statement return expected_statement
def test_default_bokchoy(self): def test_default_bokchoy(self):
......
...@@ -56,8 +56,8 @@ set -e ...@@ -56,8 +56,8 @@ set -e
############################################################################### ###############################################################################
# Violations thresholds for failing the build # Violations thresholds for failing the build
PYLINT_THRESHOLD=4725 PYLINT_THRESHOLD=4600
PEP8_THRESHOLD=150 PEP8_THRESHOLD=15
source $HOME/jenkins_env source $HOME/jenkins_env
......
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