Commit b0c2b73d by Sarina Canelake

Merge pull request #5112 from edx/sarina/quality-clean

Quality cleanup
parents a12c7959 467f2988
...@@ -26,8 +26,9 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -26,8 +26,9 @@ class CapaHtmlRenderTest(unittest.TestCase):
problem = new_loncapa_problem(xml_str) problem = new_loncapa_problem(xml_str)
# Render the HTML # Render the HTML
rendered_html = etree.XML(problem.get_html()) etree.XML(problem.get_html())
# expect that we made it here without blowing up # expect that we made it here without blowing up
self.assertTrue(True)
def test_include_html(self): def test_include_html(self):
# Create a test file to include # Create a test file to include
...@@ -123,17 +124,17 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -123,17 +124,17 @@ class CapaHtmlRenderTest(unittest.TestCase):
# Render the HTML # Render the HTML
rendered_html = etree.XML(problem.get_html()) rendered_html = etree.XML(problem.get_html())
# expect the javascript is still present in the rendered html # expect the javascript is still present in the rendered html
self.assertTrue("<script type=\"text/javascript\">function(){}</script>" in etree.tostring(rendered_html)) self.assertTrue("<script type=\"text/javascript\">function(){}</script>" in etree.tostring(rendered_html))
def test_render_response_xml(self): def test_render_response_xml(self):
# Generate some XML for a string response # Generate some XML for a string response
kwargs = {'question_text': "Test question", kwargs = {
'explanation_text': "Test explanation", 'question_text': "Test question",
'answer': 'Test answer', 'explanation_text': "Test explanation",
'hints': [('test prompt', 'test_hint', 'test hint text')]} 'answer': 'Test answer',
'hints': [('test prompt', 'test_hint', 'test hint text')]
}
xml_str = StringResponseXMLFactory().build_xml(**kwargs) xml_str = StringResponseXMLFactory().build_xml(**kwargs)
# Mock out the template renderer # Mock out the template renderer
...@@ -186,18 +187,21 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -186,18 +187,21 @@ class CapaHtmlRenderTest(unittest.TestCase):
expected_solution_context = {'id': '1_solution_1'} expected_solution_context = {'id': '1_solution_1'}
expected_calls = [mock.call('textline.html', expected_textline_context), expected_calls = [
mock.call('solutionspan.html', expected_solution_context), mock.call('textline.html', expected_textline_context),
mock.call('textline.html', expected_textline_context), mock.call('solutionspan.html', expected_solution_context),
mock.call('solutionspan.html', expected_solution_context)] mock.call('textline.html', expected_textline_context),
mock.call('solutionspan.html', expected_solution_context)
self.assertEqual(the_system.render_template.call_args_list, ]
expected_calls)
self.assertEqual(
the_system.render_template.call_args_list,
expected_calls
)
def test_render_response_with_overall_msg(self): def test_render_response_with_overall_msg(self):
# CustomResponse script that sets an overall_message # CustomResponse script that sets an overall_message
script=textwrap.dedent(""" script = textwrap.dedent("""
def check_func(*args): def check_func(*args):
msg = '<p>Test message 1<br /></p><p>Test message 2</p>' msg = '<p>Test message 1<br /></p><p>Test message 2</p>'
return {'overall_message': msg, return {'overall_message': msg,
...@@ -205,19 +209,18 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -205,19 +209,18 @@ class CapaHtmlRenderTest(unittest.TestCase):
""") """)
# Generate some XML for a CustomResponse # Generate some XML for a CustomResponse
kwargs = {'script':script, 'cfn': 'check_func'} kwargs = {'script': script, 'cfn': 'check_func'}
xml_str = CustomResponseXMLFactory().build_xml(**kwargs) xml_str = CustomResponseXMLFactory().build_xml(**kwargs)
# Create the problem and render the html # Create the problem and render the html
problem = new_loncapa_problem(xml_str) problem = new_loncapa_problem(xml_str)
# Grade the problem # Grade the problem
correctmap = problem.grade_answers({'1_2_1': 'test'}) problem.grade_answers({'1_2_1': 'test'})
# Render the html # Render the html
rendered_html = etree.XML(problem.get_html()) rendered_html = etree.XML(problem.get_html())
# Expect that there is a <div> within the response <div> # Expect that there is a <div> within the response <div>
# with css class response_message # with css class response_message
msg_div_element = rendered_html.find(".//div[@class='response_message']") msg_div_element = rendered_html.find(".//div[@class='response_message']")
...@@ -232,7 +235,6 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -232,7 +235,6 @@ class CapaHtmlRenderTest(unittest.TestCase):
self.assertEqual(msg_p_elements[1].tag, "p") self.assertEqual(msg_p_elements[1].tag, "p")
self.assertEqual(msg_p_elements[1].text, "Test message 2") self.assertEqual(msg_p_elements[1].text, "Test message 2")
def test_substitute_python_vars(self): def test_substitute_python_vars(self):
# Generate some XML with Python variables defined in a script # Generate some XML with Python variables defined in a script
# and used later as attributes # and used later as attributes
......
"""
This test tests that i18n extraction (`paver i18n_extract -v`) works properly.
"""
from datetime import datetime, timedelta from datetime import datetime, timedelta
import os import os
import sys import sys
import string import string # pylint: disable=deprecated-module
import random import random
import re import re
...@@ -65,12 +68,14 @@ class TestGenerate(TestCase): ...@@ -65,12 +68,14 @@ class TestGenerate(TestCase):
generate.main(verbosity=0, strict=False) generate.main(verbosity=0, strict=False)
for locale in CONFIGURATION.translated_locales: for locale in CONFIGURATION.translated_locales:
for filename in ('django', 'djangojs'): for filename in ('django', 'djangojs'):
mofile = filename+'.mo' mofile = filename + '.mo'
path = os.path.join(CONFIGURATION.get_messages_dir(locale), mofile) path = os.path.join(CONFIGURATION.get_messages_dir(locale), mofile)
exists = os.path.exists(path) exists = os.path.exists(path)
self.assertTrue(exists, msg='Missing file in locale %s: %s' % (locale, mofile)) self.assertTrue(exists, msg='Missing file in locale %s: %s' % (locale, mofile))
self.assertTrue(datetime.fromtimestamp(os.path.getmtime(path), UTC) >= self.start_time, self.assertTrue(
msg='File not recently modified: %s' % path) datetime.fromtimestamp(os.path.getmtime(path), UTC) >= self.start_time,
msg='File not recently modified: %s' % path
)
# Segmenting means that the merge headers don't work they way they # Segmenting means that the merge headers don't work they way they
# used to, so don't make this check for now. I'm not sure if we'll # used to, so don't make this check for now. I'm not sure if we'll
# get the merge header back eventually, or delete this code eventually. # get the merge header back eventually, or delete this code eventually.
...@@ -88,12 +93,14 @@ class TestGenerate(TestCase): ...@@ -88,12 +93,14 @@ class TestGenerate(TestCase):
""" """
path = os.path.join(CONFIGURATION.get_messages_dir(locale), 'django.po') path = os.path.join(CONFIGURATION.get_messages_dir(locale), 'django.po')
po = pofile(path) pof = pofile(path)
pattern = re.compile('^#-#-#-#-#', re.M) pattern = re.compile('^#-#-#-#-#', re.M)
match = pattern.findall(po.header) match = pattern.findall(pof.header)
self.assertEqual(len(match), 3, self.assertEqual(
msg="Found %s (should be 3) merge comments in the header for %s" % \ len(match),
(len(match), path)) 3,
msg="Found %s (should be 3) merge comments in the header for %s" % (len(match), path)
)
def random_name(size=6): def random_name(size=6):
......
...@@ -134,8 +134,6 @@ def xml_store_config(data_dir): ...@@ -134,8 +134,6 @@ def xml_store_config(data_dir):
return store return store
class ModuleStoreTestCase(TestCase): class ModuleStoreTestCase(TestCase):
""" """
Subclass for any test case that uses a ModuleStore. Subclass for any test case that uses a ModuleStore.
...@@ -282,35 +280,39 @@ class ModuleStoreTestCase(TestCase): ...@@ -282,35 +280,39 @@ class ModuleStoreTestCase(TestCase):
# Call superclass implementation # Call superclass implementation
super(ModuleStoreTestCase, self)._post_teardown() super(ModuleStoreTestCase, self)._post_teardown()
def create_sample_course(self, org, course, run, block_info_tree=default_block_info_tree, course_fields=None): def create_sample_course(self, org, course, run, block_info_tree=None, course_fields=None):
""" """
create a course in the default modulestore from the collection of BlockInfo create a course in the default modulestore from the collection of BlockInfo
records defining the course tree records defining the course tree
Returns: Returns:
course_loc: the CourseKey for the created course course_loc: the CourseKey for the created course
""" """
if block_info_tree is None:
block_info_tree = default_block_info_tree
with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, None): with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, None):
# with self.store.bulk_operations(self.store.make_course_key(org, course, run)): # with self.store.bulk_operations(self.store.make_course_key(org, course, run)):
course = self.store.create_course(org, course, run, self.user.id, fields=course_fields) course = self.store.create_course(org, course, run, self.user.id, fields=course_fields)
self.course_loc = course.location self.course_loc = course.location # pylint: disable=attribute-defined-outside-init
def create_sub_tree(parent_loc, block_info): def create_sub_tree(parent_loc, block_info):
block = self.store.create_child( """Recursively creates a sub_tree on this parent_loc with this block."""
self.user.id, block = self.store.create_child(
# TODO remove version_agnostic() when we impl the single transaction self.user.id,
parent_loc.version_agnostic(), # TODO remove version_agnostic() when we impl the single transaction
block_info.category, block_id=block_info.block_id, parent_loc.version_agnostic(),
fields=block_info.fields, block_info.category, block_id=block_info.block_id,
) fields=block_info.fields,
for tree in block_info.sub_tree: )
create_sub_tree(block.location, tree) for tree in block_info.sub_tree:
setattr(self, block_info.block_id, block.location.version_agnostic()) create_sub_tree(block.location, tree)
setattr(self, block_info.block_id, block.location.version_agnostic())
for tree in block_info_tree:
create_sub_tree(self.course_loc, tree) for tree in block_info_tree:
create_sub_tree(self.course_loc, tree)
# remove version_agnostic when bulk write works
self.store.publish(self.course_loc.version_agnostic(), self.user.id) # remove version_agnostic when bulk write works
self.store.publish(self.course_loc.version_agnostic(), self.user.id)
return self.course_loc.course_key.version_agnostic() return self.course_loc.course_key.version_agnostic()
def create_toy_course(self, org='edX', course='toy', run='2012_Fall'): def create_toy_course(self, org='edX', course='toy', run='2012_Fall'):
...@@ -318,43 +320,43 @@ class ModuleStoreTestCase(TestCase): ...@@ -318,43 +320,43 @@ class ModuleStoreTestCase(TestCase):
Create an equivalent to the toy xml course Create an equivalent to the toy xml course
""" """
# with self.store.bulk_operations(self.store.make_course_key(org, course, run)): # with self.store.bulk_operations(self.store.make_course_key(org, course, run)):
self.toy_loc = self.create_sample_course( self.toy_loc = self.create_sample_course( # pylint: disable=attribute-defined-outside-init
org, course, run, TOY_BLOCK_INFO_TREE, org, course, run, TOY_BLOCK_INFO_TREE,
{ {
"textbooks" : [["Textbook", "https://s3.amazonaws.com/edx-textbooks/guttag_computation_v3/"]], "textbooks": [["Textbook", "https://s3.amazonaws.com/edx-textbooks/guttag_computation_v3/"]],
"wiki_slug" : "toy", "wiki_slug": "toy",
"display_name" : "Toy Course", "display_name": "Toy Course",
"graded" : True, "graded": True,
"tabs" : [ "tabs": [
CoursewareTab(), CoursewareTab(),
CourseInfoTab(), CourseInfoTab(),
StaticTab(name="Syllabus", url_slug="syllabus"), StaticTab(name="Syllabus", url_slug="syllabus"),
StaticTab(name="Resources", url_slug="resources"), StaticTab(name="Resources", url_slug="resources"),
DiscussionTab(), DiscussionTab(),
WikiTab(), WikiTab(),
ProgressTab(), ProgressTab(),
], ],
"discussion_topics" : {"General" : {"id" : "i4x-edX-toy-course-2012_Fall"}}, "discussion_topics": {"General": {"id": "i4x-edX-toy-course-2012_Fall"}},
"graceperiod" : datetime.timedelta(days=2, seconds=21599), "graceperiod": datetime.timedelta(days=2, seconds=21599),
"start" : datetime.datetime(2015, 07, 17, 12, tzinfo=pytz.utc), "start": datetime.datetime(2015, 07, 17, 12, tzinfo=pytz.utc),
"xml_attributes" : {"filename" : ["course/2012_Fall.xml", "course/2012_Fall.xml"]}, "xml_attributes": {"filename": ["course/2012_Fall.xml", "course/2012_Fall.xml"]},
"pdf_textbooks" : [ "pdf_textbooks": [
{ {
"tab_title" : "Sample Multi Chapter Textbook", "tab_title": "Sample Multi Chapter Textbook",
"id" : "MyTextbook", "id": "MyTextbook",
"chapters" : [ "chapters": [
{"url" : "/static/Chapter1.pdf", "title" : "Chapter 1"}, {"url": "/static/Chapter1.pdf", "title": "Chapter 1"},
{"url" : "/static/Chapter2.pdf", "title" : "Chapter 2"} {"url": "/static/Chapter2.pdf", "title": "Chapter 2"}
] ]
} }
], ],
"course_image" : "just_a_test.jpg", "course_image": "just_a_test.jpg",
} }
) )
with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.toy_loc): with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.toy_loc):
self.store.create_item( self.store.create_item(
self.user.id, self.toy_loc, "about", block_id="short_description", self.user.id, self.toy_loc, "about", block_id="short_description",
fields={"data" : "A course about toys."} fields={"data": "A course about toys."}
) )
self.store.create_item( self.store.create_item(
self.user.id, self.toy_loc, "about", block_id="effort", self.user.id, self.toy_loc, "about", block_id="effort",
......
...@@ -7,8 +7,8 @@ The data type and use of it for declaratively creating test courses. ...@@ -7,8 +7,8 @@ The data type and use of it for declaratively creating test courses.
# fields is a dictionary of keys and values. sub_tree is a collection of BlockInfo # fields is a dictionary of keys and values. sub_tree is a collection of BlockInfo
from collections import namedtuple from collections import namedtuple
import datetime import datetime
BlockInfo = namedtuple('BlockInfo', 'block_id, category, fields, sub_tree') BlockInfo = namedtuple('BlockInfo', 'block_id, category, fields, sub_tree') # pylint: disable=invalid-name
default_block_info_tree = [ default_block_info_tree = [ # pylint: disable=invalid-name
BlockInfo( BlockInfo(
'chapter_x', 'chapter', {}, [ 'chapter_x', 'chapter', {}, [
BlockInfo( BlockInfo(
...@@ -44,7 +44,7 @@ default_block_info_tree = [ ...@@ -44,7 +44,7 @@ default_block_info_tree = [
# equivalent to toy course in xml # equivalent to toy course in xml
TOY_BLOCK_INFO_TREE = [ TOY_BLOCK_INFO_TREE = [
BlockInfo( BlockInfo(
'Overview', "chapter", {"display_name" : "Overview"}, [ 'Overview', "chapter", {"display_name": "Overview"}, [
BlockInfo( BlockInfo(
"Toy_Videos", "videosequence", { "Toy_Videos", "videosequence", {
"xml_attributes": {"filename": ["", None]}, "display_name": "Toy Videos", "format": "Lecture Sequence" "xml_attributes": {"filename": ["", None]}, "display_name": "Toy Videos", "format": "Lecture Sequence"
...@@ -52,24 +52,24 @@ TOY_BLOCK_INFO_TREE = [ ...@@ -52,24 +52,24 @@ TOY_BLOCK_INFO_TREE = [
BlockInfo( BlockInfo(
"secret:toylab", "html", { "secret:toylab", "html", {
"data": "<b>Lab 2A: Superposition Experiment</b>\n\n<<<<<<< Updated upstream\n<p>Isn't the toy course great?</p>\n\n<p>Let's add some markup that uses non-ascii characters.\nFor example, we should be able to write words like encyclop&aelig;dia, or foreign words like fran&ccedil;ais.\nLooking beyond latin-1, we should handle math symbols: &pi;r&sup2 &le; &#8734.\nAnd it shouldn't matter if we use entities or numeric codes &mdash; &Omega; &ne; &pi; &equiv; &#937; &#8800; &#960;.\n</p>\n=======\n<p>Isn't the toy course great? — &le;</p>\n>>>>>>> Stashed changes\n", "data": "<b>Lab 2A: Superposition Experiment</b>\n\n<<<<<<< Updated upstream\n<p>Isn't the toy course great?</p>\n\n<p>Let's add some markup that uses non-ascii characters.\nFor example, we should be able to write words like encyclop&aelig;dia, or foreign words like fran&ccedil;ais.\nLooking beyond latin-1, we should handle math symbols: &pi;r&sup2 &le; &#8734.\nAnd it shouldn't matter if we use entities or numeric codes &mdash; &Omega; &ne; &pi; &equiv; &#937; &#8800; &#960;.\n</p>\n=======\n<p>Isn't the toy course great? — &le;</p>\n>>>>>>> Stashed changes\n",
"xml_attributes": { "filename" : [ "html/secret/toylab.xml", "html/secret/toylab.xml" ] }, "xml_attributes": {"filename": ["html/secret/toylab.xml", "html/secret/toylab.xml"]},
"display_name" : "Toy lab" "display_name": "Toy lab"
}, [] }, []
), ),
BlockInfo( BlockInfo(
"toyjumpto", "html", { "toyjumpto", "html", {
"data" : "<a href=\"/jump_to_id/vertical_test\">This is a link to another page and some Chinese 四節比分和七年前</a> <p>Some more Chinese 四節比分和七年前</p>\n", "data": "<a href=\"/jump_to_id/vertical_test\">This is a link to another page and some Chinese 四節比分和七年前</a> <p>Some more Chinese 四節比分和七年前</p>\n",
"xml_attributes": { "filename" : [ "html/toyjumpto.xml", "html/toyjumpto.xml" ] } "xml_attributes": {"filename": ["html/toyjumpto.xml", "html/toyjumpto.xml"]}
}, []), }, []),
BlockInfo( BlockInfo(
"toyhtml", "html", { "toyhtml", "html", {
"data" : "<a href='/static/handouts/sample_handout.txt'>Sample</a>", "data": "<a href='/static/handouts/sample_handout.txt'>Sample</a>",
"xml_attributes" : { "filename" : [ "html/toyhtml.xml", "html/toyhtml.xml" ] } "xml_attributes": {"filename": ["html/toyhtml.xml", "html/toyhtml.xml"]}
}, []), }, []),
BlockInfo( BlockInfo(
"nonportable", "html", { "nonportable", "html", {
"data": "<a href=\"/static/foo.jpg\">link</a>\n", "data": "<a href=\"/static/foo.jpg\">link</a>\n",
"xml_attributes" : { "filename" : [ "html/nonportable.xml", "html/nonportable.xml" ] } "xml_attributes": {"filename": ["html/nonportable.xml", "html/nonportable.xml"]}
}, []), }, []),
BlockInfo( BlockInfo(
"nonportable_link", "html", { "nonportable_link", "html", {
...@@ -79,7 +79,7 @@ TOY_BLOCK_INFO_TREE = [ ...@@ -79,7 +79,7 @@ TOY_BLOCK_INFO_TREE = [
BlockInfo( BlockInfo(
"badlink", "html", { "badlink", "html", {
"data": "<img src=\"/static//file.jpg\" />\n", "data": "<img src=\"/static//file.jpg\" />\n",
"xml_attributes" : { "filename" : [ "html/badlink.xml", "html/badlink.xml" ] } "xml_attributes": {"filename": ["html/badlink.xml", "html/badlink.xml"]}
}, []), }, []),
BlockInfo( BlockInfo(
"with_styling", "html", { "with_styling", "html", {
...@@ -89,11 +89,11 @@ TOY_BLOCK_INFO_TREE = [ ...@@ -89,11 +89,11 @@ TOY_BLOCK_INFO_TREE = [
BlockInfo( BlockInfo(
"just_img", "html", { "just_img", "html", {
"data": "<img src=\"/static/foo_bar.jpg\" />", "data": "<img src=\"/static/foo_bar.jpg\" />",
"xml_attributes": {"filename": [ "html/just_img.xml", "html/just_img.xml" ] } "xml_attributes": {"filename": ["html/just_img.xml", "html/just_img.xml"]}
}, []), }, []),
BlockInfo( BlockInfo(
"Video_Resources", "video", { "Video_Resources", "video", {
"youtube_id_1_0" : "1bK-WdDi6Qw", "display_name" : "Video Resources" "youtube_id_1_0": "1bK-WdDi6Qw", "display_name": "Video Resources"
}, []), }, []),
]), ]),
BlockInfo( BlockInfo(
...@@ -109,7 +109,7 @@ TOY_BLOCK_INFO_TREE = [ ...@@ -109,7 +109,7 @@ TOY_BLOCK_INFO_TREE = [
), ),
BlockInfo( BlockInfo(
"secret:magic", "chapter", { "secret:magic", "chapter", {
"xml_attributes": {"filename": [ "chapter/secret/magic.xml", "chapter/secret/magic.xml"]} "xml_attributes": {"filename": ["chapter/secret/magic.xml", "chapter/secret/magic.xml"]}
}, [ }, [
BlockInfo( BlockInfo(
"toyvideo", "video", {"youtube_id_1_0": "OEoXaMPEzfMA", "display_name": "toyvideo"}, [] "toyvideo", "video", {"youtube_id_1_0": "OEoXaMPEzfMA", "display_name": "toyvideo"}, []
...@@ -117,18 +117,18 @@ TOY_BLOCK_INFO_TREE = [ ...@@ -117,18 +117,18 @@ TOY_BLOCK_INFO_TREE = [
] ]
), ),
BlockInfo( BlockInfo(
"poll_test", "chapter", {}, [ "poll_test", "chapter", {}, [
BlockInfo( BlockInfo(
"T1_changemind_poll_foo", "poll_question", { "T1_changemind_poll_foo", "poll_question", {
"question": "<p>Have you changed your mind? ’</p>", "question": "<p>Have you changed your mind? ’</p>",
"answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}], "answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}],
"xml_attributes": {"reset": "false", "filename": ["", None]}, "xml_attributes": {"reset": "false", "filename": ["", None]},
"display_name": "Change your answer" "display_name": "Change your answer"
}, []) ] }, [])]
), ),
BlockInfo( BlockInfo(
"vertical_container", "chapter", { "vertical_container", "chapter", {
"xml_attributes": {"filename": ["chapter/vertical_container.xml", "chapter/vertical_container.xml"]} "xml_attributes": {"filename": ["chapter/vertical_container.xml", "chapter/vertical_container.xml"]}
}, [ }, [
BlockInfo("vertical_sequential", "sequential", {}, [ BlockInfo("vertical_sequential", "sequential", {}, [
BlockInfo("vertical_test", "vertical", { BlockInfo("vertical_test", "vertical", {
...@@ -163,7 +163,7 @@ TOY_BLOCK_INFO_TREE = [ ...@@ -163,7 +163,7 @@ TOY_BLOCK_INFO_TREE = [
"T1_changemind_poll_foo_2", "poll_question", { "T1_changemind_poll_foo_2", "poll_question", {
"question": "<p>Have you changed your mind?</p>", "question": "<p>Have you changed your mind?</p>",
"answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}], "answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}],
"xml_attributes": {"reset": "false", "filename": [ "", None]}, "xml_attributes": {"reset": "false", "filename": ["", None]},
"display_name": "Change your answer" "display_name": "Change your answer"
}, []), }, []),
]), ]),
...@@ -174,8 +174,8 @@ TOY_BLOCK_INFO_TREE = [ ...@@ -174,8 +174,8 @@ TOY_BLOCK_INFO_TREE = [
] ]
), ),
BlockInfo( BlockInfo(
"handout_container", "chapter", { "handout_container", "chapter", {
"xml_attributes" : {"filename" : ["chapter/handout_container.xml", "chapter/handout_container.xml"]} "xml_attributes": {"filename": ["chapter/handout_container.xml", "chapter/handout_container.xml"]}
}, [ }, [
BlockInfo( BlockInfo(
"html_7e5578f25f79", "html", { "html_7e5578f25f79", "html", {
......
...@@ -33,7 +33,7 @@ class PeerGradingFields(object): ...@@ -33,7 +33,7 @@ class PeerGradingFields(object):
use_for_single_location = Boolean( use_for_single_location = Boolean(
display_name=_("Show Single Problem"), display_name=_("Show Single Problem"),
help=_('When True, only the single problem specified by "Link to Problem Location" is shown. ' help=_('When True, only the single problem specified by "Link to Problem Location" is shown. '
'When False, a panel is displayed with all problems available for peer grading.'), 'When False, a panel is displayed with all problems available for peer grading.'),
default=False, default=False,
scope=Scope.settings scope=Scope.settings
) )
...@@ -54,9 +54,9 @@ class PeerGradingFields(object): ...@@ -54,9 +54,9 @@ class PeerGradingFields(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=_("Date that this problem is due by for a particular student. This "
"can be set by an instructor, and will override the global due " "can be set by an instructor, and will override the global due "
"date if it is set to a date that is later than the global due " "date if it is set to a date that is later than the global due "
"date."), "date."),
default=None, default=None,
scope=Scope.user_state, scope=Scope.user_state,
) )
...@@ -86,6 +86,7 @@ class PeerGradingFields(object): ...@@ -86,6 +86,7 @@ class PeerGradingFields(object):
scope=Scope.content scope=Scope.content
) )
class InvalidLinkLocation(Exception): class InvalidLinkLocation(Exception):
""" """
Exception for the case in which a peer grading module tries to link to an invalid location. Exception for the case in which a peer grading module tries to link to an invalid location.
...@@ -150,7 +151,8 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -150,7 +151,8 @@ class PeerGradingModule(PeerGradingFields, XModule):
try: try:
self.student_data_for_location = json.loads(self.student_data_for_location) self.student_data_for_location = json.loads(self.student_data_for_location)
except Exception: except Exception: # pylint: disable=broad-except
# OK with this broad exception because we just want to continue on any error
pass pass
@property @property
...@@ -218,9 +220,9 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -218,9 +220,9 @@ class PeerGradingModule(PeerGradingFields, XModule):
# This is a dev_facing_error # This is a dev_facing_error
return json.dumps({'error': 'Error handling action. Please try again.', 'success': False}) return json.dumps({'error': 'Error handling action. Please try again.', 'success': False})
d = handlers[dispatch](data) data_dict = handlers[dispatch](data)
return json.dumps(d, cls=ComplexEncoder) return json.dumps(data_dict, cls=ComplexEncoder)
def query_data_for_location(self, location): def query_data_for_location(self, location):
student_id = self.system.anonymous_student_id student_id = self.system.anonymous_student_id
...@@ -229,13 +231,12 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -229,13 +231,12 @@ class PeerGradingModule(PeerGradingFields, XModule):
try: try:
response = self.peer_gs.get_data_for_location(location, student_id) response = self.peer_gs.get_data_for_location(location, student_id)
count_graded = response['count_graded'] _count_graded = response['count_graded']
count_required = response['count_required'] _count_required = response['count_required']
success = True success = True
except GradingServiceError: except GradingServiceError:
# This is a dev_facing_error # This is a dev_facing_error
log.exception("Error getting location data from controller for location {0}, student {1}" log.exception("Error getting location data from controller for location %s, student %s", location, student_id)
.format(location, student_id))
return success, response return success, response
...@@ -322,8 +323,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -322,8 +323,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
return response return response
except GradingServiceError: except GradingServiceError:
# This is a dev_facing_error # This is a dev_facing_error
log.exception("Error getting next submission. server url: {0} location: {1}, grader_id: {2}" log.exception("Error getting next submission. server url: %s location: %s, grader_id: %s", self.peer_gs.url, location, grader_id)
.format(self.peer_gs.url, location, grader_id))
# This is a student_facing_error # This is a student_facing_error
return {'success': False, return {'success': False,
'error': EXTERNAL_GRADER_NO_CONTACT_ERROR} 'error': EXTERNAL_GRADER_NO_CONTACT_ERROR}
...@@ -355,7 +355,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -355,7 +355,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
if not success: if not success:
return self._err_response(message) return self._err_response(message)
data_dict = {k:data.get(k) for k in required} data_dict = {k: data.get(k) for k in required}
if 'rubric_scores[]' in required: if 'rubric_scores[]' in required:
data_dict['rubric_scores'] = data.getall('rubric_scores[]') data_dict['rubric_scores'] = data.getall('rubric_scores[]')
data_dict['grader_id'] = self.system.anonymous_student_id data_dict['grader_id'] = self.system.anonymous_student_id
...@@ -365,15 +365,14 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -365,15 +365,14 @@ class PeerGradingModule(PeerGradingFields, XModule):
success, location_data = self.query_data_for_location(data_dict['location']) success, location_data = self.query_data_for_location(data_dict['location'])
#Don't check for success above because the response = statement will raise the same Exception as the one #Don't check for success above because the response = statement will raise the same Exception as the one
#that will cause success to be false. #that will cause success to be false.
response.update({'required_done' : False}) response.update({'required_done': False})
if 'count_graded' in location_data and 'count_required' in location_data and int(location_data['count_graded'])>=int(location_data['count_required']): if 'count_graded' in location_data and 'count_required' in location_data and int(location_data['count_graded']) >= int(location_data['count_required']):
response['required_done'] = True response['required_done'] = True
return response return response
except GradingServiceError: except GradingServiceError:
# This is a dev_facing_error # This is a dev_facing_error
log.exception("""Error saving grade to open ended grading service. server url: {0}""" log.exception("Error saving grade to open ended grading service. server url: %s", self.peer_gs.url)
.format(self.peer_gs.url)
)
# This is a student_facing_error # This is a student_facing_error
return { return {
'success': False, 'success': False,
...@@ -411,8 +410,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -411,8 +410,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
return response return response
except GradingServiceError: except GradingServiceError:
# This is a dev_facing_error # This is a dev_facing_error
log.exception("Error from open ended grading service. server url: {0}, grader_id: {0}, location: {1}" log.exception("Error from open ended grading service. server url: %s, grader_id: %s, location: %s", self.peer_gs.url, grader_id, location)
.format(self.peer_gs.url, grader_id, location))
# This is a student_facing_error # This is a student_facing_error
return { return {
'success': False, 'success': False,
...@@ -456,8 +454,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -456,8 +454,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
return response return response
except GradingServiceError: except GradingServiceError:
# This is a dev_facing_error # This is a dev_facing_error
log.exception("Error from open ended grading service. server url: {0}, location: {0}" log.exception("Error from open ended grading service. server url: %s, location: %s", self.peer_gs.url, location)
.format(self.peer_gs.url, location))
# This is a student_facing_error # This is a student_facing_error
return {'success': False, return {'success': False,
'error': EXTERNAL_GRADER_NO_CONTACT_ERROR} 'error': EXTERNAL_GRADER_NO_CONTACT_ERROR}
...@@ -492,7 +489,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -492,7 +489,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
if not success: if not success:
return self._err_response(message) return self._err_response(message)
data_dict = {k:data.get(k) for k in required} data_dict = {k: data.get(k) for k in required}
data_dict['rubric_scores'] = data.getall('rubric_scores[]') data_dict['rubric_scores'] = data.getall('rubric_scores[]')
data_dict['student_id'] = self.system.anonymous_student_id data_dict['student_id'] = self.system.anonymous_student_id
data_dict['calibration_essay_id'] = data_dict['submission_id'] data_dict['calibration_essay_id'] = data_dict['submission_id']
...@@ -619,7 +616,7 @@ class PeerGradingModule(PeerGradingFields, XModule): ...@@ -619,7 +616,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
elif data.get('location') is not None: elif data.get('location') is not None:
problem_location = self.course_id.make_usage_key_from_deprecated_string(data.get('location')) problem_location = self.course_id.make_usage_key_from_deprecated_string(data.get('location'))
module = self._find_corresponding_module_for_location(problem_location) module = self._find_corresponding_module_for_location(problem_location) # pylint: disable-unused-variable
ajax_url = self.ajax_url ajax_url = self.ajax_url
html = self.system.render_template('peer_grading/peer_grading_problem.html', { html = self.system.render_template('peer_grading/peer_grading_problem.html', {
......
"""View functions for the LMS Student dashboard"""
from django.http import Http404 from django.http import Http404
from edxmako.shortcuts import render_to_response from edxmako.shortcuts import render_to_response
from django.db import connection from django.db import connection
...@@ -15,15 +16,18 @@ def dictfetchall(cursor): ...@@ -15,15 +16,18 @@ def dictfetchall(cursor):
# ensure response from db is a list, not a tuple (which is returned # ensure response from db is a list, not a tuple (which is returned
# by MySQL backed django instances) # by MySQL backed django instances)
rows_from_cursor=cursor.fetchall() rows_from_cursor = cursor.fetchall()
table = table + [list(row) for row in rows_from_cursor] table = table + [list(row) for row in rows_from_cursor]
return table return table
def SQL_query_to_list(cursor, query_string):
def SQL_query_to_list(cursor, query_string): # pylint: disable=invalid-name
"""Returns the raw result of the query"""
cursor.execute(query_string) cursor.execute(query_string)
raw_result=dictfetchall(cursor) raw_result = dictfetchall(cursor)
return raw_result return raw_result
def dashboard(request): def dashboard(request):
""" """
Slightly less hackish hack to show staff enrollment numbers and other Slightly less hackish hack to show staff enrollment numbers and other
...@@ -40,11 +44,11 @@ def dashboard(request): ...@@ -40,11 +44,11 @@ def dashboard(request):
# two types of results: scalars and tables. Scalars should be represented # two types of results: scalars and tables. Scalars should be represented
# as "Visible Title": Value and tables should be lists of lists where each # as "Visible Title": Value and tables should be lists of lists where each
# inner list represents a single row of the table # inner list represents a single row of the table
results = {"scalars":{},"tables":{}} results = {"scalars": {}, "tables": {}}
# count how many users we have # count how many users we have
results["scalars"]["Unique Usernames"]=User.objects.filter().count() results["scalars"]["Unique Usernames"] = User.objects.filter().count()
results["scalars"]["Activated Usernames"]=User.objects.filter(is_active=1).count() results["scalars"]["Activated Usernames"] = User.objects.filter(is_active=1).count()
# count how many enrollments we have # count how many enrollments we have
results["scalars"]["Total Enrollments Across All Courses"] = CourseEnrollment.objects.filter(is_active=1).count() results["scalars"]["Total Enrollments Across All Courses"] = CourseEnrollment.objects.filter(is_active=1).count()
...@@ -78,6 +82,6 @@ def dashboard(request): ...@@ -78,6 +82,6 @@ def dashboard(request):
cursor.execute(table_queries[query]) cursor.execute(table_queries[query])
results["tables"][query] = SQL_query_to_list(cursor, table_queries[query]) results["tables"][query] = SQL_query_to_list(cursor, table_queries[query])
context={"results":results} context = {"results": results}
return render_to_response("admin_dashboard.html",context) return render_to_response("admin_dashboard.html", context)
...@@ -193,7 +193,7 @@ class SingleThreadTestCase(ModuleStoreTestCase): ...@@ -193,7 +193,7 @@ class SingleThreadTestCase(ModuleStoreTestCase):
) )
mock_request.assert_called_with( mock_request.assert_called_with(
"get", "get",
StringEndsWithMatcher(thread_id), # url StringEndsWithMatcher(thread_id), # url
data=None, data=None,
params=PartialDictMatcher({"mark_as_read": True, "user_id": 1, "recursive": True}), params=PartialDictMatcher({"mark_as_read": True, "user_id": 1, "recursive": True}),
headers=ANY, headers=ANY,
...@@ -227,7 +227,7 @@ class SingleThreadTestCase(ModuleStoreTestCase): ...@@ -227,7 +227,7 @@ class SingleThreadTestCase(ModuleStoreTestCase):
) )
mock_request.assert_called_with( mock_request.assert_called_with(
"get", "get",
StringEndsWithMatcher(thread_id), # url StringEndsWithMatcher(thread_id), # url
data=None, data=None,
params=PartialDictMatcher({ params=PartialDictMatcher({
"mark_as_read": True, "mark_as_read": True,
...@@ -365,7 +365,7 @@ class UserProfileTestCase(ModuleStoreTestCase): ...@@ -365,7 +365,7 @@ class UserProfileTestCase(ModuleStoreTestCase):
"course_id": self.course.id.to_deprecated_string(), "course_id": self.course.id.to_deprecated_string(),
"page": params.get("page", 1), "page": params.get("page", 1),
"per_page": views.THREADS_PER_PAGE "per_page": views.THREADS_PER_PAGE
}), }),
headers=ANY, headers=ANY,
timeout=ANY timeout=ANY
) )
...@@ -393,7 +393,7 @@ class UserProfileTestCase(ModuleStoreTestCase): ...@@ -393,7 +393,7 @@ class UserProfileTestCase(ModuleStoreTestCase):
self.assertEqual( self.assertEqual(
sorted(response_data.keys()), sorted(response_data.keys()),
["annotated_content_info", "discussion_data", "num_pages", "page"] ["annotated_content_info", "discussion_data", "num_pages", "page"]
) )
self.assertEqual(len(response_data['discussion_data']), 1) self.assertEqual(len(response_data['discussion_data']), 1)
self.assertEqual(response_data["page"], 1) self.assertEqual(response_data["page"], 1)
self.assertEqual(response_data["num_pages"], 1) self.assertEqual(response_data["num_pages"], 1)
...@@ -444,6 +444,7 @@ class UserProfileTestCase(ModuleStoreTestCase): ...@@ -444,6 +444,7 @@ class UserProfileTestCase(ModuleStoreTestCase):
) )
self.assertEqual(response.status_code, 405) self.assertEqual(response.status_code, 405)
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE) @override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
@patch('requests.request') @patch('requests.request')
class CommentsServiceRequestHeadersTestCase(UrlResetMixin, ModuleStoreTestCase): class CommentsServiceRequestHeadersTestCase(UrlResetMixin, ModuleStoreTestCase):
...@@ -463,8 +464,8 @@ class CommentsServiceRequestHeadersTestCase(UrlResetMixin, ModuleStoreTestCase): ...@@ -463,8 +464,8 @@ class CommentsServiceRequestHeadersTestCase(UrlResetMixin, ModuleStoreTestCase):
def assert_all_calls_have_header(self, mock_request, key, value): def assert_all_calls_have_header(self, mock_request, key, value):
expected = call( expected = call(
ANY, # method ANY, # method
ANY, # url ANY, # url
data=ANY, data=ANY,
params=ANY, params=ANY,
headers=PartialDictMatcher({key: value}), headers=PartialDictMatcher({key: value}),
...@@ -537,7 +538,7 @@ class ForumFormDiscussionUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin): ...@@ -537,7 +538,7 @@ class ForumFormDiscussionUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin):
mock_request.side_effect = make_mock_request_impl(text) mock_request.side_effect = make_mock_request_impl(text)
request = RequestFactory().get("dummy_url") request = RequestFactory().get("dummy_url")
request.user = self.student request.user = self.student
request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True
response = views.forum_form_discussion(request, self.course.id.to_deprecated_string()) response = views.forum_form_discussion(request, self.course.id.to_deprecated_string())
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -559,7 +560,7 @@ class SingleThreadUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin): ...@@ -559,7 +560,7 @@ class SingleThreadUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin):
mock_request.side_effect = make_mock_request_impl(text, thread_id) mock_request.side_effect = make_mock_request_impl(text, thread_id)
request = RequestFactory().get("dummy_url") request = RequestFactory().get("dummy_url")
request.user = self.student request.user = self.student
request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True
response = views.single_thread(request, self.course.id.to_deprecated_string(), "dummy_discussion_id", thread_id) response = views.single_thread(request, self.course.id.to_deprecated_string(), "dummy_discussion_id", thread_id)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -580,7 +581,7 @@ class UserProfileUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin): ...@@ -580,7 +581,7 @@ class UserProfileUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin):
mock_request.side_effect = make_mock_request_impl(text) mock_request.side_effect = make_mock_request_impl(text)
request = RequestFactory().get("dummy_url") request = RequestFactory().get("dummy_url")
request.user = self.student request.user = self.student
request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True
response = views.user_profile(request, self.course.id.to_deprecated_string(), str(self.student.id)) response = views.user_profile(request, self.course.id.to_deprecated_string(), str(self.student.id))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -601,7 +602,7 @@ class FollowedThreadsUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin): ...@@ -601,7 +602,7 @@ class FollowedThreadsUnicodeTestCase(ModuleStoreTestCase, UnicodeTestMixin):
mock_request.side_effect = make_mock_request_impl(text) mock_request.side_effect = make_mock_request_impl(text)
request = RequestFactory().get("dummy_url") request = RequestFactory().get("dummy_url")
request.user = self.student request.user = self.student
request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True request.META["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" # so request.is_ajax() == True
response = views.followed_threads(request, self.course.id.to_deprecated_string(), str(self.student.id)) response = views.followed_threads(request, self.course.id.to_deprecated_string(), str(self.student.id))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
......
...@@ -3,7 +3,7 @@ Instructor Views ...@@ -3,7 +3,7 @@ Instructor Views
""" """
## NOTE: This is the code for the legacy instructor dashboard ## NOTE: This is the code for the legacy instructor dashboard
## We are no longer supporting this file or accepting changes into it. ## We are no longer supporting this file or accepting changes into it.
# pylint: skip-file
from contextlib import contextmanager from contextlib import contextmanager
import csv import csv
import json import json
...@@ -1427,6 +1427,7 @@ def get_student_grade_summary_data(request, course, get_grades=True, get_raw_sco ...@@ -1427,6 +1427,7 @@ def get_student_grade_summary_data(request, course, get_grades=True, get_raw_sco
# Gradebook has moved to instructor.api.spoc_gradebook # # Gradebook has moved to instructor.api.spoc_gradebook #
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
def grade_summary(request, course_key): def grade_summary(request, course_key):
"""Display the grade summary for a course.""" """Display the grade summary for a course."""
......
...@@ -24,7 +24,7 @@ from xmodule.modulestore.django import modulestore ...@@ -24,7 +24,7 @@ from xmodule.modulestore.django import modulestore
from course_modes.models import CourseMode from course_modes.models import CourseMode
from edxmako.shortcuts import render_to_string from edxmako.shortcuts import render_to_string
from student.models import CourseEnrollment, unenroll_done from student.models import CourseEnrollment, UNENROLL_DONE
from util.query import use_read_replica_if_available from util.query import use_read_replica_if_available
from xmodule_django.models import CourseKeyField from xmodule_django.models import CourseKeyField
...@@ -639,7 +639,7 @@ class CertificateItem(OrderItem): ...@@ -639,7 +639,7 @@ class CertificateItem(OrderItem):
course_enrollment = models.ForeignKey(CourseEnrollment) course_enrollment = models.ForeignKey(CourseEnrollment)
mode = models.SlugField() mode = models.SlugField()
@receiver(unenroll_done) @receiver(UNENROLL_DONE)
def refund_cert_callback(sender, course_enrollment=None, **kwargs): def refund_cert_callback(sender, course_enrollment=None, **kwargs):
""" """
When a CourseEnrollment object calls its unenroll method, this function checks to see if that unenrollment When a CourseEnrollment object calls its unenroll method, this function checks to see if that unenrollment
......
# Import other classes here so they can be imported from here. """Import other classes here so they can be imported from here."""
# pylint: disable=W0611 # pylint: disable=unused-import
from .comment import Comment from .comment import Comment
from .thread import Thread from .thread import Thread
from .user import User from .user import User
......
"""Provides base Commentable model class"""
import models import models
import settings import settings
class Commentable(models.Model): class Commentable(models.Model):
base_url = "{prefix}/commentables".format(prefix=settings.PREFIX) base_url = "{prefix}/commentables".format(prefix=settings.PREFIX)
......
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