@@ -351,7 +351,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
def test_create_static_tab_and_rename(self):
|
||||
module_store = modulestore('direct')
|
||||
CourseFactory.create(org='edX', course='999', display_name='Robot Super Course')
|
||||
course_location = Location(['i4x', 'edX', '999', 'course', 'Robot_Super_Course', None])
|
||||
course_location = Location(['i4x', 'edX', '999', 'course', 'Robot_Super_Course', None])
|
||||
|
||||
item = ItemFactory.create(parent_location=course_location, category='static_tab', display_name="My Tab")
|
||||
|
||||
@@ -733,7 +733,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
|
||||
# we want to assert equality between the objects, but we know the locations
|
||||
# differ, so just make them equal for testing purposes
|
||||
source_item.scope_ids = source_item.scope_ids._replace(def_id=new_loc, usage_id=new_loc)
|
||||
source_item.location = new_loc
|
||||
if hasattr(source_item, 'data') and hasattr(lookup_item, 'data'):
|
||||
self.assertEqual(source_item.data, lookup_item.data)
|
||||
|
||||
@@ -914,8 +914,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
depth=1
|
||||
)
|
||||
# We had a bug where orphaned draft nodes caused export to fail. This is here to cover that case.
|
||||
draft_loc = mongo.draft.as_draft(vertical.location.replace(name='no_references'))
|
||||
vertical.scope_ids = vertical.scope_ids._replace(def_id=draft_loc, usage_id=draft_loc)
|
||||
vertical.location = mongo.draft.as_draft(vertical.location.replace(name='no_references'))
|
||||
|
||||
draft_store.save_xmodule(vertical)
|
||||
orphan_vertical = draft_store.get_item(vertical.location)
|
||||
@@ -933,8 +932,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
root_dir = path(mkdtemp_clean())
|
||||
|
||||
# now create a new/different private (draft only) vertical
|
||||
draft_loc = mongo.draft.as_draft(Location(['i4x', 'edX', 'toy', 'vertical', 'a_private_vertical', None]))
|
||||
vertical.scope_ids = vertical.scope_ids._replace(def_id=draft_loc, usage_id=draft_loc)
|
||||
vertical.location = mongo.draft.as_draft(Location(['i4x', 'edX', 'toy', 'vertical', 'a_private_vertical', None]))
|
||||
draft_store.save_xmodule(vertical)
|
||||
private_vertical = draft_store.get_item(vertical.location)
|
||||
vertical = None # blank out b/c i destructively manipulated its location 2 lines above
|
||||
@@ -983,7 +981,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.assertEqual(on_disk['course/2012_Fall'], own_metadata(course))
|
||||
|
||||
# remove old course
|
||||
delete_course(module_store, content_store, location)
|
||||
delete_course(module_store, content_store, location, commit=True)
|
||||
|
||||
# reimport
|
||||
import_from_xml(module_store, root_dir, ['test_export'], draft_store=draft_store)
|
||||
|
||||
@@ -42,7 +42,7 @@ def wrap_draft(item):
|
||||
non-draft location in either case
|
||||
"""
|
||||
setattr(item, 'is_draft', item.location.revision == DRAFT)
|
||||
item.scope_ids = item.scope_ids._replace(usage_id=item.location.replace(revision=None))
|
||||
item.location = item.location.replace(revision=None)
|
||||
return item
|
||||
|
||||
|
||||
|
||||
@@ -110,27 +110,19 @@ def _clone_modules(modulestore, modules, source_location, dest_location):
|
||||
original_loc = Location(module.location)
|
||||
|
||||
if original_loc.category != 'course':
|
||||
new_location = module.location._replace(
|
||||
module.location = module.location._replace(
|
||||
tag=dest_location.tag,
|
||||
org=dest_location.org,
|
||||
course=dest_location.course
|
||||
)
|
||||
module.scope_ids = module.scope_ids._replace(
|
||||
def_id=new_location,
|
||||
usage_id=new_location
|
||||
)
|
||||
else:
|
||||
# on the course module we also have to update the module name
|
||||
new_location = module.location._replace(
|
||||
module.location = module.location._replace(
|
||||
tag=dest_location.tag,
|
||||
org=dest_location.org,
|
||||
course=dest_location.course,
|
||||
name=dest_location.name
|
||||
)
|
||||
module.scope_ids = module.scope_ids._replace(
|
||||
def_id=new_location,
|
||||
usage_id=new_location
|
||||
)
|
||||
|
||||
print "Cloning module {0} to {1}....".format(original_loc, module.location)
|
||||
|
||||
|
||||
@@ -375,30 +375,22 @@ def remap_namespace(module, target_location_namespace):
|
||||
# This looks a bit wonky as we need to also change the 'name' of the imported course to be what
|
||||
# the caller passed in
|
||||
if module.location.category != 'course':
|
||||
new_location = module.location._replace(
|
||||
module.location = module.location._replace(
|
||||
tag=target_location_namespace.tag,
|
||||
org=target_location_namespace.org,
|
||||
course=target_location_namespace.course
|
||||
)
|
||||
module.scope_ids = module.scope_ids._replace(
|
||||
def_id=new_location,
|
||||
usage_id=new_location
|
||||
)
|
||||
else:
|
||||
original_location = module.location
|
||||
#
|
||||
# module is a course module
|
||||
#
|
||||
new_location = module.location._replace(
|
||||
module.location = module.location._replace(
|
||||
tag=target_location_namespace.tag,
|
||||
org=target_location_namespace.org,
|
||||
course=target_location_namespace.course,
|
||||
name=target_location_namespace.name
|
||||
)
|
||||
module.scope_ids = module.scope_ids._replace(
|
||||
def_id=new_location,
|
||||
usage_id=new_location
|
||||
)
|
||||
#
|
||||
# There is more re-namespacing work we have to do when importing course modules
|
||||
#
|
||||
|
||||
@@ -94,9 +94,9 @@ class MockPeerGradingService(object):
|
||||
'success': True,
|
||||
'submission_id': 1,
|
||||
'submission_key': "",
|
||||
'student_response': 'fake student response',
|
||||
'prompt': 'fake submission prompt',
|
||||
'rubric': 'fake rubric',
|
||||
'student_response': 'Sample student response.',
|
||||
'prompt': 'Sample submission prompt.',
|
||||
'rubric': 'Placeholder text for the full rubric.',
|
||||
'max_score': 4
|
||||
}
|
||||
|
||||
@@ -110,9 +110,9 @@ class MockPeerGradingService(object):
|
||||
return {'success': True,
|
||||
'submission_id': 1,
|
||||
'submission_key': '',
|
||||
'student_response': 'fake student response',
|
||||
'prompt': 'fake submission prompt',
|
||||
'rubric': 'fake rubric',
|
||||
'student_response': 'Sample student response.',
|
||||
'prompt': 'Sample submission prompt.',
|
||||
'rubric': 'Placeholder text for the full rubric.',
|
||||
'max_score': 4}
|
||||
|
||||
def save_calibration_essay(self, **kwargs):
|
||||
|
||||
@@ -8,7 +8,7 @@ from pkg_resources import resource_string
|
||||
from .capa_module import ComplexEncoder
|
||||
from .x_module import XModule
|
||||
from xmodule.raw_module import RawDescriptor
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError, NoPathToItem
|
||||
from .timeinfo import TimeInfo
|
||||
from xblock.fields import Dict, String, Scope, Boolean, Float
|
||||
from xmodule.fields import Date, Timedelta
|
||||
@@ -72,6 +72,12 @@ class PeerGradingFields(object):
|
||||
scope=Scope.content
|
||||
)
|
||||
|
||||
class InvalidLinkLocation(Exception):
|
||||
"""
|
||||
Exception for the case in which a peer grading module tries to link to an invalid location.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class PeerGradingModule(PeerGradingFields, XModule):
|
||||
"""
|
||||
@@ -103,11 +109,21 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
|
||||
if self.use_for_single_location:
|
||||
try:
|
||||
self.linked_problem = self.system.get_module(self.link_to_location)
|
||||
linked_descriptors = self.descriptor.get_required_module_descriptors()
|
||||
if len(linked_descriptors) == 0:
|
||||
error_msg = "Peer grading module {0} is trying to use single problem mode without "
|
||||
"a location specified.".format(self.location)
|
||||
log.error(error_msg)
|
||||
raise InvalidLinkLocation(error_msg)
|
||||
self.linked_problem = self.system.get_module(linked_descriptors[0])
|
||||
except ItemNotFoundError:
|
||||
log.error("Linked location {0} for peer grading module {1} does not exist".format(
|
||||
self.link_to_location, self.location))
|
||||
raise
|
||||
except NoPathToItem:
|
||||
log.error("Linked location {0} for peer grading module {1} cannot be linked to.".format(
|
||||
self.link_to_location, self.location))
|
||||
raise
|
||||
due_date = self.linked_problem.due
|
||||
if due_date:
|
||||
self.due = due_date
|
||||
@@ -514,14 +530,18 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
|
||||
|
||||
def _find_corresponding_module_for_location(location):
|
||||
'''
|
||||
find the peer grading module that links to the given location
|
||||
'''
|
||||
"""
|
||||
Find the peer grading module that exists at the given location.
|
||||
"""
|
||||
try:
|
||||
return modulestore().get_instance(self.system.course_id, location)
|
||||
except Exception:
|
||||
# the linked problem doesn't exist
|
||||
log.error("Problem {0} does not exist in this course".format(location))
|
||||
return self.descriptor.system.load_item(location)
|
||||
except ItemNotFoundError:
|
||||
# The linked problem doesn't exist.
|
||||
log.error("Problem {0} does not exist in this course.".format(location))
|
||||
raise
|
||||
except NoPathToItem:
|
||||
# The linked problem does not have a path to it (ie is in a draft or other strange state).
|
||||
log.error("Cannot find a path to problem {0} in this course.".format(location))
|
||||
raise
|
||||
|
||||
good_problem_list = []
|
||||
@@ -529,7 +549,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
|
||||
problem_location = problem['location']
|
||||
try:
|
||||
descriptor = _find_corresponding_module_for_location(problem_location)
|
||||
except Exception:
|
||||
except (NoPathToItem, ItemNotFoundError):
|
||||
continue
|
||||
if descriptor:
|
||||
problem['due'] = descriptor.due
|
||||
|
||||
@@ -2,6 +2,12 @@ import unittest
|
||||
from xmodule.modulestore import Location
|
||||
from .import get_test_system
|
||||
from test_util_open_ended import MockQueryDict, DummyModulestore
|
||||
from xmodule.open_ended_grading_classes.peer_grading_service import MockPeerGradingService
|
||||
import json
|
||||
from mock import Mock
|
||||
from xmodule.peer_grading_module import PeerGradingModule
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
|
||||
import logging
|
||||
|
||||
@@ -136,6 +142,13 @@ class PeerGradingModuleTest(unittest.TestCase, DummyModulestore):
|
||||
"""
|
||||
self.peer_grading.get_instance_state()
|
||||
|
||||
class MockPeerGradingServiceProblemList(MockPeerGradingService):
|
||||
def get_problem_list(self, course_id, grader_id):
|
||||
return {'success': True,
|
||||
'problem_list': [
|
||||
{"num_graded": 3, "num_pending": 681, "num_required": 3, "location": "i4x://edX/open_ended/combinedopenended/SampleQuestion", "problem_name": "Peer-Graded Essay"},
|
||||
]}
|
||||
|
||||
class PeerGradingModuleScoredTest(unittest.TestCase, DummyModulestore):
|
||||
"""
|
||||
Test peer grading xmodule at the unit level. More detailed tests are difficult, as the module relies on an
|
||||
@@ -155,3 +168,70 @@ class PeerGradingModuleScoredTest(unittest.TestCase, DummyModulestore):
|
||||
def test_metadata_load(self):
|
||||
peer_grading = self.get_module_from_location(self.problem_location, COURSE)
|
||||
self.assertEqual(peer_grading.closed(), False)
|
||||
|
||||
def test_problem_list(self):
|
||||
"""
|
||||
Test to see if a peer grading problem list can be correctly initialized.
|
||||
"""
|
||||
|
||||
# Initialize peer grading module.
|
||||
peer_grading = self.get_module_from_location(self.problem_location, COURSE)
|
||||
|
||||
# Ensure that it cannot find any peer grading.
|
||||
html = peer_grading.peer_grading()
|
||||
self.assertNotIn("Peer-Graded", html)
|
||||
|
||||
# Swap for our mock class, which will find peer grading.
|
||||
peer_grading.peer_gs = MockPeerGradingServiceProblemList()
|
||||
html = peer_grading.peer_grading()
|
||||
self.assertIn("Peer-Graded", html)
|
||||
|
||||
class PeerGradingModuleLinkedTest(unittest.TestCase, DummyModulestore):
|
||||
"""
|
||||
Test peer grading that is linked to an open ended module.
|
||||
"""
|
||||
problem_location = Location(["i4x", "edX", "open_ended", "peergrading",
|
||||
"PeerGradingLinked"])
|
||||
coe_location = Location(["i4x", "edX", "open_ended", "combinedopenended",
|
||||
"SampleQuestion"])
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create a peer grading module from a test system.
|
||||
"""
|
||||
self.test_system = get_test_system()
|
||||
self.test_system.open_ended_grading_interface = None
|
||||
self.setup_modulestore(COURSE)
|
||||
|
||||
def test_linked_problem(self):
|
||||
"""
|
||||
Check to see if a peer grading module with a linked problem loads properly.
|
||||
"""
|
||||
|
||||
# Mock the linked problem descriptor.
|
||||
linked_descriptor = Mock()
|
||||
linked_descriptor.location = self.coe_location
|
||||
|
||||
# Mock the peer grading descriptor.
|
||||
pg_descriptor = Mock()
|
||||
pg_descriptor.location = self.problem_location
|
||||
pg_descriptor.get_required_module_descriptors = lambda: [linked_descriptor, ]
|
||||
|
||||
# Setup the proper field data for the peer grading module.
|
||||
field_data = DictFieldData({
|
||||
'data': '<peergrading/>',
|
||||
'location': self.problem_location,
|
||||
'use_for_single_location': True,
|
||||
'link_to_location': self.coe_location,
|
||||
})
|
||||
|
||||
# Initialize the peer grading module.
|
||||
peer_grading = PeerGradingModule(
|
||||
pg_descriptor,
|
||||
self.test_system,
|
||||
field_data,
|
||||
ScopeIds(None, None, self.problem_location, self.problem_location)
|
||||
)
|
||||
|
||||
# Ensure that it is properly setup.
|
||||
self.assertTrue(peer_grading.use_for_single_location)
|
||||
|
||||
@@ -146,6 +146,13 @@ class XModule(XModuleFields, HTMLSnippet, XBlock):
|
||||
else:
|
||||
return BlockUsageLocator(self.scope_ids.usage_id)
|
||||
|
||||
@location.setter
|
||||
def location(self, value):
|
||||
self.scope_ids = self.scope_ids._replace(
|
||||
def_id=value,
|
||||
usage_id=value,
|
||||
)
|
||||
|
||||
@property
|
||||
def url_name(self):
|
||||
if self.descriptor:
|
||||
@@ -457,6 +464,13 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
|
||||
else:
|
||||
return BlockUsageLocator(self.scope_ids.usage_id)
|
||||
|
||||
@location.setter
|
||||
def location(self, value):
|
||||
self.scope_ids = self.scope_ids._replace(
|
||||
def_id=value,
|
||||
usage_id=value,
|
||||
)
|
||||
|
||||
@property
|
||||
def url_name(self):
|
||||
if isinstance(self.location, Location):
|
||||
|
||||
@@ -93,7 +93,7 @@ $(document).ready(function() {
|
||||
% if "honor" in modes:
|
||||
<dt class="faq-question">${_("What if I can't afford it or don't have the necessary equipment?")}</dt>
|
||||
<dd class="faq-answer">
|
||||
<p>${_("If you can't afford the minimum fee, don't have a webcam, credit card, debit card or acceptable ID, you can audit the course for free. You may also elect to pursue an Honor Code certificate, but you will need to tell us why you would like the fee waived below. Then click the 'Select Certificate' button to complete your registration.")}</p>
|
||||
<p>${_("If you can't afford the minimum fee or don't meet the requirements, you can audit the course for free. You may also elect to pursue an Honor Code certificate, but you will need to tell us why you would like the fee waived below. Then click the 'Select Certificate' button to complete your registration.")}</p>
|
||||
|
||||
<ul class="list-fields">
|
||||
<li class="field field-honor-code checkbox">
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
<combinedopenended url_name="SampleQuestion1Attempt"/>
|
||||
<peergrading url_name="PeerGradingSample"/>
|
||||
<peergrading url_name="PeerGradingScored"/>
|
||||
<peergrading url_name="PeerGradingLinked"/>
|
||||
</chapter>
|
||||
</course>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<peergrading is_graded="True" max_grade="1" use_for_single_location="True" link_to_location="i4x://edX/open_ended/combinedopenended/SampleQuestion"/>
|
||||
1
common/test/data/open_ended_nopath/README.md
Normal file
1
common/test/data/open_ended_nopath/README.md
Normal file
@@ -0,0 +1 @@
|
||||
This is a very very simple course, useful for debugging open ended grading code. This is specifically for testing if a peer grading module with no path to it in the course will be handled properly.
|
||||
1
common/test/data/open_ended_nopath/course.xml
Normal file
1
common/test/data/open_ended_nopath/course.xml
Normal file
@@ -0,0 +1 @@
|
||||
<course org="edX" course="open_ended_nopath" url_name="2012_Fall"/>
|
||||
4
common/test/data/open_ended_nopath/course/2012_Fall.xml
Normal file
4
common/test/data/open_ended_nopath/course/2012_Fall.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<course>
|
||||
<chapter url_name="Overview">
|
||||
</chapter>
|
||||
</course>
|
||||
@@ -0,0 +1 @@
|
||||
<peergrading/>
|
||||
11
common/test/data/open_ended_nopath/policies/2012_Fall.json
Normal file
11
common/test/data/open_ended_nopath/policies/2012_Fall.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"course/2012_Fall": {
|
||||
"graceperiod": "2 days 5 hours 59 minutes 59 seconds",
|
||||
"start": "2015-07-17T12:00",
|
||||
"display_name": "Self Assessment Test",
|
||||
"graded": "true"
|
||||
},
|
||||
"chapter/Overview": {
|
||||
"display_name": "Overview"
|
||||
}
|
||||
}
|
||||
@@ -22,5 +22,6 @@ MAPPINGS = {
|
||||
'edX/test_about_blob_end_date/2012_Fall': 'xml',
|
||||
'edX/graded/2012_Fall': 'xml',
|
||||
'edX/open_ended/2012_Fall': 'xml',
|
||||
'edX/open_ended_nopath/2012_Fall': 'xml',
|
||||
}
|
||||
TEST_DATA_MIXED_MODULESTORE = mixed_store_config(TEST_DATA_DIR, MAPPINGS)
|
||||
|
||||
@@ -320,3 +320,22 @@ class TestPanel(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
request = Mock(user=self.user)
|
||||
response = views.student_problem_list(request, self.course.id)
|
||||
self.assertRegexpMatches(response.content, "Here are a list of open ended problems for this course.")
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
|
||||
class TestPeerGradingFound(ModuleStoreTestCase):
|
||||
"""
|
||||
Test to see if peer grading modules can be found properly.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.course_name = 'edX/open_ended_nopath/2012_Fall'
|
||||
self.course = modulestore().get_course(self.course_name)
|
||||
|
||||
def test_peer_grading_nopath(self):
|
||||
"""
|
||||
The open_ended_nopath course contains a peer grading module with no path to it.
|
||||
Ensure that the exception is caught.
|
||||
"""
|
||||
|
||||
found, url = views.find_peer_grading_module(self.course)
|
||||
self.assertEqual(found, False)
|
||||
@@ -20,7 +20,7 @@ import open_ended_notifications
|
||||
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore import search
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError, NoPathToItem
|
||||
|
||||
from django.http import HttpResponse, Http404, HttpResponseRedirect
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
@@ -97,25 +97,32 @@ def find_peer_grading_module(course):
|
||||
@param course: A course object.
|
||||
@return: boolean found_module, string problem_url
|
||||
"""
|
||||
#Reverse the base course url
|
||||
|
||||
# Reverse the base course url.
|
||||
base_course_url = reverse('courses')
|
||||
found_module = False
|
||||
problem_url = ""
|
||||
|
||||
#Get the course id and split it
|
||||
# Get the course id and split it.
|
||||
course_id_parts = course.id.split("/")
|
||||
log.info("COURSE ID PARTS")
|
||||
log.info(course_id_parts)
|
||||
#Get the peer grading modules currently in the course. Explicitly specify the course id to avoid issues with different runs.
|
||||
# Get the peer grading modules currently in the course. Explicitly specify the course id to avoid issues with different runs.
|
||||
items = modulestore().get_items(['i4x', course_id_parts[0], course_id_parts[1], 'peergrading', None],
|
||||
course_id=course.id)
|
||||
#See if any of the modules are centralized modules (ie display info from multiple problems)
|
||||
items = [i for i in items if not getattr(i, "use_for_single_location", True)]
|
||||
#Get the first one
|
||||
if len(items) > 0:
|
||||
item_location = items[0].location
|
||||
#Generate a url for the first module and redirect the user to it
|
||||
problem_url_parts = search.path_to_location(modulestore(), course.id, item_location)
|
||||
# Loop through all potential peer grading modules, and find the first one that has a path to it.
|
||||
for item in items:
|
||||
item_location = item.location
|
||||
# Generate a url for the first module and redirect the user to it.
|
||||
try:
|
||||
problem_url_parts = search.path_to_location(modulestore(), course.id, item_location)
|
||||
except NoPathToItem:
|
||||
# In the case of nopathtoitem, the peer grading module that was found is in an invalid state, and
|
||||
# can no longer be accessed. Log an informational message, but this will not impact normal behavior.
|
||||
log.info("Invalid peer grading module location {0} in course {1}. This module may need to be removed.".format(item_location, course.id))
|
||||
continue
|
||||
problem_url = generate_problem_url(problem_url_parts, base_course_url)
|
||||
found_module = True
|
||||
|
||||
|
||||
@@ -476,7 +476,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
|
||||
|
||||
body = {
|
||||
"EdX-ID": str(self.receipt_id),
|
||||
"ExpectedName": self.user.profile.name,
|
||||
"ExpectedName": self.name,
|
||||
"PhotoID": self.image_url("photo_id"),
|
||||
"PhotoIDKey": self.photo_id_key,
|
||||
"SendResponseTo": callback_url,
|
||||
@@ -500,7 +500,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
|
||||
header_txt = "\n".join(
|
||||
"{}: {}".format(h, v) for h,v in sorted(headers.items())
|
||||
)
|
||||
body_txt = json.dumps(body, indent=2, sort_keys=True)
|
||||
body_txt = json.dumps(body, indent=2, sort_keys=True, ensure_ascii=False)
|
||||
|
||||
return header_txt + "\n\n" + body_txt
|
||||
|
||||
@@ -509,7 +509,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification):
|
||||
response = requests.post(
|
||||
settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_URL"],
|
||||
headers=headers,
|
||||
data=json.dumps(body, indent=2, sort_keys=True)
|
||||
data=json.dumps(body, indent=2, sort_keys=True, ensure_ascii=False)
|
||||
)
|
||||
log.debug("Sent request to Software Secure for {}".format(self.receipt_id))
|
||||
log.debug("Headers:\n{}\n\n".format(headers))
|
||||
|
||||
@@ -131,7 +131,8 @@ def generate_signed_message(method, headers_dict, body_dict, access_key, secret_
|
||||
body_str = body_string(body_dict)
|
||||
message = headers_str + body_str
|
||||
|
||||
hashed = hmac.new(secret_key, message, sha256)
|
||||
# hmac needs a byte string for it's starting key, can't be unicode.
|
||||
hashed = hmac.new(secret_key.encode('utf-8'), message, sha256)
|
||||
signature = binascii.b2a_base64(hashed.digest()).rstrip('\n')
|
||||
authorization_header = "SSI {}:{}".format(access_key, signature)
|
||||
|
||||
@@ -161,7 +162,7 @@ def body_string(body_dict):
|
||||
for key, value in sorted(body_dict.items()):
|
||||
if value is None:
|
||||
value = "null"
|
||||
body_list.append(u"{}:{}\n".format(key, value))
|
||||
body_list.append(u"{}:{}\n".format(key, value).encode('utf-8'))
|
||||
|
||||
return "".join(body_list) # Note that trailing \n's are important
|
||||
|
||||
|
||||
@@ -50,7 +50,11 @@ var submitToPaymentProcessing = function() {
|
||||
$("#pay_form").submit();
|
||||
})
|
||||
.fail(function(jqXhr,text_status, error_thrown) {
|
||||
alert(jqXhr.responseText);
|
||||
if(jqXhr.status == 400) {
|
||||
$('#order-error .copy p').html(jqXhr.responseText);
|
||||
}
|
||||
$('#order-error').show();
|
||||
$("html, body").animate({ scrollTop: 0 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ site_status_msg = get_site_status_msg(course_id)
|
||||
<h1 class="logo">
|
||||
<a href="${marketing_link('ROOT')}">
|
||||
<%block name="navigation_logo">
|
||||
<img src="${static.url(branding.get_logo_url(request.META.get('HTTP_HOST')))}" alt="${_('{settings.PLATFORM_NAME} home')}" />
|
||||
<img src="${static.url(branding.get_logo_url(request.META.get('HTTP_HOST')))}" alt="${settings.PLATFORM_NAME} ${_('Home')}" />
|
||||
</%block>
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<span class="sts-track">
|
||||
<span class="sts-track-value">
|
||||
<span class="context">${_("Registering as: ")}</span> ${_("ID Verified")}
|
||||
<span class="context">${_("Registered as: ")}</span> ${_("ID Verified")}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -38,6 +38,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="order-error" style="display: none;" class="wrapper-msg wrapper-msg-activate">
|
||||
<div class=" msg msg-activate">
|
||||
<i class="msg-icon icon-warning-sign"></i>
|
||||
<div class="msg-content">
|
||||
<h3 class="title">${_("Error processing your order")}</h3>
|
||||
<div class="copy">
|
||||
<p>${_("Oops! Something went wrong. Please confirm your details again and click the button to move on to payment. If you are still having trouble, please try again later.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<section class="wrapper">
|
||||
@@ -309,7 +320,7 @@
|
||||
<ul class="list-help list-tips copy">
|
||||
<li class="tip">${_("Be readable (not too far away, no glare)")}</li>
|
||||
<li class="tip">${_("The photo on your ID must match the photo of your face")}</li>
|
||||
<li class="tip">${_("The name on your ID must match the name on your account above")}</li>
|
||||
<li class="tip">${_("The name on your ID must match the name on your account below")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user