From 79e1dae0e74648a7fdb3da4defd79e369cca1ae3 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Tue, 14 Jan 2014 14:18:35 -0500 Subject: [PATCH 001/134] Fix pylint violations from #2129 --- common/djangoapps/student/roles.py | 3 --- common/lib/xmodule/xmodule/conditional_module.py | 2 +- common/lib/xmodule/xmodule/error_module.py | 2 +- common/lib/xmodule/xmodule/modulestore/__init__.py | 4 +++- .../xmodule/xmodule/modulestore/tests/test_location.py | 5 +++++ common/lib/xmodule/xmodule/modulestore/xml.py | 9 ++++++--- common/lib/xmodule/xmodule/tests/test_import.py | 4 ++++ common/lib/xmodule/xmodule/tests/test_xml_module.py | 2 -- common/lib/xmodule/xmodule/x_module.py | 2 +- common/lib/xmodule/xmodule/xml_module.py | 9 ++++----- pylintrc | 8 +++++++- 11 files changed, 32 insertions(+), 18 deletions(-) diff --git a/common/djangoapps/student/roles.py b/common/djangoapps/student/roles.py index 9da258e19b..b01180198e 100644 --- a/common/djangoapps/student/roles.py +++ b/common/djangoapps/student/roles.py @@ -157,7 +157,6 @@ class CourseRole(GroupBasedRole): # direct copy from auth.authz.get_all_course_role_groupnames will refactor to one impl asap groupnames = [] - # pylint: disable=no-member if isinstance(self.location, Location): try: groupnames.append('{0}_{1}'.format(role, self.location.course_id)) @@ -193,8 +192,6 @@ class OrgRole(GroupBasedRole): A named role in a particular org """ def __init__(self, role, location): - # pylint: disable=no-member - location = Location(location) super(OrgRole, self).__init__(['{}_{}'.format(role, location.org)]) diff --git a/common/lib/xmodule/xmodule/conditional_module.py b/common/lib/xmodule/xmodule/conditional_module.py index 9a2332d556..e296af5aa5 100644 --- a/common/lib/xmodule/xmodule/conditional_module.py +++ b/common/lib/xmodule/xmodule/conditional_module.py @@ -224,7 +224,7 @@ class ConditionalDescriptor(ConditionalFields, SequenceDescriptor): if child.tag == 'show': location = ConditionalDescriptor.parse_sources(child, system) children.extend(location) - show_tag_list.extend(location.url()) + show_tag_list.extend(location.url()) # pylint: disable=no-member else: try: descriptor = system.process_xml(etree.tostring(child)) diff --git a/common/lib/xmodule/xmodule/error_module.py b/common/lib/xmodule/xmodule/error_module.py index 4f7ad25eb0..d6129299c5 100644 --- a/common/lib/xmodule/xmodule/error_module.py +++ b/common/lib/xmodule/xmodule/error_module.py @@ -134,7 +134,7 @@ class ErrorDescriptor(ErrorFields, XModuleDescriptor): ) @classmethod - def from_xml(cls, xml_data, system, id_generator, + def from_xml(cls, xml_data, system, id_generator, # pylint: disable=arguments-differ error_msg='Error not available'): '''Create an instance of this descriptor from the supplied data. diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py index 0a1cf9ee10..89b8e80d15 100644 --- a/common/lib/xmodule/xmodule/modulestore/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/__init__.py @@ -268,7 +268,9 @@ class Location(_LocationBase): _check_location_part(value, INVALID_CHARS_NAME) else: _check_location_part(value, INVALID_CHARS) - return super(Location, self)._replace(**kwargs) + + # namedtuple is an old-style class, so don't use super + return _LocationBase._replace(self, **kwargs) def replace(self, **kwargs): ''' diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_location.py b/common/lib/xmodule/xmodule/modulestore/tests/test_location.py index 5f4983e1bf..f0c815f41c 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_location.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_location.py @@ -19,6 +19,9 @@ GENERAL_PAIRS = [ @ddt.ddt class TestLocations(TestCase): + """ + Tests of :class:`.Location` + """ @ddt.data( "tag://org/course/category/name", "tag://org/course/category/name@revision" @@ -173,6 +176,8 @@ class TestLocations(TestCase): loc.course_id # pylint: disable=pointless-statement def test_replacement(self): + # pylint: disable=protected-access + self.assertEquals( Location('t://o/c/c/n@r')._replace(name='new_name'), Location('t://o/c/c/new_name@r'), diff --git a/common/lib/xmodule/xmodule/modulestore/xml.py b/common/lib/xmodule/xmodule/modulestore/xml.py index 13079bf9b3..664cbd9807 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml.py +++ b/common/lib/xmodule/xmodule/modulestore/xml.py @@ -18,10 +18,9 @@ from xmodule.error_module import ErrorDescriptor from xmodule.errortracker import make_error_tracker, exc_info_to_str from xmodule.course_module import CourseDescriptor from xmodule.mako_module import MakoDescriptorSystem -from xmodule.x_module import XMLParsingSystem, prefer_xmodules, policy_key +from xmodule.x_module import XMLParsingSystem, policy_key from xmodule.html_module import HtmlDescriptor -from xblock.core import XBlock from xblock.fields import ScopeIds from xblock.field_data import DictFieldData from xblock.runtime import DictKeyValueStore, IdReader, IdGenerator @@ -222,6 +221,7 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): # load_item should actually be get_instance, because it expects the course-specific # policy to be loaded. For now, just add the course_id here... def load_item(location): + """Return the XBlock for the specified location""" return xmlstore.get_instance(course_id, Location(location)) resources_fs = OSFS(xmlstore.data_dir / course_dir) @@ -509,6 +509,9 @@ class XMLModuleStore(ModuleStoreReadBase): course_id = CourseDescriptor.make_id(org, course, url_name) def get_policy(usage_id): + """ + Return the policy dictionary to be applied to the specified XBlock usage + """ return policy.get(policy_key(usage_id), {}) system = ImportSystem( @@ -570,7 +573,7 @@ class XMLModuleStore(ModuleStoreReadBase): html = f.read().decode('utf-8') # tabs are referenced in policy.json through a 'slug' which is just the filename without the .html suffix slug = os.path.splitext(os.path.basename(filepath))[0] - loc = course_descriptor.scope_ids.usage_id._replace(category=category, name=slug) + loc = course_descriptor.scope_ids.usage_id.replace(category=category, name=slug) module = system.construct_xblock_from_class( HtmlDescriptor, # We're loading a descriptor, so student_id is meaningless diff --git a/common/lib/xmodule/xmodule/tests/test_import.py b/common/lib/xmodule/xmodule/tests/test_import.py index 994603e2e1..12f889d897 100644 --- a/common/lib/xmodule/xmodule/tests/test_import.py +++ b/common/lib/xmodule/xmodule/tests/test_import.py @@ -78,6 +78,7 @@ class BaseCourseTestCase(unittest.TestCase): class GenericXBlock(XBlock): + """XBlock for testing pure xblock xml import""" has_children = True field1 = String(default="something", scope=Scope.user_state) field2 = Integer(scope=Scope.user_state) @@ -85,6 +86,9 @@ class GenericXBlock(XBlock): @ddt.ddt class PureXBlockImportTest(BaseCourseTestCase): + """ + Tests of import pure XBlocks (not XModules) from xml + """ def assert_xblocks_are_good(self, block): """Assert a number of conditions that must be true for `block` to be good.""" diff --git a/common/lib/xmodule/xmodule/tests/test_xml_module.py b/common/lib/xmodule/xmodule/tests/test_xml_module.py index 62ebf4dce0..d8192d7a8d 100644 --- a/common/lib/xmodule/xmodule/tests/test_xml_module.py +++ b/common/lib/xmodule/xmodule/tests/test_xml_module.py @@ -119,13 +119,11 @@ class InheritingFieldDataTest(unittest.TestCase): parent = self.get_a_block(usage_id="parent") parent.inherited = "Changed!" self.assertEqual(parent.inherited, "Changed!") - parent_id = "parent" for child_num in range(10): usage_id = "child_{}".format(child_num) child = self.get_a_block(usage_id=usage_id) child.parent = "parent" self.assertEqual(child.inherited, "Changed!") - parent_id = usage_id def test_not_inherited(self): # Fields not in the inherited_names list won't be inherited. diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index e7bc5c47fb..c62d1c9a70 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -14,7 +14,7 @@ from xblock.core import XBlock from xblock.fields import Scope, Integer, Float, List, XBlockMixin, String from xblock.fragment import Fragment from xblock.plugin import default_select -from xblock.runtime import Runtime, MemoryIdManager +from xblock.runtime import Runtime from xmodule.fields import RelativeTime from xmodule.errortracker import exc_info_to_str diff --git a/common/lib/xmodule/xmodule/xml_module.py b/common/lib/xmodule/xmodule/xml_module.py index c659b7787b..d4ea3b17ee 100644 --- a/common/lib/xmodule/xmodule/xml_module.py +++ b/common/lib/xmodule/xmodule/xml_module.py @@ -6,8 +6,7 @@ import sys from lxml import etree from xblock.fields import Dict, Scope, ScopeIds -from xmodule.x_module import XModuleDescriptor, policy_key -from xmodule.modulestore import Location +from xmodule.x_module import XModuleDescriptor from xmodule.modulestore.inheritance import own_metadata, InheritanceKeyValueStore from xmodule.modulestore.xml_exporter import EdxJSONEncoder from xblock.runtime import KvsFieldData @@ -176,7 +175,7 @@ class XmlDescriptor(XModuleDescriptor): return etree.parse(file_object, parser=edx_xml_parser).getroot() @classmethod - def load_file(cls, filepath, fs, def_id): + def load_file(cls, filepath, fs, def_id): # pylint: disable=invalid-name ''' Open the specified file in fs, and call cls.file_to_xml on it, returning the lxml object. @@ -184,8 +183,8 @@ class XmlDescriptor(XModuleDescriptor): Add details and reraise on error. ''' try: - with fs.open(filepath) as file: - return cls.file_to_xml(file) + with fs.open(filepath) as xml_file: + return cls.file_to_xml(xml_file) except Exception as err: # Add info about where we are, but keep the traceback msg = 'Unable to load file contents at path %s for item %s: %s ' % ( diff --git a/pylintrc b/pylintrc index 5a3a1ad01c..cba312005f 100644 --- a/pylintrc +++ b/pylintrc @@ -127,7 +127,13 @@ generated-members= build, # For xblocks fields, - +# For locations + tag, + org, + course, + category, + name, + revision, [BASIC] From c4fb232c6cec26c26aec6b49f34389283c297163 Mon Sep 17 00:00:00 2001 From: Mark Hoeber Date: Thu, 23 Jan 2014 15:10:58 -0500 Subject: [PATCH 002/134] 1st draft external graders doc --- .../source/Images/external-grader-correct.png | Bin 0 -> 78447 bytes .../Images/external-grader-incorrect.png | Bin 0 -> 32746 bytes .../source/external_graders.rst | 137 ++++++++++++++++++ docs/en_us/course_authors/source/index.rst | 1 + 4 files changed, 138 insertions(+) create mode 100644 docs/en_us/course_authors/source/Images/external-grader-correct.png create mode 100644 docs/en_us/course_authors/source/Images/external-grader-incorrect.png create mode 100644 docs/en_us/course_authors/source/external_graders.rst diff --git a/docs/en_us/course_authors/source/Images/external-grader-correct.png b/docs/en_us/course_authors/source/Images/external-grader-correct.png new file mode 100644 index 0000000000000000000000000000000000000000..636b9bc479ce7aefdf6a9705c7166e8424261556 GIT binary patch literal 78447 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%0AJW&wwf@n8r)|@B968PKI`ghZ(e>g5DRZL)j7ig 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 Lu((b%`aAYNpOw+J literal 0 HcmV?d00001 diff --git a/docs/en_us/course_authors/source/Images/external-grader-incorrect.png b/docs/en_us/course_authors/source/Images/external-grader-incorrect.png new file mode 100644 index 0000000000000000000000000000000000000000..d3bbcbed8a7eea05973bd48421ffd61ca92ab1e2 GIT binary patch literal 32746 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 literal 0 HcmV?d00001 diff --git a/docs/en_us/course_authors/source/external_graders.rst b/docs/en_us/course_authors/source/external_graders.rst new file mode 100644 index 0000000000..487caf0374 --- /dev/null +++ b/docs/en_us/course_authors/source/external_graders.rst @@ -0,0 +1,137 @@ +.. _Using External Graders: + +########################### +Using External Graders +########################### +This chapter describes how external graders work, how to set up problems to use external graders, and operational issues you must address. + +* +* +* + + +.. _External Grader Overview: +******************* +Overview +******************* + +An external grader is a system you deploy separately from the edX platform that receives student resposnes to a problem, processes those responses, and returns feedback and a problem grade to the edX platform. + +Using an external grader is particularly useful for software programming courses where students are asked to submit complicated code. The external grader can run tests that you define on that code and return results to a student. + +For example, the student enters Python code for the following problem. When the student clicks Check, the code is sent to an external grader, where tests are run on it. If the code passes all tests, the external grader returns the score you assigned to the problem and a notice that the solution is correct. + +.. image:: Images/external-grader-correct.png + :alt: Image of a students view of a programming problem that uses an external grader, with a correct result + + +The external grader can return a string with results, which can be displayed to the student. This can be particularly useful when the solution is not correct and you want to return information about the failed tests. For example: + +.. image:: Images/external-grader-incorrect.png + :alt: Image of a students view of a programming problem that uses an external grader, with a correct result + + +************************************** +External Graders and XQueue +************************************** + +To use an external grader to check problems, you use an XQueue server. XQueue is the edX interface that manages communication between the edX Platform and your external grader. The XQueue provides student input to the external grader; it then receives results from the external grader and returns them to the student using the edX Learning Management System. + +XQueue must be set up in one of two modes: + +* **Pull** + + +* **Push** + + +You must determine which mode to use when you are building your course and your external grader, and communicate this decision to your edX Program Manager. + +[DO WE NEED TO MORE SPECIFICALLY DEFINE THE RESTful INTERFACE FOR THIS?] + +================== +Pull Mode +================== + +In Pull mode, student submissions are collected in XQueue, where they remain until the external grader actively retrieves, or pulls, the next submission from the queue for grading. + +The external grader polls the XQueue through a RESTful interface at a regular interval. When the external grader receives a submission, it runs the defined tests on it, then pushes the response back to XQueue through the RESTful interface. XQueue then delivers the response to the edX Learning Management System. + + +================== +Push Mode +================== + +In Push mode, XQueue actively pushes student submissions to the external grader, which passivily waits for the next submission to grade. When the external grader receives a submission, it runs the defines tests on it, then synchronously delivers the graded response back to the XQueue. XQueue then delivers the response to the edX Learning Management System. + + + +============================ +External Grader Workflow +============================ + +The following steps show the complete process: + +#. The student either enters code or attaches a file for a problem, then clicks Check. +#. XQueue either pushes the code to the external grader, or waits until the external grader pulls the code. +#. The external runs tests that you define on the code. +#. The external grader returns the the XQueue the grade for the code, as well as any results in a string. +#. The XQueue delivers the results to the edX Learning Management System. +#. The student sees the problem results and the grade. + + +**************************** +Building an External Grader +**************************** + +Course staff, not edX, is responsible for building and deploying the external grader. + +In addition to creating tests that are specific to the problems you add to your course, there are four areas that you must plan for when building an external grader: + +* **Scale** +* **Security** +* **Reliability** +* **Notifications** + +================== +Scale +================== + +Your external grader must be able to scale to support the number of students in your course. + +Keep in mind that student submissions will likely come in spikes, and not in an even flow. For example, you should expect the load to be much greater than average in the hours before an exam is due. Therefore, you should verify that the external grader can process submissions from a majority of students in a short period of time. [HOW MUCH MORE SPECIFIC CAN WE BE HERE] + +================== +Security +================== + +Students are submitting code than executes directly on a server your are responsible for. It is possible that a student submits malicious code to your external grader. Your system must protect against this and ensure that it runs only code that is relevent to the course problems. How you implement these protections depends on the programming language you are using and your deployment architecture. You should verify that your external grader can identify malicious code and prevent its execution. + +============================== +Reliability and Recovery +============================== + +Once your course starts, many students will submit code at any possible time, and expect to see results quickly. If your external grader is prone to failure or unexpected delays, the student experience will be poor. + +Therefore, you must ensure that your external grader has high availability and can recover from errors. Prior to your course starting, you must develop a plan to immediately notifiy the team reponsible for operating your external grader, as well as edX operations, when the external grader fails. Contact your edX Program Manager for more information. + +If you know the external grader will be unavailable at a certain time for maintenance, you should :ref:`Add a Course Update`. + +================== +Notifications +================== + +*************************** +Set up an External Grader +*************************** + +1. Request new xqueue from PM and get name + +2. Set up problems -- example of text box and upload file + +3. Set up graders. hosting responsibility. + +4. test (and negative test) your problems. + + + diff --git a/docs/en_us/course_authors/source/index.rst b/docs/en_us/course_authors/source/index.rst index c9d3e30193..5b7d3e4271 100755 --- a/docs/en_us/course_authors/source/index.rst +++ b/docs/en_us/course_authors/source/index.rst @@ -24,6 +24,7 @@ Contents common_problems advanced_problems specialized_problems + external_graders open_response_assessment tools set_content_releasedates From 147aab4d54fb96054860744d537a431314bf2fc4 Mon Sep 17 00:00:00 2001 From: Mark Hoeber Date: Fri, 24 Jan 2014 11:37:46 -0500 Subject: [PATCH 003/134] External Graders First Review Draft --- .../source/Images/external-grader-correct.png | Bin 78447 -> 33172 bytes .../source/external_graders.rst | 174 ++++++++++++++---- 2 files changed, 137 insertions(+), 37 deletions(-) diff --git a/docs/en_us/course_authors/source/Images/external-grader-correct.png b/docs/en_us/course_authors/source/Images/external-grader-correct.png index 636b9bc479ce7aefdf6a9705c7166e8424261556..9842d580c58c81e18076de0031a9cfd450907cbb 100644 GIT binary patch delta 30708 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 O<*~eqT+u@#|NjFSgkWH4JlzHQ)a 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%0AJW&wwf@n8r)|@B968PKI`ghZ(e>g5DRZL)j7ig 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 Lu((b%`aAYNpOw+J diff --git a/docs/en_us/course_authors/source/external_graders.rst b/docs/en_us/course_authors/source/external_graders.rst index 487caf0374..76614206e3 100644 --- a/docs/en_us/course_authors/source/external_graders.rst +++ b/docs/en_us/course_authors/source/external_graders.rst @@ -3,11 +3,6 @@ ########################### Using External Graders ########################### -This chapter describes how external graders work, how to set up problems to use external graders, and operational issues you must address. - -* -* -* .. _External Grader Overview: @@ -15,55 +10,67 @@ This chapter describes how external graders work, how to set up problems to use Overview ******************* -An external grader is a system you deploy separately from the edX platform that receives student resposnes to a problem, processes those responses, and returns feedback and a problem grade to the edX platform. +An external grader is a service that receives student resposnes to a problem, processes those responses, and returns feedback and a problem grade to the edX platform. You build and deploy an external grader separately from the edX platform. -Using an external grader is particularly useful for software programming courses where students are asked to submit complicated code. The external grader can run tests that you define on that code and return results to a student. +See the following sections for more information: -For example, the student enters Python code for the following problem. When the student clicks Check, the code is sent to an external grader, where tests are run on it. If the code passes all tests, the external grader returns the score you assigned to the problem and a notice that the solution is correct. +* :ref:`External Grader Example` +* :ref:`External Graders and XQueue` +* :ref:`The XQueue Interface` +* :ref:`Building an External Grader` +* :ref:`Create a Code Response Problem` + +.. _External Grader Example: + +*************************** +External Grader Example +*************************** + +An external grader is particularly useful for software programming courses where students are asked to submit complicated code. The grader can run tests that you define on that code and return results to a student. + +For example, a student enters Python code for the following problem. When the student clicks Check, the code is sent to the grader for testing. If the code passes all tests, the grader returns the score you assigned to the problem and a string indicating that the solution is correct. .. image:: Images/external-grader-correct.png :alt: Image of a students view of a programming problem that uses an external grader, with a correct result -The external grader can return a string with results, which can be displayed to the student. This can be particularly useful when the solution is not correct and you want to return information about the failed tests. For example: +The external grader can return a string with results, which the student can see by clicking See full output. This can be particularly useful when the solution is not correct and you want to return information about the failed tests. For example: .. image:: Images/external-grader-incorrect.png :alt: Image of a students view of a programming problem that uses an external grader, with a correct result +.. _External Graders and XQueue: ************************************** External Graders and XQueue ************************************** -To use an external grader to check problems, you use an XQueue server. XQueue is the edX interface that manages communication between the edX Platform and your external grader. The XQueue provides student input to the external grader; it then receives results from the external grader and returns them to the student using the edX Learning Management System. +To use an external grader to check problems, you use XQueue. XQueue is the edX interface that manages communication between the edX Platform and your grader. The XQueue provides students' input to the grader; it then receives results from the grader and returns them to students. XQueue must be set up in one of two modes: * **Pull** - * **Push** -You must determine which mode to use when you are building your course and your external grader, and communicate this decision to your edX Program Manager. - -[DO WE NEED TO MORE SPECIFICALLY DEFINE THE RESTful INTERFACE FOR THIS?] +You determine which mode to use when you are building your course and your grader. You must communicate this decision to your edX Program Manager. The student experience is not affected by this decision. ================== Pull Mode ================== -In Pull mode, student submissions are collected in XQueue, where they remain until the external grader actively retrieves, or pulls, the next submission from the queue for grading. +In Pull mode, student submissions are collected in XQueue, where they remain until the grader actively retrieves, or pulls, the next submission from the queue for grading. -The external grader polls the XQueue through a RESTful interface at a regular interval. When the external grader receives a submission, it runs the defined tests on it, then pushes the response back to XQueue through the RESTful interface. XQueue then delivers the response to the edX Learning Management System. +The external grader polls the XQueue through a RESTful interface at a regular interval. When the external grader receives a submission, it runs the tests on it, then pushes the response back to XQueue through the RESTful interface. XQueue then delivers the response to the edX Learning Management System. +[INTERFACE DETAILS NEEDED?] ================== Push Mode ================== -In Push mode, XQueue actively pushes student submissions to the external grader, which passivily waits for the next submission to grade. When the external grader receives a submission, it runs the defines tests on it, then synchronously delivers the graded response back to the XQueue. XQueue then delivers the response to the edX Learning Management System. - +In Push mode, XQueue actively pushes student submissions to the external grader, which passivily waits for the next submission to grade. When the external grader receives a submission, it runs the tests on it, then synchronously delivers the graded response back to the XQueue. XQueue then delivers the response to the edX Learning Management System. ============================ @@ -74,24 +81,78 @@ The following steps show the complete process: #. The student either enters code or attaches a file for a problem, then clicks Check. #. XQueue either pushes the code to the external grader, or waits until the external grader pulls the code. -#. The external runs tests that you define on the code. -#. The external grader returns the the XQueue the grade for the code, as well as any results in a string. +#. The external grader runs the tests that you created on the code. +#. The external grader returns to the XQueue the grade for the submission, as well as any results in a string. #. The XQueue delivers the results to the edX Learning Management System. #. The student sees the problem results and the grade. +================== +The XQueue Name +================== + +Your course will use a specific XQueue name. You use this name when creating problems in edX Studio. You get this name from your edX Program Manager. As edX hosts many XQueues for different courses, it is critical that you use the exact XQueue name in your problems, as described in the section :ref:`Create a Code Response Problem`. + + +.. _The XQueue Interface: + +************************************** +The XQueue Interface +************************************** + +The student submission sent from XQueue to the grader, and the response send from the grader to XQueue, are JSON objects, as described below. + +====================================================== +Inputs to the External Grader +====================================================== + +The grader receives student submissions as a JSON object with two keys: + +* **student_response**: A string containing the student's code submission. The string can come from input the student enters in the edX Learning Management System, or a file the student attaches. + +* **grader_payload**: An optional string that you can specify when creating the problem. For more information, see the section :ref:`Create a Code Response Problem`. + +For example:: + + { + "xqueue_body": + "{ + "student_response": "def double(x):\n return 2*x\n", + "grader_payload": "problem_2" + }" + } + +====================================================== +External Grader Responses +====================================================== + +After running tests and recording results for a submission, the grader must return information by posting a JSON response. The JSON string contains an indication if the submission was correct, the score, and any message the tests create. + +In the following example, the grader returns a JSON string that indicates the submission was correct, the score was 1, and a message:: + + { + "correct": true, + "score": 1, + "msg": "

The code passed all tests.

" + } + +.. _Building an External Grader: + **************************** Building an External Grader **************************** Course staff, not edX, is responsible for building and deploying the external grader. -In addition to creating tests that are specific to the problems you add to your course, there are four areas that you must plan for when building an external grader: +In addition to creating tests that are specific to the problems you use in your course, there are four areas that you must plan for when building an external grader: -* **Scale** -* **Security** -* **Reliability** -* **Notifications** +* :ref:`Scale` +* :ref:`Security` +* :ref:`Reliability and Recovery` +* :ref:`Testing` + + +.. _Scale: ================== Scale @@ -99,13 +160,17 @@ Scale Your external grader must be able to scale to support the number of students in your course. -Keep in mind that student submissions will likely come in spikes, and not in an even flow. For example, you should expect the load to be much greater than average in the hours before an exam is due. Therefore, you should verify that the external grader can process submissions from a majority of students in a short period of time. [HOW MUCH MORE SPECIFIC CAN WE BE HERE] +Keep in mind that student submissions will likely come in spikes, not in an even flow. For example, you should expect the load to be much greater than average in the hours before an exam is due. Therefore, you should verify that the external grader can process submissions from a majority of students in a short period of time. [HOW MUCH MORE SPECIFIC CAN WE BE HERE?] + +.. _Security: ================== Security ================== -Students are submitting code than executes directly on a server your are responsible for. It is possible that a student submits malicious code to your external grader. Your system must protect against this and ensure that it runs only code that is relevent to the course problems. How you implement these protections depends on the programming language you are using and your deployment architecture. You should verify that your external grader can identify malicious code and prevent its execution. +Students are submitting code than executes directly on a server your are responsible for. It is possible that a student will submit malicious code. Your system must protect against this and ensure that the external grader runs only code that is relevent to the course problems. How you implement these protections depends on the programming language you are using and your deployment architecture. You must verify that your system can identify malicious code and prevent its execution. + +.. _Reliability and Recovery: ============================== Reliability and Recovery @@ -113,25 +178,60 @@ Reliability and Recovery Once your course starts, many students will submit code at any possible time, and expect to see results quickly. If your external grader is prone to failure or unexpected delays, the student experience will be poor. -Therefore, you must ensure that your external grader has high availability and can recover from errors. Prior to your course starting, you must develop a plan to immediately notifiy the team reponsible for operating your external grader, as well as edX operations, when the external grader fails. Contact your edX Program Manager for more information. +Therefore, you must ensure that your grader has high availability and can recover from errors. Prior to your course starting, you must have a plan to immediately notifiy the team reponsible for operating your grader, as well as edX operations, when the grader fails. In collaboration with edX, you should develop a system to quickly identify to cause of failure, which may be your grader or edX's XQueue. -If you know the external grader will be unavailable at a certain time for maintenance, you should :ref:`Add a Course Update`. +Contact your edX Program Manager for more information. + +If you know the grader will be unavailable at a certain time for maintenance, you should :ref:`Add a Course Update`. + +.. _Testing: ================== -Notifications +Testing ================== -*************************** -Set up an External Grader -*************************** +You should test your grader thoroughly before you course starts. Be sure to test incorrect code to ensure that the grader responds with appropriate scores and messages. -1. Request new xqueue from PM and get name +.. _Create a Code Response Problem: -2. Set up problems -- example of text box and upload file +******************************** +Create a Code Response Problem +******************************** -3. Set up graders. hosting responsibility. +You create a code response problem in edX Studio adding a common blank problem, then editing the XML problem definition in the :ref:`Advanced Editor`. -4. test (and negative test) your problems. +See :ref:`Working with Problem Components` for more information. + +Following is a basic example of the XML definition of a problem that uses an external grader:: + + + +

Write a program that prints "hello world".

+
+ + + + + # students please write your program here + print "" + + + print "hello world" + + + {"output": "hello world", "max_length": 2} + + + +
+ +Note the following about the XML definition: + +* **queuename**: The value of the queuename attribute of the element maps to an XQueue that edX sets up for the course. You get this name from your edX Program Manager. You must use this exact name in order for the problem to communicate with the correct XQueue. + +* **Input Type**: In this example, the input type is specificed by the **** element. When you use , the student enters code in a browser field when viewing the course unit. The other element you can use is , which enables the student to attach and submit a code file in the unit. + +* ****: You can use the element to send information to the external grader in the form of a JSON object. For example, you can use to tell the grader which tests to run for this problem. From 14871342907e1b861f452762b7a4b7a5279a757b Mon Sep 17 00:00:00 2001 From: Giulio Gratta Date: Fri, 17 Jan 2014 14:12:05 -0800 Subject: [PATCH 004/134] Notify instructors that static pages are public --- cms/templates/edit-tabs.html | 1 + 1 file changed, 1 insertion(+) diff --git a/cms/templates/edit-tabs.html b/cms/templates/edit-tabs.html index 70c3e61801..d0e8f7b86b 100644 --- a/cms/templates/edit-tabs.html +++ b/cms/templates/edit-tabs.html @@ -47,6 +47,7 @@ require(["js/models/explicit_url", "coffee/src/views/tabs"], function(TabsModel, + + + + <%include file="_reverification_support.html" /> + + + +<%include file="_modal_editname.html" /> + diff --git a/lms/templates/verify_student/midcourse_reverification_confirmation.html b/lms/templates/verify_student/midcourse_reverification_confirmation.html new file mode 100644 index 0000000000..31f6e76538 --- /dev/null +++ b/lms/templates/verify_student/midcourse_reverification_confirmation.html @@ -0,0 +1,45 @@ + +<%! from django.utils.translation import ugettext as _ %> +<%! from django.core.urlresolvers import reverse %> +<%inherit file="../main.html" /> +<%namespace name='static' file='/static_content.html'/> + +<%block name="bodyclass">register verification-process is-not-verified step-confirmation +<%block name="title">${_("Re-Verification Submission Confirmation")} + +<%block name="js_extra"> + + + +<%block name="content"> + +
+
+ +
+
+
+
+
+

${_("Your Credentials Have Been Updated")}

+ +
+

${_("We have received your re-verification details and submitted them for review. Your dashboard will show the notification status once the review is complete.")}

+

${_("The professor may ask you to re-verify again at other key points in the course.")}

+
+ +
    + +
+
+
+
+
+
+ + <%include file="_reverification_support.html" /> +
+
+ diff --git a/lms/templates/verify_student/midcourse_reverify_dash.html b/lms/templates/verify_student/midcourse_reverify_dash.html new file mode 100644 index 0000000000..92403c297d --- /dev/null +++ b/lms/templates/verify_student/midcourse_reverify_dash.html @@ -0,0 +1,28 @@ +<%! from django.core.urlresolvers import reverse %> + +

Re-verify

+ +

You currently need to re-verify for the following course:

+ +% for course_id, course_name, date, status in reverify_course_data: +

${course_name}: Re-verify by ${date}. + % if status == "must_reverify": + Re-verify + % elif status == "completed": + Completed + % elif status == "failed": + Failed + % endif +

+% endfor + +

Why do I need to re-verify?

+

At key points in a course, the professor will ask you to re-verify your identity by submitting a new photo of your face. We will send the new photo to be matched up with the photo of the original ID you submitted when you signed up for the course. If you are taking multiple courses, you may need to re-verify multiple times, once for every important point in each course you are taking as a verified student.

+ +

What will I need to re-verify?

+

Because you are just confirming that you are still you, the only thing you will need to do to re-verify is to submit a new photo of your face with your webcam. The process is quick and you will be brought back to where you left off so you can keep on learning.

+ +

If you changed your name during the semester and it no longer matches the original ID you submitted, you will need to re-edit your name to match as well.

+ +

What if I have trouble with my re-verification?

+

Because of the short time that re-verification is open, you will not be able to correct a failed verification. If you think there was an error in the review, please contact us at support@edx.org.

\ No newline at end of file diff --git a/lms/templates/verify_student/prompt_midcourse_reverify.html b/lms/templates/verify_student/prompt_midcourse_reverify.html new file mode 100644 index 0000000000..b160036643 --- /dev/null +++ b/lms/templates/verify_student/prompt_midcourse_reverify.html @@ -0,0 +1,6 @@ +<%! from django.utils.translation import ugettext as _ %> + +

${_("You need to re-verify to continue")}

+

+ ${_("To continue in the verified track in {course}, you need to re-verify your identity by {date}. Go to URL.").format(email)} +

From 9c4a9d8f640e36a8862c5be293170bf38659d0fe Mon Sep 17 00:00:00 2001 From: Jay Zoldak Date: Fri, 31 Jan 2014 16:35:56 -0500 Subject: [PATCH 093/134] Make code safe for unicode course ids --- lms/djangoapps/bulk_email/tasks.py | 14 ++++++++------ lms/djangoapps/instructor_task/tasks_helper.py | 14 +++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/lms/djangoapps/bulk_email/tasks.py b/lms/djangoapps/bulk_email/tasks.py index 5b07f49fa5..c7e89d9142 100644 --- a/lms/djangoapps/bulk_email/tasks.py +++ b/lms/djangoapps/bulk_email/tasks.py @@ -159,8 +159,9 @@ def perform_delegate_email_batches(entry_id, course_id, task_input, action_name) # Perfunctory check, since expansion is made for convenience of other task # code that doesn't need the entry_id. if course_id != entry.course_id: - format_msg = "Course id conflict: explicit value {} does not match task value {}" - raise ValueError(format_msg.format(course_id, entry.course_id)) + format_msg = u"Course id conflict: explicit value {} does not match task value {}" + log.warning("Task %s: %s", task_id, format_msg.format(course_id, entry.course_id)) + raise ValueError("Course id conflict: explicit value does not match task value") # Fetch the CourseEmail. email_id = task_input['email_id'] @@ -186,8 +187,9 @@ def perform_delegate_email_batches(entry_id, course_id, task_input, action_name) # Sanity check that course for email_obj matches that of the task referencing it. if course_id != email_obj.course_id: - format_msg = "Course id conflict: explicit value {} does not match email value {}" - raise ValueError(format_msg.format(course_id, email_obj.course_id)) + format_msg = u"Course id conflict: explicit value {} does not match email value {}" + log.warning("Task %s: %s", task_id, format_msg.format(course_id, entry.course_id)) + raise ValueError("Course id conflict: explicit value does not match email value") # Fetch the course object. try: @@ -219,7 +221,7 @@ def perform_delegate_email_batches(entry_id, course_id, task_input, action_name) recipient_qset = _get_recipient_queryset(user_id, to_option, course_id, course.location) recipient_fields = ['profile__name', 'email'] - log.info("Task %s: Preparing to queue subtasks for sending emails for course %s, email %s, to_option %s", + log.info(u"Task %s: Preparing to queue subtasks for sending emails for course %s, email %s, to_option %s", task_id, course_id, email_id, to_option) progress = queue_subtasks_for_query( @@ -374,7 +376,7 @@ def _get_source_address(course_id, course_title): invalid_chars = re.compile(r"[^\w.-]") course_num = invalid_chars.sub('_', course_num) - from_addr = '"{0}" Course Staff <{1}-{2}>'.format(course_title_no_quotes, course_num, settings.BULK_EMAIL_DEFAULT_FROM_EMAIL) + from_addr = u'"{0}" Course Staff <{1}-{2}>'.format(course_title_no_quotes, course_num, settings.BULK_EMAIL_DEFAULT_FROM_EMAIL) return from_addr diff --git a/lms/djangoapps/instructor_task/tasks_helper.py b/lms/djangoapps/instructor_task/tasks_helper.py index 23d51a9ad7..4c7429def5 100644 --- a/lms/djangoapps/instructor_task/tasks_helper.py +++ b/lms/djangoapps/instructor_task/tasks_helper.py @@ -181,7 +181,7 @@ def run_main_task(entry_id, task_fcn, action_name): task_input = json.loads(entry.task_input) # construct log message: - fmt = 'task "{task_id}": course "{course_id}" input "{task_input}"' + fmt = u'task "{task_id}": course "{course_id}" input "{task_input}"' task_info_string = fmt.format(task_id=task_id, course_id=course_id, task_input=task_input) TASK_LOG.info('Starting update (nothing %s yet): %s', action_name, task_info_string) @@ -190,7 +190,7 @@ def run_main_task(entry_id, task_fcn, action_name): # that is running. request_task_id = _get_current_task().request.id if task_id != request_task_id: - fmt = 'Requested task did not match actual task "{actual_id}": {task_info}' + fmt = u'Requested task did not match actual task "{actual_id}": {task_info}' message = fmt.format(actual_id=request_task_id, task_info=task_info_string) TASK_LOG.error(message) raise ValueError(message) @@ -416,15 +416,15 @@ def rescore_problem_module_state(xmodule_instance_args, module_descriptor, stude if 'success' not in result: # don't consider these fatal, but false means that the individual call didn't complete: TASK_LOG.warning(u"error processing rescore call for course {course}, problem {loc} and student {student}: " - "unexpected response {msg}".format(msg=result, course=course_id, loc=module_state_key, student=student)) + u"unexpected response {msg}".format(msg=result, course=course_id, loc=module_state_key, student=student)) return UPDATE_STATUS_FAILED elif result['success'] not in ['correct', 'incorrect']: TASK_LOG.warning(u"error processing rescore call for course {course}, problem {loc} and student {student}: " - "{msg}".format(msg=result['success'], course=course_id, loc=module_state_key, student=student)) + u"{msg}".format(msg=result['success'], course=course_id, loc=module_state_key, student=student)) return UPDATE_STATUS_FAILED else: TASK_LOG.debug(u"successfully processed rescore call for course {course}, problem {loc} and student {student}: " - "{msg}".format(msg=result['success'], course=course_id, loc=module_state_key, student=student)) + u"{msg}".format(msg=result['success'], course=course_id, loc=module_state_key, student=student)) return UPDATE_STATUS_SUCCEEDED @@ -558,7 +558,7 @@ def push_grades_to_s3(_xmodule_instance_args, _entry_id, course_id, _task_input, grades_store = GradesStore.from_config() grades_store.store_rows( course_id, - "{}_grade_report_{}.csv".format(course_id_prefix, timestamp_str), + u"{}_grade_report_{}.csv".format(course_id_prefix, timestamp_str), rows ) @@ -566,7 +566,7 @@ def push_grades_to_s3(_xmodule_instance_args, _entry_id, course_id, _task_input, if len(err_rows) > 1: grades_store.store_rows( course_id, - "{}_grade_report_{}_err.csv".format(course_id_prefix, timestamp_str), + u"{}_grade_report_{}_err.csv".format(course_id_prefix, timestamp_str), err_rows ) From 6c7d715e0e0a7c5b2fb26d17c252205b45f33772 Mon Sep 17 00:00:00 2001 From: Frances Botsford Date: Fri, 17 Jan 2014 16:54:14 -0500 Subject: [PATCH 094/134] re-verification dashboard styles --- common/djangoapps/student/views.py | 29 +- lms/djangoapps/certificates/queue.py | 15 +- lms/djangoapps/verify_student/admin.py | 2 +- ...nwindow__add_field_softwaresecurephoto.py} | 13 +- ...03_auto__add_sspmidcoursereverification.py | 94 ---- lms/djangoapps/verify_student/models.py | 364 +++++---------- .../verify_student/tests/factories.py | 19 + .../verify_student/tests/test_models.py | 172 ++++++- .../verify_student/tests/test_views.py | 62 +++ lms/djangoapps/verify_student/views.py | 20 +- .../js/verify_student/photocapturebasic2.js | 4 +- lms/static/sass/application-extend1.scss.mako | 2 +- lms/static/sass/application-extend2.scss.mako | 3 +- lms/static/sass/application.scss.mako | 2 +- lms/static/sass/base/_mixins.scss | 24 + lms/static/sass/base/_variables.scss | 5 + lms/static/sass/course.scss.mako | 2 +- .../sass/elements/_system-feedback.scss | 74 +++ lms/static/sass/views/_verification.scss | 433 +++++++++++++----- lms/templates/dashboard.html | 15 +- .../_dashboard_prompt_midcourse_reverify.html | 26 +- .../_reverification_support.html | 7 +- .../verify_student/_verification_header.html | 4 + .../midcourse_photo_reverification.html | 44 +- .../midcourse_reverify_dash.html | 93 +++- 25 files changed, 982 insertions(+), 546 deletions(-) rename lms/djangoapps/verify_student/migrations/{0002_auto__add_midcoursereverificationwindow.py => 0002_auto__add_midcoursereverificationwindow__add_field_softwaresecurephoto.py} (90%) delete mode 100644 lms/djangoapps/verify_student/migrations/0003_auto__add_sspmidcoursereverification.py create mode 100644 lms/djangoapps/verify_student/tests/factories.py create mode 100644 lms/static/sass/elements/_system-feedback.scss diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index ea0c2a5114..585da3d713 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -10,7 +10,6 @@ import string # pylint: disable=W0402 import urllib import uuid import time -import datetime from pytz import UTC from django.conf import settings @@ -47,7 +46,7 @@ from student.models import ( ) from student.forms import PasswordResetFormNoActive -from verify_student.models import SoftwareSecurePhotoVerification, MidcourseReverificationWindow, SSPMidcourseReverification +from verify_student.models import SoftwareSecurePhotoVerification, MidcourseReverificationWindow from certificates.models import CertificateStatuses, certificate_status_for_student from xmodule.course_module import CourseDescriptor @@ -394,21 +393,24 @@ def dashboard(request): verification_status, verification_msg = SoftwareSecurePhotoVerification.user_status(user) # TODO: Factor this out into a function; I'm pretty sure there's code duplication floating around... - prompt_midcourse_reverify = False reverify_course_data = [] for (course, enrollment) in course_enrollment_pairs: - if MidcourseReverificationWindow.window_open_for_course(course.id) and not SSPMidcourseReverification.user_has_valid_or_pending(user, course.id): + + # IF the reverification window is open + if (MidcourseReverificationWindow.window_open_for_course(course.id)): + # AND the user is actually verified-enrolled AND they don't have a pending reverification already window = MidcourseReverificationWindow.get_window(course.id, datetime.datetime.now(UTC)) - status_for_window = SSPMidcourseReverification.get_status_for_window(user, window) - reverify_course_data.append( - ( - course.id, - course.display_name, - window.end_date, - "must_reverify" # TODO: reflect more states than just "must_reverify" has_valid_or_pending (must show failure) + if (enrollment.mode == "verified" and not SoftwareSecurePhotoVerification.user_has_valid_or_pending(user, window=window)): + window = MidcourseReverificationWindow.get_window(course.id, datetime.datetime.now(UTC)) + status_for_window = SoftwareSecurePhotoVerification.user_status(user, window=window) + reverify_course_data.append( + ( + course.id, + course.display_name, + window.end_date, + "must_reverify" # TODO: reflect more states than just "must_reverify" has_valid_or_pending (must show failure) + ) ) - ) - prompt_midcourse_reverify = True show_refund_option_for = frozenset(course.id for course, _enrollment in course_enrollment_pairs if _enrollment.refundable()) @@ -430,7 +432,6 @@ def dashboard(request): 'all_course_modes': course_modes, 'cert_statuses': cert_statuses, 'show_email_settings_for': show_email_settings_for, - 'prompt_midcourse_reverify': prompt_midcourse_reverify, 'reverify_course_data': reverify_course_data, 'verification_status': verification_status, 'verification_msg': verification_msg, diff --git a/lms/djangoapps/certificates/queue.py b/lms/djangoapps/certificates/queue.py index 0cf92ba428..04d1d0ba48 100644 --- a/lms/djangoapps/certificates/queue.py +++ b/lms/djangoapps/certificates/queue.py @@ -176,21 +176,16 @@ class XQueueCertInterface(object): is_whitelisted = self.whitelist.filter( user=student, course_id=course_id, whitelist=True).exists() enrollment_mode = CourseEnrollment.enrollment_mode_for_user(student, course_id) + mode_is_verified = (enrollment_mode == GeneratedCertificate.MODES.verified) + user_is_verified = SoftwareSecurePhotoVerification.user_is_verified(student) + user_is_reverified = SoftwareSecurePhotoVerification.user_is_reverified_for_all(course_id, student) org = course_id.split('/')[0] course_num = course_id.split('/')[1] cert_mode = enrollment_mode - if ( - (enrollment_mode == GeneratedCertificate.MODES.verified) and - SoftwareSecurePhotoVerification.user_is_verified(student) and - SSPMidcourseReverification.user_is_reverified_for_all(course_id, student) - ): + if (mode_is_verified and user_is_verified and user_is_reverified): template_pdf = "certificate-template-{0}-{1}-verified.pdf".format( org, course_num) - elif ( - (enrollment_mode == GeneratedCertificate.MODES.verified) and not - (SoftwareSecurePhotoVerification.user_is_verified(student)) and not - (SSPMidcourseReverification.user_is_reverified_for_all(course_id, student)) - ): + elif (mode_is_verified and not (user_is_verified and user_is_reverified)): template_pdf = "certificate-template-{0}-{1}.pdf".format( org, course_num) cert_mode = GeneratedCertificate.MODES.honor diff --git a/lms/djangoapps/verify_student/admin.py b/lms/djangoapps/verify_student/admin.py index fe7c2411a7..be3f5b24d3 100644 --- a/lms/djangoapps/verify_student/admin.py +++ b/lms/djangoapps/verify_student/admin.py @@ -3,4 +3,4 @@ from verify_student.models import SoftwareSecurePhotoVerification from verify_student.models import MidcourseReverificationWindow admin.site.register(SoftwareSecurePhotoVerification) -admin.site.register(MidcourseReverificationWindow) \ No newline at end of file +admin.site.register(MidcourseReverificationWindow) diff --git a/lms/djangoapps/verify_student/migrations/0002_auto__add_midcoursereverificationwindow.py b/lms/djangoapps/verify_student/migrations/0002_auto__add_midcoursereverificationwindow__add_field_softwaresecurephoto.py similarity index 90% rename from lms/djangoapps/verify_student/migrations/0002_auto__add_midcoursereverificationwindow.py rename to lms/djangoapps/verify_student/migrations/0002_auto__add_midcoursereverificationwindow__add_field_softwaresecurephoto.py index 5543247aed..92ec4fdb4c 100644 --- a/lms/djangoapps/verify_student/migrations/0002_auto__add_midcoursereverificationwindow.py +++ b/lms/djangoapps/verify_student/migrations/0002_auto__add_midcoursereverificationwindow__add_field_softwaresecurephoto.py @@ -17,11 +17,19 @@ class Migration(SchemaMigration): )) db.send_create_signal('verify_student', ['MidcourseReverificationWindow']) + # Adding field 'SoftwareSecurePhotoVerification.window' + db.add_column('verify_student_softwaresecurephotoverification', 'window', + self.gf('django.db.models.fields.related.ForeignKey')(to=orm['verify_student.MidcourseReverificationWindow'], null=True), + keep_default=False) + def backwards(self, orm): # Deleting model 'MidcourseReverificationWindow' db.delete_table('verify_student_midcoursereverificationwindow') + # Deleting field 'SoftwareSecurePhotoVerification.window' + db.delete_column('verify_student_softwaresecurephotoverification', 'window_id') + models = { 'auth.group': { @@ -77,14 +85,15 @@ class Migration(SchemaMigration): 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), 'photo_id_image_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}), 'photo_id_key': ('django.db.models.fields.TextField', [], {'max_length': '1024'}), - 'receipt_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'db_index': 'True'}), + 'receipt_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'db_index': 'True'}), 'reviewing_service': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), 'reviewing_user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'photo_verifications_reviewed'", 'null': 'True', 'to': "orm['auth.User']"}), 'status': ('model_utils.fields.StatusField', [], {'default': "'created'", 'max_length': '100', u'no_check_for_status': 'True'}), 'status_changed': ('model_utils.fields.MonitorField', [], {'default': 'datetime.datetime.now', u'monitor': "u'status'"}), 'submitted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'window': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['verify_student.MidcourseReverificationWindow']", 'null': 'True'}) } } diff --git a/lms/djangoapps/verify_student/migrations/0003_auto__add_sspmidcoursereverification.py b/lms/djangoapps/verify_student/migrations/0003_auto__add_sspmidcoursereverification.py deleted file mode 100644 index 55720b1d04..0000000000 --- a/lms/djangoapps/verify_student/migrations/0003_auto__add_sspmidcoursereverification.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding model 'SSPMidcourseReverification' - db.create_table('verify_student_sspmidcoursereverification', ( - ('softwaresecurephotoverification_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['verify_student.SoftwareSecurePhotoVerification'], unique=True, primary_key=True)), - ('window', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['verify_student.MidcourseReverificationWindow'])), - )) - db.send_create_signal('verify_student', ['SSPMidcourseReverification']) - - - def backwards(self, orm): - # Deleting model 'SSPMidcourseReverification' - db.delete_table('verify_student_sspmidcoursereverification') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'verify_student.midcoursereverificationwindow': { - 'Meta': {'object_name': 'MidcourseReverificationWindow'}, - 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), - 'end_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'start_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}) - }, - 'verify_student.softwaresecurephotoverification': { - 'Meta': {'ordering': "['-created_at']", 'object_name': 'SoftwareSecurePhotoVerification'}, - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), - 'error_code': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'error_msg': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'face_image_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), - 'photo_id_image_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}), - 'photo_id_key': ('django.db.models.fields.TextField', [], {'max_length': '1024'}), - 'receipt_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'db_index': 'True'}), - 'reviewing_service': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), - 'reviewing_user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'photo_verifications_reviewed'", 'null': 'True', 'to': "orm['auth.User']"}), - 'status': ('model_utils.fields.StatusField', [], {'default': "'created'", 'max_length': '100', u'no_check_for_status': 'True'}), - 'status_changed': ('model_utils.fields.MonitorField', [], {'default': 'datetime.datetime.now', u'monitor': "u'status'"}), - 'submitted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), - 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) - }, - 'verify_student.sspmidcoursereverification': { - 'Meta': {'ordering': "['-created_at']", 'object_name': 'SSPMidcourseReverification', '_ormbases': ['verify_student.SoftwareSecurePhotoVerification']}, - 'softwaresecurephotoverification_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['verify_student.SoftwareSecurePhotoVerification']", 'unique': 'True', 'primary_key': 'True'}), - 'window': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['verify_student.MidcourseReverificationWindow']"}) - } - } - - complete_apps = ['verify_student'] \ No newline at end of file diff --git a/lms/djangoapps/verify_student/models.py b/lms/djangoapps/verify_student/models.py index a93362d9c9..b406cfa25b 100644 --- a/lms/djangoapps/verify_student/models.py +++ b/lms/djangoapps/verify_student/models.py @@ -23,7 +23,7 @@ import pytz import requests from django.conf import settings -from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.urlresolvers import reverse from django.db import models from django.contrib.auth.models import User @@ -38,28 +38,33 @@ from verify_student.ssencrypt import ( log = logging.getLogger(__name__) -# Evidently South migrations complain a lot if you have a default set to uuid.uuid4, so -# I had to add this function to make South happy, see this for more: -# http://stackoverflow.com/questions/15041265/south-migrate-error-name-uuid-is-not-defined -# If anyone knows a happier solution, do let me know; otherwise I'll remove this comment -# after CR + def generateUUID(): - return str(uuid.uuid4) + return str(uuid.uuid4) + class MidcourseReverificationWindow(models.Model): """ Defines the start and end times for midcourse reverification for a particular course. - There can be many MidcourseReverificationWindows per course, but they should not - have overlapping time-ranges (i.e. Window2's start date should not be before Window1's - start date) (TODO: should the non-overlap constraint be explicitly enforced by the model?) + There can be many MidcourseReverificationWindows per course, but they cannot have + overlapping time ranges. This is enforced by this class's clean() method. """ # the course that this window is attached to - # TODO should this be a foreignkey? course_id = models.CharField(max_length=255, db_index=True) start_date = models.DateTimeField(default=None, null=True, blank=True) end_date = models.DateTimeField(default=None, null=True, blank=True) + def clean(self): + """ + Gives custom validation for the MidcourseReverificationWindow model. + Prevents overlapping windows for any particular course. + """ + query = MidcourseReverificationWindow.objects.filter(course_id=self.course_id) + for item in query: + if (self.start_date <= item.end_date) and (item.start_date <= self.end_date): + raise ValidationError('Reverification windows cannot overlap for a given course.') + @classmethod def window_open_for_course(cls, course_id): """ @@ -67,16 +72,16 @@ class MidcourseReverificationWindow(models.Model): """ now = datetime.now(pytz.UTC) - # We are assuming one window per course_id. TODO find out if this assumption is OK try: - window = cls.objects.get(course_id=course_id) + cls.objects.get( + course_id=course_id, + start_date__lte=now, + end_date__gte=now, + ) except(ObjectDoesNotExist): return False - if (window.start_date <= now <= window.end_date): - return True - else: - return False + return True @classmethod def get_window(cls, course_id, date): @@ -219,8 +224,6 @@ class PhotoVerification(StatusModel): # capturing it so that we can later query for the common problems. error_code = models.CharField(blank=True, max_length=50) - - class Meta: abstract = True ordering = ['-created_at'] @@ -239,21 +242,22 @@ class PhotoVerification(StatusModel): return allowed_date @classmethod - def user_is_verified(cls, user, earliest_allowed_date=None): + def user_is_verified(cls, user, earliest_allowed_date=None, window=None): """ Return whether or not a user has satisfactorily proved their - identity. Depending on the policy, this can expire after some period of - time, so a user might have to renew periodically. + identity wrt to the INITIAL verification. Depending on the policy, + this can expire after some period of time, so a user might have to renew periodically. """ return cls.objects.filter( user=user, status="approved", created_at__gte=(earliest_allowed_date - or cls._earliest_allowed_date()) + or cls._earliest_allowed_date()), + window=window ).exists() @classmethod - def user_has_valid_or_pending(cls, user, earliest_allowed_date=None): + def user_has_valid_or_pending(cls, user, earliest_allowed_date=None, window=None): """ Return whether the user has a complete verification attempt that is or *might* be good. This means that it's approved, been submitted, or would @@ -261,30 +265,34 @@ class PhotoVerification(StatusModel): submitted. It's basically any situation in which the user has signed off on the contents of the attempt, and we have not yet received a denial. """ - valid_statuses = ['must_retry', 'submitted', 'approved'] + if window: + valid_statuses = ['submitted', 'approved'] + else: + valid_statuses = ['must_retry', 'submitted', 'approved'] return cls.objects.filter( user=user, status__in=valid_statuses, created_at__gte=(earliest_allowed_date - or cls._earliest_allowed_date()) + or cls._earliest_allowed_date()), + window=window, ).exists() @classmethod - def active_for_user(cls, user): + def active_for_user(cls, user, window=None): """ - Return the most recent PhotoVerification that is marked ready (i.e. the + Return the most recent INITIAL PhotoVerification that is marked ready (i.e. the user has said they're set, but we haven't submitted anything yet). """ # This should only be one at the most, but just in case we create more # by mistake, we'll grab the most recently created one. - active_attempts = cls.objects.filter(user=user, status='ready').order_by('-created_at') + active_attempts = cls.objects.filter(user=user, status='ready', window=window).order_by('-created_at') if active_attempts: return active_attempts[0] else: return None @classmethod - def user_status(cls, user): + def user_status(cls, user, window=None): """ Returns the status of the user based on their past verification attempts @@ -297,32 +305,46 @@ class PhotoVerification(StatusModel): status = 'none' error_msg = '' - if cls.user_is_verified(user): + if cls.user_is_verified(user, window=window): status = 'approved' - elif cls.user_has_valid_or_pending(user): + + elif cls.user_has_valid_or_pending(user, window=window): # user_has_valid_or_pending does include 'approved', but if we are # here, we know that the attempt is still pending status = 'pending' + else: # we need to check the most recent attempt to see if we need to ask them to do # a retry try: - attempts = cls.objects.filter(user=user).order_by('-updated_at') + attempts = cls.objects.filter(user=user, window=window).order_by('-updated_at') attempt = attempts[0] except IndexError: - return ('none', error_msg) + + # If no verification exists for a *midcourse* reverification, then that just + # means the student still needs to reverify. For *original* verifications, + # we return 'none' + if(window): + return('must_reverify', error_msg) + else: + return ('none', error_msg) + if attempt.created_at < cls._earliest_allowed_date(): return ('expired', error_msg) - # right now, this is the only state at which they must reverify. It - # may change later + # If someone is denied their original verification attempt, they can try to reverify. + # However, if a midcourse reverification is denied, that denial is permanent. if attempt.status == 'denied': - status = 'must_reverify' + if window is None: + status = 'must_reverify' + else: + status = 'denied' if attempt.error_msg: error_msg = attempt.parsed_error_msg() return (status, error_msg) + def parsed_error_msg(self): """ Sometimes, the error message we've received needs to be parsed into @@ -374,10 +396,6 @@ class PhotoVerification(StatusModel): self.status = "ready" self.save() - @status_before_must_be("must_retry", "ready", "submitted") - def submit(self): - raise NotImplementedError - @status_before_must_be("must_retry", "submitted", "approved", "denied") def approve(self, user_id=None, service=""): """ @@ -508,6 +526,12 @@ class SoftwareSecurePhotoVerification(PhotoVerification): 3. The encrypted photos are base64 encoded and stored in an S3 bucket that edx-platform does not have read access to. + + Note: this model handles both *inital* verifications (which you must perform + at the time you register for a verified cert), and *midcourse reverifications*. + To distinguish between the two, check the value of the property window: + intial verifications of a window of None, whereas midcourse reverifications + * must always be linked to a specific window*. """ # This is a base64.urlsafe_encode(rsa_encrypt(photo_id_aes_key), ss_pub_key) # So first we generate a random AES-256 key to encrypt our photo ID with. @@ -517,6 +541,43 @@ class SoftwareSecurePhotoVerification(PhotoVerification): IMAGE_LINK_DURATION = 5 * 60 * 60 * 24 # 5 days in seconds + window = models.ForeignKey(MidcourseReverificationWindow, db_index=True, null=True) + + @classmethod + def user_is_reverified_for_all(cls, course_id, user): + """ + Checks to see if the student has successfully reverified for all of the + mandatory re-verification windows associated with a course. + + This is used primarily by the certificate generation code... if the user is + not re-verified for all windows, then they cannot receive a certificate. + """ + all_windows = MidcourseReverificationWindow.objects.filter(course_id=course_id) + # if there are no windows for a course, then return True right off + if (not all_windows): + return True + + for window in all_windows: + try: + # The status of the most recent reverification for each window must be "approved" + # for a student to count as completely reverified + attempts = cls.objects.filter(user=user, window=window).order_by('-updated_at') + attempt = attempts[0] + if attempt.status != "approved": + return False + except: + return False + + return True + + @classmethod + def original_verification(cls, user): + """ + Returns the most current SoftwareSecurePhotoVerification object associated with the user. + """ + query = cls.objects.filter(user=user, window=None).order_by('-updated_at') + return query[0] + @status_before_must_be("created") def upload_face_image(self, img_data): """ @@ -540,6 +601,19 @@ class SoftwareSecurePhotoVerification(PhotoVerification): s3_key = self._generate_s3_key("face") s3_key.set_contents_from_string(encrypt_and_encode(img_data, aes_key)) + @status_before_must_be("created") + def fetch_photo_id_image(self): + """ + Find the user's photo ID image, which was submitted with their original verification. + The image has already been encrypted and stored in s3, so we just need to find that + location + """ + if settings.FEATURES.get('AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'): + return + + self.photo_id_key = self.original_verification(self.user).photo_id_key + self.save() + @status_before_must_be("created") def upload_photo_id_image(self, img_data): """ @@ -715,6 +789,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): return header_txt + "\n\n" + body_txt + def send_request(self): """ Assembles a submission to Software Secure and sends it via HTTPS. @@ -745,210 +820,3 @@ class SoftwareSecurePhotoVerification(PhotoVerification): log.debug("Return message:\n\n{}\n\n".format(response.text)) return response - -class SSPMidcourseReverification(SoftwareSecurePhotoVerification): - """ - Model to re-verify identity using a service provided by Software Secure. - - As of now, it's inheriting a great deal of logic from both `PhotoVerification` - and `SoftwareSecurePhotoVerification`, but it might make more sense to just inherit - from `PhotoVerification`, or maybe not at all... a lot of classes had to get stomped/ - rewritten. Will think about this during CR. - - TODO: another important thing to note during CR: right now we're assuming there's one - window per (user, course) combo. This is UNTRUE in general (there can be many windows - per course, user pair), but we only need ONE window per (user, course) to launch. - Note the user_status methods in particular make this assumption. - - Fix this if time permits... - """ - window = models.ForeignKey(MidcourseReverificationWindow, db_index=True) - - @classmethod - def user_is_reverified_for_all(self, course_id, user): - """ - Checks to see if the student has successfully reverified for all of the - mandatory re-verification windows associated with a course. - - This is used primarily by the certificate generation code... if the user is - not re-verified for all windows, then they cannot receive a certificate. - """ - all_windows = MidcourseReverificationWindow.objects.filter(course_id=course_id) - # TODO check on this - # if there are no windows for a course, then return True right off - if (not all_windows): - return True - for window in all_windows: - try: - # There should be one and only one reverification object per (user, window) - # and the status of that object should be approved - if cls.objects.get(window=window, user=user).status != "approved": - return False - except: - return False - return True - - # TODO does this actually get the original_verification? pretty sure I need to search by date - def original_verification(self): - """ - Returns the most current SoftwareSecurePhotoVerification object associated with the user. - """ - return (SoftwareSecurePhotoVerification.objects.get(user=self.user)) - - # TODO could just call original_verification's _generate_s3_key? - def _generate_original_s3_key(self, prefix): - - #Generates a key into the S3 bucket where the original verification is stored - - #Example: face/4dd1add9-6719-42f7-bea0-115c008c4fca - - conn = S3Connection( - settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["AWS_ACCESS_KEY"], - settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["AWS_SECRET_KEY"] - ) - bucket = conn.get_bucket(settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["S3_BUCKET"]) - - key = Key(bucket) - key.key = "{}/{}".format(prefix, self.original_verification().receipt_id) - - return key - - @status_before_must_be("created") - def fetch_photo_id_image(self): - - #Find the user's photo ID image, which was submitted with their original verification. - #The image has already been encrypted and stored in s3, so we just need to find that - #location - - - if settings.FEATURES.get('AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'): - return - - old_s3_key = self._generate_original_s3_key("face") - new_s3_key = self._generate_s3_key("face") - - original_photo_id = old_s3_key.get_contents_as_string() - - # Unlike upload_face_image, we don't need to encrypt and encode with AES, since that - # was already done when we uploaded it for the initial verification - new_s3_key.set_contents_from_string(original_photo_id) - self.photo_id_key = self.original_verification().photo_id_key - self.save() - - # we replace_photo_id_image with fetch_photo_id_image - @status_before_must_be("created") - def upload_photo_id_image(self, img_data): - raise NotImplementedError - - # TODO right now this does nothing but return must_reverify, fix!!! - @classmethod - def get_status_for_window(cls, user, window): - """ - Returns the status of the user based on their past verification attempts - - If no such verification exists, returns 'must_reverify' - If verification has expired, returns 'expired' --> does this exist for windows? - If the verification has been approved, returns 'approved' - If the verification process is still ongoing, returns 'pending' - If the verification has been denied and the user must resubmit photos, returns 'must_reverify' - """ - reverify_attempt = cls.objects.filter(user=user, window=window) - return "must_reverify" - #if not reverify_attempt: - # return "must_reverify" - #else: - #return reverify_attempt.STATUS - - # can't just inherit the old user_status function, because it's insufficiently specific - # reverifications are unique for a particular (user, window) pair, not just on user - # TODO: Note that a lot of the user_status related stuff is having to get overwritten. - # Does it still make sense to inherit from our parent object(s)? - @classmethod - def user_status(cls, user): - raise NotImplementedError - - @classmethod - def user_status(cls, user, course_id): - """ - Returns the status of the user based on their past verification attempts - - If no such verification exists, returns 'none' - If verification has expired, returns 'expired' - If the verification has been approved, returns 'approved' - If the verification process is still ongoing, returns 'pending' - If the verification has been denied and the user must resubmit photos, returns 'must_reverify' - """ - status = 'none' - error_msg = '' - - if cls.user_is_verified(user): - status = 'approved' - elif cls.user_has_valid_or_pending(user): - # user_has_valid_or_pending does include 'approved', but if we are - # here, we know that the attempt is still pending - status = 'pending' - else: - # we need to check the most recent attempt to see if we need to ask them to do - # a retry - try: - attempts = cls.objects.filter(user=user).order_by('-updated_at') - attempt = attempts[0] - # this is the change for SSPMidcoursePhotoVerification objects - # if there is no verification, we look up course_id, via window, and find out if the user has a verified enrollment - # if verified enrolled in course but no verification: must_reverify - # if not verified enrollment: none - except IndexError: - if CourseEnrollment.objects.filter(user=user, course_id=course_id, mode="verified").exists: - return ('must_reverify', error_msg) - else: - return('none', error_msg) - if attempt.created_at < cls._earliest_allowed_date(): - return ('expired', error_msg) - - # right now, this is the only state at which they must reverify. It - # may change later - if attempt.status == 'denied': - status = 'must_reverify' - if attempt.error_msg: - error_msg = attempt.parsed_error_msg() - - return (status, error_msg) - - # can't inherit - @classmethod - def user_is_verified(cls, user): - raise NotImplementedError - - @classmethod - def user_is_verified(cls, user, course_id): - return cls.objects.filter( - user=user, status="approved", window__course_id=course_id - ).exists() - - # can't inherit - @classmethod - def user_has_valid_or_pending(cls, user): - return NotImplementedError - - # changing this method? - @classmethod - def user_has_valid_or_pending(cls, user, course_id): - valid_statuses = ['submitted', 'approved'] - return cls.objects.filter( - user=user, - window__course_id=course_id, - status__in=valid_statuses, - ).exists() - - # can't inherit - @classmethod - def active_for_user(cls, user): - return NotImplementedError - - @classmethod - def active_for_user(cls, user, course_id): - active_attempts = cls.objects.filter(user=user, status='ready', window__course_id=course_id) - if active_attempts: - return active_attempts[0] - else: - return None diff --git a/lms/djangoapps/verify_student/tests/factories.py b/lms/djangoapps/verify_student/tests/factories.py new file mode 100644 index 0000000000..57f0125eb0 --- /dev/null +++ b/lms/djangoapps/verify_student/tests/factories.py @@ -0,0 +1,19 @@ +""" +verify_student factories +""" +from verify_student.models import MidcourseReverificationWindow +from factory.django import DjangoModelFactory +import pytz +from datetime import timedelta, datetime + + +# Factories don't have __init__ methods, and are self documenting +# pylint: disable=W0232 +class MidcourseReverificationWindowFactory(DjangoModelFactory): + """ Creates a generic MidcourseReverificationWindow. """ + FACTORY_FOR = MidcourseReverificationWindow + + course_id = u'MITx/999/Robot_Super_Course' + # By default this factory creates a window that is currently open + start_date = datetime.now(pytz.UTC) - timedelta(days=100) + end_date = datetime.now(pytz.UTC) + timedelta(days=100) diff --git a/lms/djangoapps/verify_student/tests/test_models.py b/lms/djangoapps/verify_student/tests/test_models.py index fd2b767859..26c4149dc3 100644 --- a/lms/djangoapps/verify_student/tests/test_models.py +++ b/lms/djangoapps/verify_student/tests/test_models.py @@ -1,18 +1,26 @@ # -*- coding: utf-8 -*- -from datetime import timedelta +from datetime import timedelta, datetime import json +from xmodule.modulestore.tests.factories import CourseFactory from nose.tools import ( assert_in, assert_is_none, assert_equals, assert_not_equals, assert_raises, assert_true, assert_false ) from mock import MagicMock, patch +import pytz from django.test import TestCase +from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE +from django.test.utils import override_settings from django.conf import settings import requests import requests.exceptions from student.tests.factories import UserFactory -from verify_student.models import SoftwareSecurePhotoVerification, VerificationException +from verify_student.models import ( + SoftwareSecurePhotoVerification, VerificationException, + MidcourseReverificationWindow, +) +from verify_student.tests.factories import MidcourseReverificationWindowFactory from util.testing import UrlResetMixin import verify_student.models @@ -208,6 +216,23 @@ class TestPhotoVerification(TestCase): return attempt + def test_fetch_photo_id_image(self): + user = UserFactory.create() + orig_attempt = SoftwareSecurePhotoVerification(user=user, window=None) + orig_attempt.save() + + old_key = orig_attempt.photo_id_key + + window = MidcourseReverificationWindowFactory( + course_id="ponies", + start_date=datetime.now(pytz.utc) - timedelta(days=5), + end_date=datetime.now(pytz.utc) + timedelta(days=5) + ) + new_attempt = SoftwareSecurePhotoVerification(user=user, window=window) + new_attempt.save() + new_attempt.fetch_photo_id_image() + assert_equals(new_attempt.photo_id_key, old_key) + def test_submissions(self): """Test that we set our status correctly after a submission.""" # Basic case, things go well. @@ -362,3 +387,146 @@ class TestPhotoVerification(TestCase): attempt.error_msg = msg parsed_error_msg = attempt.parsed_error_msg() self.assertEquals(parsed_error_msg, "There was an error verifying your ID photos.") + + +@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) +class TestMidcourseReverificationWindow(TestCase): + """ Tests for MidcourseReverificationWindow objects """ + def setUp(self): + self.course_id = "MITx/999/Robot_Super_Course" + CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course') + + def test_window_open_for_course(self): + # Should return False if no windows exist for a course + self.assertFalse(MidcourseReverificationWindow.window_open_for_course(self.course_id)) + + # Should return False if a window exists, but it's not in the current timeframe + MidcourseReverificationWindowFactory( + course_id=self.course_id, + start_date=datetime.now(pytz.utc) - timedelta(days=10), + end_date=datetime.now(pytz.utc) - timedelta(days=5) + ) + self.assertFalse(MidcourseReverificationWindow.window_open_for_course(self.course_id)) + + # Should return True if a non-expired window exists + MidcourseReverificationWindowFactory( + course_id=self.course_id, + start_date=datetime.now(pytz.utc) - timedelta(days=3), + end_date=datetime.now(pytz.utc) + timedelta(days=3) + ) + self.assertTrue(MidcourseReverificationWindow.window_open_for_course(self.course_id)) + + def test_get_window(self): + # if no window exists, returns None + self.assertIsNone(MidcourseReverificationWindow.get_window(self.course_id, datetime.now(pytz.utc))) + + # we should get the expected window otherwise + window_valid = MidcourseReverificationWindowFactory( + course_id=self.course_id, + start_date=datetime.now(pytz.utc) - timedelta(days=3), + end_date=datetime.now(pytz.utc) + timedelta(days=3) + ) + self.assertEquals( + window_valid, + MidcourseReverificationWindow.get_window(self.course_id, datetime.now(pytz.utc)) + ) + + +@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) +@patch.dict(settings.VERIFY_STUDENT, FAKE_SETTINGS) +@patch('verify_student.models.S3Connection', new=MockS3Connection) +@patch('verify_student.models.Key', new=MockKey) +@patch('verify_student.models.requests.post', new=mock_software_secure_post) +class TestMidcourseReverification(TestCase): + def setUp(self): + self.course_id = "MITx/999/Robot_Super_Course" + self.course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course') + self.user = UserFactory.create() + + def test_user_is_reverified_for_all(self): + + # if there are no windows for a course, this should return True + self.assertTrue(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course_id, self.user)) + + # first, make three windows + window1 = MidcourseReverificationWindowFactory( + course_id=self.course_id, + start_date=datetime.now(pytz.UTC) - timedelta(days=15), + end_date=datetime.now(pytz.UTC) - timedelta(days=13), + ) + + window2 = MidcourseReverificationWindowFactory( + course_id=self.course_id, + start_date=datetime.now(pytz.UTC) - timedelta(days=10), + end_date=datetime.now(pytz.UTC) - timedelta(days=8), + ) + + window3 = MidcourseReverificationWindowFactory( + course_id=self.course_id, + start_date=datetime.now(pytz.UTC) - timedelta(days=5), + end_date=datetime.now(pytz.UTC) - timedelta(days=3), + ) + + # make two SSPMidcourseReverifications for those windows + attempt1 = SoftwareSecurePhotoVerification( + status="approved", + user=self.user, + window=window1 + ) + attempt1.save() + + attempt2 = SoftwareSecurePhotoVerification( + status="approved", + user=self.user, + window=window2 + ) + attempt2.save() + + # should return False because only 2 of 3 windows have verifications + self.assertFalse(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course_id, self.user)) + + attempt3 = SoftwareSecurePhotoVerification( + status="must_retry", + user=self.user, + window=window3 + ) + attempt3.save() + + # should return False because the last verification exists BUT is not approved + self.assertFalse(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course_id, self.user)) + + attempt3.status = "approved" + attempt3.save() + + # should now return True because all windows have approved verifications + self.assertTrue(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course_id, self.user)) + + def test_original_verification(self): + orig_attempt = SoftwareSecurePhotoVerification(user=self.user) + orig_attempt.save() + window = MidcourseReverificationWindowFactory( + course_id=self.course_id, + start_date=datetime.now(pytz.UTC) - timedelta(days=15), + end_date=datetime.now(pytz.UTC) - timedelta(days=13), + ) + midcourse_attempt = SoftwareSecurePhotoVerification(user=self.user, window=window) + self.assertEquals(midcourse_attempt.original_verification(user=self.user), orig_attempt) + + def test_user_has_valid_or_pending(self): + window = MidcourseReverificationWindowFactory( + course_id=self.course_id, + start_date=datetime.now(pytz.UTC) - timedelta(days=15), + end_date=datetime.now(pytz.UTC) - timedelta(days=13), + ) + + attempt = SoftwareSecurePhotoVerification(status="must_retry", user=self.user, window=window) + attempt.save() + + assert_false(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user=self.user, window=window)) + + attempt.status = "approved" + attempt.save() + assert_true(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user=self.user, window=window)) + + def test_active_for_user(self): + pass diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index c14f41d87b..a6dd07b489 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -21,9 +21,11 @@ from django.core.exceptions import ObjectDoesNotExist from xmodule.modulestore.tests.factories import CourseFactory from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE from student.tests.factories import UserFactory +from student.models import CourseEnrollment from course_modes.models import CourseMode from verify_student.views import render_to_response from verify_student.models import SoftwareSecurePhotoVerification +from verify_student.tests.factories import MidcourseReverificationWindowFactory def mock_render_to_response(*args, **kwargs): @@ -80,6 +82,8 @@ class TestReverifyView(TestCase): def setUp(self): self.user = UserFactory.create(username="rusty", password="test") self.client.login(username="rusty", password="test") + self.course_id = "MITx/999/Robot_Super_Course" + self.course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course') @patch('verify_student.views.render_to_response', render_mock) def test_reverify_get(self): @@ -110,3 +114,61 @@ class TestReverifyView(TestCase): self.assertIsNotNone(verification_attempt) except ObjectDoesNotExist: self.fail('No verification object generated') + self.assertIn('photo_reverification', template) + self.assertTrue(context['error']) + + @patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True}) + def test_reverify_post_success(self): + url = reverse('verify_student_reverify') + response = self.client.post(url, {'face_image': ',', + 'photo_id_image': ','}) + self.assertEquals(response.status_code, 302) + try: + verification_attempt = SoftwareSecurePhotoVerification.objects.get(user=self.user) + self.assertIsNotNone(verification_attempt) + except ObjectDoesNotExist: + self.fail('No verification object generated') + + +@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) +class TestMidCourseReverifyView(TestCase): + def setUp(self): + self.user = UserFactory.create(username="rusty", password="test") + self.client.login(username="rusty", password="test") + self.course_id = 'Robot/999/Test_Course' + CourseFactory.create(org='Robot', number='999', display_name='Test Course') + + @patch('verify_student.views.render_to_response', render_mock) + def test_midcourse_reverify_get(self): + url = reverse('verify_student_midcourse_reverify', + kwargs={"course_id": self.course_id}) + response = self.client.get(url) + self.assertEquals(response.status_code, 200) + ((_template, context), _kwargs) = render_mock.call_args + self.assertFalse(context['error']) + + @patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True}) + def test_midcourse_reverify_post_success(self): + url = reverse('verify_student_midcourse_reverify', kwargs={'course_id': self.course_id}) + response = self.client.post(url, {'face_image': ','}) + self.assertEquals(response.status_code, 302) + try: + verification_attempt = SoftwareSecurePhotoVerification.objects.get(user=self.user) + self.assertIsNotNone(verification_attempt) + except ObjectDoesNotExist: + self.fail('No verification object generated') + + # TODO make this test more detailed + @patch('verify_student.views.render_to_response', render_mock) + def test_midcourse_reverify_dash(self): + url = reverse('verify_student_midcourse_reverify_dash') + response = self.client.get(url) + # not enrolled in any courses + self.assertEquals(response.status_code, 200) + + enrollment = CourseEnrollment.get_or_create_enrollment(self.user, self.course_id) + enrollment.update_enrollment(mode="verified", is_active=True) + MidcourseReverificationWindowFactory(course_id=self.course_id) + response = self.client.get(url) + # enrolled in a verified course, and the window is open + self.assertEquals(response.status_code, 200) diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index d972441d4d..283d03fa64 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -30,9 +30,10 @@ from shoppingcart.processors.CyberSource import ( get_signed_purchase_params, get_purchase_endpoint ) from verify_student.models import ( - SoftwareSecurePhotoVerification, MidcourseReverificationWindow, SSPMidcourseReverification + SoftwareSecurePhotoVerification, MidcourseReverificationWindow, ) import ssencrypt +from xmodule.modulestore.exceptions import ItemNotFoundError log = logging.getLogger(__name__) @@ -326,6 +327,7 @@ class ReverifyView(View): } return render_to_response("verify_student/photo_reverification.html", context) + class MidCourseReverifyView(View): """ The mid-course reverification view. @@ -341,10 +343,16 @@ class MidCourseReverifyView(View): """ display this view """ + course = course_from_id(course_id) context = { "user_full_name": request.user.profile.name, "error": False, "course_id": course_id, + "course_name": course.display_name_with_default, + "course_org": course.display_org_with_default, + "course_num": course.display_number_with_default, + "reverify": True, + } return render_to_response("verify_student/midcourse_photo_reverification.html", context) @@ -356,7 +364,7 @@ class MidCourseReverifyView(View): try: # TODO look at this more carefully! #1 testing candidate now = datetime.datetime.now(UTC) - attempt = SSPMidcourseReverification(user=request.user, window=MidcourseReverificationWindow.get_window(course_id, now)) + attempt = SoftwareSecurePhotoVerification(user=request.user, window=MidcourseReverificationWindow.get_window(course_id, now)) b64_face_image = request.POST['face_image'].split(",")[1] attempt.upload_face_image(b64_face_image.decode('base64')) @@ -376,7 +384,12 @@ class MidCourseReverifyView(View): } return render_to_response("verify_student/midcourse_photo_reverification.html", context) + def midcourse_reverify_dash(_request): + """ + Shows the "course reverification dashboard", which displays the reverification status (must reverify, + pending, approved, failed, etc) of all courses in which a student has a verified enrollment. + """ # TODO same comment as in student/views.py: need to factor out this functionality user = _request.user course_enrollment_pairs = [] @@ -397,13 +410,13 @@ def midcourse_reverify_dash(_request): "must_reverify" ) ) - prompt_midcourse_reverify = True context = { "user_full_name": _request.user.profile.name, "reverify_course_data": reverify_course_data, } return render_to_response("verify_student/midcourse_reverify_dash.html", context) + @login_required def reverification_submission_confirmation(_request): """ @@ -411,6 +424,7 @@ def reverification_submission_confirmation(_request): """ return render_to_response("verify_student/reverification_confirmation.html") + @login_required def midcourse_reverification_confirmation(_request): """ diff --git a/lms/static/js/verify_student/photocapturebasic2.js b/lms/static/js/verify_student/photocapturebasic2.js index 0ca03aca4d..7bf34582cd 100644 --- a/lms/static/js/verify_student/photocapturebasic2.js +++ b/lms/static/js/verify_student/photocapturebasic2.js @@ -1,4 +1,3 @@ -// TODO diff this against photocapture.js, see if I actually needed a whole honking new file var onVideoFail = function(e) { if(e == 'NO_DEVICES_FOUND') { $('#no-webcam').show(); @@ -25,7 +24,7 @@ var submitReverificationPhotos = function() { name: 'face_image', value: $("#face_image")[0].src, }).appendTo("#reverify_form"); - + // there is a change here $("#reverify_form").submit(); } @@ -48,6 +47,7 @@ var submitToPaymentProcessing = function() { "course_id" : course_id, "contribution": contribution, "face_image" : $("#face_image")[0].src, + // there is a change here }, function(data) { for (prop in data) { diff --git a/lms/static/sass/application-extend1.scss.mako b/lms/static/sass/application-extend1.scss.mako index 310abf9f2d..cb993e14b6 100644 --- a/lms/static/sass/application-extend1.scss.mako +++ b/lms/static/sass/application-extend1.scss.mako @@ -12,8 +12,8 @@ // base - utilities @import 'base/reset'; -@import 'base/mixins'; @import 'base/variables'; +@import 'base/mixins'; ## THEMING ## ------- diff --git a/lms/static/sass/application-extend2.scss.mako b/lms/static/sass/application-extend2.scss.mako index 9473a41e4a..cfb6a1b698 100644 --- a/lms/static/sass/application-extend2.scss.mako +++ b/lms/static/sass/application-extend2.scss.mako @@ -12,8 +12,8 @@ // base - utilities @import 'base/reset'; -@import 'base/mixins'; @import 'base/variables'; +@import 'base/mixins'; ## THEMING ## ------- @@ -41,6 +41,7 @@ // base - elements @import 'elements/typography'; @import 'elements/controls'; +@import 'elements/system-feedback'; // base - specific views @import 'views/verification'; diff --git a/lms/static/sass/application.scss.mako b/lms/static/sass/application.scss.mako index 7d6da444ce..5365ec52ac 100644 --- a/lms/static/sass/application.scss.mako +++ b/lms/static/sass/application.scss.mako @@ -11,8 +11,8 @@ // base - utilities @import 'base/reset'; -@import 'base/mixins'; @import 'base/variables'; +@import 'base/mixins'; ## THEMING ## ------- diff --git a/lms/static/sass/base/_mixins.scss b/lms/static/sass/base/_mixins.scss index 5a5a4fde94..22a1d96dbd 100644 --- a/lms/static/sass/base/_mixins.scss +++ b/lms/static/sass/base/_mixins.scss @@ -54,6 +54,30 @@ // ==================== + +// extends - UI - used for page/view-level wrappers (for centering/grids) +%ui-wrapper { + @include clearfix(); + @include box-sizing(border-box); + width: 100%; +} + +// extends - UI - window +%ui-window { + @include clearfix(); + border-radius: 3px; + box-shadow: 0 1px 2px 1px $shadow-l1; + margin-bottom: $baseline; + border: 1px solid $light-gray; + background: $white; +} + +// extends - UI archetypes - well +%ui-well { + box-shadow: inset 0 1px 2px 1px $shadow-l1; + padding: ($baseline*0.75) $baseline; +} + // extends - UI - visual link %ui-fake-link { cursor: pointer; diff --git a/lms/static/sass/base/_variables.scss b/lms/static/sass/base/_variables.scss index 5dda2071b5..dff281e1d5 100644 --- a/lms/static/sass/base/_variables.scss +++ b/lms/static/sass/base/_variables.scss @@ -308,3 +308,8 @@ $video-thumb-url: '../images/courses/video-thumb.jpg'; $f-serif: 'Bree Serif', Georgia, Cambria, 'Times New Roman', Times, serif; $f-sans-serif: 'Open Sans','Helvetica Neue', Helvetica, Arial, sans-serif; $f-monospace: 'Bitstream Vera Sans Mono', Consolas, Courier, monospace; + +// SPLINT: colors + +$msg-bg: $action-primary-bg; + diff --git a/lms/static/sass/course.scss.mako b/lms/static/sass/course.scss.mako index 4bd8cfe4bd..2cdd1c2031 100644 --- a/lms/static/sass/course.scss.mako +++ b/lms/static/sass/course.scss.mako @@ -2,8 +2,8 @@ @import 'base/reset'; @import 'base/font_face'; -@import 'base/mixins'; @import 'base/variables'; +@import 'base/mixins'; ## THEMING ## ------- diff --git a/lms/static/sass/elements/_system-feedback.scss b/lms/static/sass/elements/_system-feedback.scss new file mode 100644 index 0000000000..5ee7cc591b --- /dev/null +++ b/lms/static/sass/elements/_system-feedback.scss @@ -0,0 +1,74 @@ +// lms - elements - system feedback +// ==================== + +// messages + +// UI : message +.wrapper-msg { + box-shadow: 0 0 5px $action-primary-shadow inset; + margin-bottom: ($baseline*1.5); + padding: $baseline ($baseline*1.5); + background: $action-primary-bg; + + .msg { + @include clearfix(); + max-width: grid-width(12); + min-width: 760px; + width: flex-grid(12); + margin: 0 auto; + } + + .msg-content, + .msg-icon { + display: inline-block; + vertical-align: middle; + } + + .msg-content { + width: flex-grid(10,12); + + .title { + @extend %t-title5; + @extend %t-weight4; + margin-bottom: ($baseline/4); + color: $white; + text-transform: none; + letter-spacing: 0; + } + + .copy { + @extend %t-copy-sub1; + color: $white; + + p { // nasty reset + @extend %t-copy-sub1; + color: $white; + } + } + } + + .has-actions { + + .msg-content { + width: flex-grid(10,12); + } + + .nav-actions { + width: flex-grid(2,12); + display: inline-block; + vertical-align: middle; + + .action-primary { + @extend %btn-primary-green; + } + } + } + + +} + +// prompts + +// notifications + +// alerts diff --git a/lms/static/sass/views/_verification.scss b/lms/static/sass/views/_verification.scss index 9bd1274928..1cde75de14 100644 --- a/lms/static/sass/views/_verification.scss +++ b/lms/static/sass/views/_verification.scss @@ -1,87 +1,6 @@ // lms - views - verification flow // ==================== -// MISC: extends - type -// application: canned headings -%hd-lv1 { - @extend %t-title1; - @extend %t-weight1; - color: $m-gray-d4; - margin: 0 0 ($baseline*2) 0; -} - -%hd-lv2 { - @extend %t-title4; - @extend %t-weight1; - margin: 0 0 ($baseline*0.75) 0; - border-bottom: 1px solid $m-gray-l4; - padding-bottom: ($baseline/2); - color: $m-gray-d4; -} - -%hd-lv3 { - @extend %t-title6; - @extend %t-weight4; - margin: 0 0 ($baseline/4) 0; - color: $m-gray-d4; -} - -%hd-lv4 { - @extend %t-title6; - @extend %t-weight2; - margin: 0 0 $baseline 0; - color: $m-gray-d4; -} - -%hd-lv5 { - @extend %t-title7; - @extend %t-weight4; - margin: 0 0 ($baseline/4) 0; - color: $m-gray-d4; -} - -// application: canned copy -%copy-base { - @extend %t-copy-base; - color: $m-gray-d2; -} - -%copy-lead1 { - @extend %t-copy-lead2; - color: $m-gray; -} - -%copy-detail { - @extend %t-copy-sub1; - @extend %t-weight3; - color: $m-gray-d1; -} - -%copy-metadata { - @extend %t-copy-sub2; - color: $m-gray-d1; - - - %copy-metadata-value { - @extend %t-weight2; - } - - %copy-metadata-value { - @extend %t-weight4; - } -} - -// application: canned links -%copy-link { - border-bottom: 1px dotted transparent; - - &:hover, &:active { - border-color: $link-color-d1; - } -} - -// ==================== - // MISC: extends - button %btn-verify-primary { @extend %btn-primary-green; @@ -89,26 +8,6 @@ // ==================== -// MISC: extends - UI - window -%ui-window { - @include clearfix(); - border-radius: ($baseline/10); - box-shadow: 0 1px 2px 1px $shadow-l1; - margin-bottom: $baseline; - border: 1px solid $m-gray-l3; - background: $white; -} - -// ==================== - -// MISC: extends - UI - well -%ui-well { - box-shadow: inset 0 1px 2px 1px $shadow-l1; - padding: ($baseline*0.75) $baseline; -} - -// ==================== - // MISC: expandable UI .is-expandable { @@ -153,7 +52,8 @@ // ==================== // VIEW: all verification steps -.verification-process { +.verification-process, +.midcourse-reverification-process { // reset: box-sizing (making things so right its scary) * { @@ -1894,6 +1794,335 @@ } } } + + // VIEW: midcourse re-verification + &.midcourse-reverification-process { + + // step-dash + .action-reverify { + @extend %btn-verify-primary; + padding: ($baseline/2) ($baseline*0.75); + } + + .reverification-table { + width: 100%; + + th { + display: none; + } + + th, + td { + padding: ($baseline/2) 0; + text-align: left; + border-bottom: 1px solid $light-gray; + } + + .course-name { + @extend %t-title5; + display: block; + font-weight: bold; + } + + .deadline { + @extend %copy-detail; + display: block; + } + } + + + .wrapper-reverification-help { + margin-top: $baseline*2; + + .faq-item { + display: inline-block; + vertical-align: top; + width: flex-grid(4,12); + padding-right: $baseline; + + &:last-child { + padding-right: 0; + } + + .faq-answer { + @extend %t-copy-sub1; + } + } + } + + // step-photos + .wrapper-task { + @include clearfix(); + width: flex-grid(12,12); + margin: $baseline 0; + + .wrapper-help { + float: right; + width: flex-grid(6,12); + padding: 0 $baseline; + + .help { + margin-bottom: ($baseline*1.5); + + &:last-child { + margin-bottom: 0; + } + + .title { + @extend %hd-lv3; + } + + .copy { + @extend %copy-detail; + } + + .example { + color: $m-gray-l2; + } + + // help - general list + .list-help { + margin-top: ($baseline/2); + color: $black; + + .help-item { + margin-bottom: ($baseline/4); + border-bottom: 1px solid $m-gray-l4; + padding-bottom: ($baseline/4); + + &:last-child { + margin-bottom: 0; + border-bottom: none; + padding-bottom: 0; + } + } + + .help-item-emphasis { + @extend %t-weight4; + } + } + + // help - faq + .list-faq { + margin-bottom: $baseline; + } + } + } + + .task { + @extend %ui-window; + float: left; + width: flex-grid(6,12); + margin-right: flex-gutter(); + } + + .controls { + padding: ($baseline*0.75) $baseline; + background: $m-gray-l4; + + .list-controls { + position: relative; + } + + .control { + position: absolute; + + .action { + @extend %btn-primary-blue; + padding: ($baseline/2) ($baseline*0.75); + + *[class^="icon-"] { + @extend %t-icon4; + padding: ($baseline*.25) ($baseline*.5); + display: block; + } + } + + // STATE: hidden + &.is-hidden { + visibility: hidden; + } + + // STATE: shown + &.is-shown { + visibility: visible; + } + + // STATE: approved + &.approved { + + .action { + @extend %btn-verify-primary; + padding: ($baseline/2) ($baseline*0.75); + } + } + } + + // control - redo + .control-redo { + position: absolute; + left: ($baseline/2); + } + + // control - take/do + .control-do { + left: 45%; + } + + // control - approve + .control-approve { + position: absolute; + right: ($baseline/2); + } + } + + .msg { + @include clearfix(); + margin-top: ($baseline*2); + + .copy { + float: left; + width: flex-grid(8,12); + margin-right: flex-gutter(); + } + + .list-actions { + position: relative; + top: -($baseline/2); + float: left; + width: flex-grid(4,12); + text-align: right; + + .action-retakephotos a { + @extend %btn-primary-blue; + @include font-size(14); + padding: ($baseline/2) ($baseline*.75); + } + } + } + + .msg-followup { + border-top: ($baseline/10) solid $m-gray-t0; + padding-top: $baseline; + } + } + + + .review-task { + margin-bottom: ($baseline*1.5); + padding: ($baseline*0.75) $baseline; + border-radius: ($baseline/10); + background: $m-gray-l4; + + &:last-child { + margin-bottom: 0; + } + + > .title { + @extend %hd-lv3; + } + + .copy { + @extend %copy-base; + + strong { + @extend %t-weight5; + color: $m-gray-d4; + } + } + } + + + // individual task - name + .review-task-name { + @include clearfix(); + + .copy { + float: left; + width: flex-grid(8,12); + margin-right: flex-gutter(); + } + + .list-actions { + position: relative; + top: -($baseline); + float: left; + width: flex-grid(4,12); + text-align: right; + + .action-editname a { + @extend %btn-primary-blue; + @include font-size(14); + padding: ($baseline/2) ($baseline*.75); + } + } + } + + .nav-wizard { + padding: ($baseline*.75) $baseline; + + .prompt-verify { + float: left; + width: flex-grid(6,12); + margin: 0 flex-gutter() 0 0; + + .title { + @extend %hd-lv4; + margin-bottom: ($baseline/4); + } + + .copy { + @extend %t-copy-sub1; + @extend %t-weight3; + } + + .list-actions { + margin-top: ($baseline/2); + } + + .action-verify label { + @extend %t-copy-sub1; + } + } + + .wizard-steps { + margin-top: ($baseline/2); + + .wizard-step { + margin-right: flex-gutter(); + display: inline-block; + vertical-align: middle; + + &:last-child { + margin-right: 0; + } + } + } + } + + + .modal { + + fieldset { + margin-top: $baseline; + } + + .close-modal { + @include font-size(24); + color: $m-blue-d3; + + &:hover, &:focus { + color: $m-blue-d1; + border: none; + } + } + } + + } + + + + } // ==================== diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html index 397c73fff0..dc75b3c59b 100644 --- a/lms/templates/dashboard.html +++ b/lms/templates/dashboard.html @@ -152,14 +152,17 @@ -
- - %if prompt_midcourse_reverify: -
+ +%if reverify_course_data: +
+
<%include file='dashboard/_dashboard_prompt_midcourse_reverify.html' /> -
- % endif + +
+% endif + +
%if message:
diff --git a/lms/templates/dashboard/_dashboard_prompt_midcourse_reverify.html b/lms/templates/dashboard/_dashboard_prompt_midcourse_reverify.html index b0c3bb8fa0..51e0da3427 100644 --- a/lms/templates/dashboard/_dashboard_prompt_midcourse_reverify.html +++ b/lms/templates/dashboard/_dashboard_prompt_midcourse_reverify.html @@ -2,12 +2,24 @@ <%! from django.core.urlresolvers import reverse %> -% if prompt_midcourse_reverify: -

${_("You need to re-verify to continue")}

-% for course_id, course_name, date, status in reverify_course_data: -

- ${_('To continue in the verified track in {course_name}, you need to re-verify your identity by {date}.').format(course_name=course_name, date=date)} - Click here to re-verify. -

+% if reverify_course_data: +
+
+

${_("You need to re-verify to continue")}

+ % for course_id, course_name, date, status in reverify_course_data: +
+

+ ${_('To continue in the verified track in {course_name}, you need to re-verify your identity by {date}.').format(course_name=course_name, date=date)} +

+
+
+ +
+ % endfor %endif diff --git a/lms/templates/verify_student/_reverification_support.html b/lms/templates/verify_student/_reverification_support.html index 44bf0c89d8..4e6bda98ff 100644 --- a/lms/templates/verify_student/_reverification_support.html +++ b/lms/templates/verify_student/_reverification_support.html @@ -6,12 +6,7 @@
  • ${_("Why Do I Need to Re-Verify?")}

    -

    ${_("There was a problem with your original verification. To make sure that your identity is correctly associated with your course progress, we need to retake your photo and a photo of your identification document. If you don't have a valid identification document, contact {link_start}{support_email}{link_end}.").format( - support_email=settings.DEFAULT_FEEDBACK_EMAIL, - link_start=u''.format( - address=settings.DEFAULT_FEEDBACK_EMAIL, - subject_line=_('Problem with ID re-verification')), - link_end=u'')}

    +

    ${_("At key points in a course, the professor will ask you to re-verify your identity. We will send the new photo to be matched up with the photo of the original ID you submitted when you signed up for the course.")}

  • diff --git a/lms/templates/verify_student/_verification_header.html b/lms/templates/verify_student/_verification_header.html index 80a1e939b0..682652b870 100644 --- a/lms/templates/verify_student/_verification_header.html +++ b/lms/templates/verify_student/_verification_header.html @@ -4,6 +4,8 @@

    %if upgrade: ${_("You are upgrading your registration for")} + %elif reverify: + ${_("You are re-verifying for")} %else: ${_("You are registering for")} %endif @@ -19,6 +21,8 @@ %if upgrade: ${_("Upgrading to:")} ${_("ID Verified")} + %elif reverify: + ${_("Re-verifying for:")} ${_("ID Verified")} %else: ${_("Registering as: ")} ${_("ID Verified")} %endif diff --git a/lms/templates/verify_student/midcourse_photo_reverification.html b/lms/templates/verify_student/midcourse_photo_reverification.html index 98c464329d..ac06e65234 100644 --- a/lms/templates/verify_student/midcourse_photo_reverification.html +++ b/lms/templates/verify_student/midcourse_photo_reverification.html @@ -3,8 +3,8 @@ <%inherit file="../main.html" /> <%namespace name='static' file='/static_content.html'/> -<%block name="bodyclass">register verification-process is-not-verified step-photos -<%block name="title">${_("Re-Verification")} +<%block name="bodyclass">midcourse-reverification-process is-not-verified step-photos register +<%block name="title">${_("Re-Verify | edX")} <%block name="js_extra"> @@ -56,20 +56,13 @@
    -
    -
    -
    -

    ${_("You are re-verifying your identity")}

    -
    - - -
    -
    -
    -

    + +