Merge remote-tracking branch 'origin/master' into feature/vik/oe-messages
Conflicts: common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py
@@ -208,4 +208,4 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
|
||||
for child in ['task']:
|
||||
add_child(child)
|
||||
|
||||
return elt
|
||||
return elt
|
||||
|
||||
@@ -2,8 +2,7 @@ import json
|
||||
import logging
|
||||
from lxml import etree
|
||||
from lxml.html import rewrite_links
|
||||
|
||||
|
||||
from xmodule.timeinfo import TimeInfo
|
||||
from xmodule.capa_module import only_one, ComplexEncoder
|
||||
from xmodule.editing_module import EditingDescriptor
|
||||
from xmodule.html_checker import check_html
|
||||
@@ -153,28 +152,14 @@ class CombinedOpenEndedV1Module():
|
||||
self.skip_basic_checks = self.metadata.get('skip_spelling_checks', SKIP_BASIC_CHECKS)
|
||||
|
||||
display_due_date_string = self.metadata.get('due', None)
|
||||
if display_due_date_string is not None:
|
||||
try:
|
||||
self.display_due_date = dateutil.parser.parse(display_due_date_string)
|
||||
except ValueError:
|
||||
#This is a staff_facing_error
|
||||
log.error("Could not parse due date {0} for location {1}. Contact the learning sciences group for assistance.".format(display_due_date_string, location))
|
||||
raise
|
||||
else:
|
||||
self.display_due_date = None
|
||||
|
||||
grace_period_string = self.metadata.get('graceperiod', None)
|
||||
if grace_period_string is not None and self.display_due_date:
|
||||
try:
|
||||
self.grace_period = parse_timedelta(grace_period_string)
|
||||
self.close_date = self.display_due_date + self.grace_period
|
||||
except:
|
||||
#This is a staff_facing_error
|
||||
log.error("Error parsing the grace period {0} for location {1}. Contact the learning sciences group for assistance.".format(grace_period_string, location))
|
||||
raise
|
||||
else:
|
||||
self.grace_period = None
|
||||
self.close_date = self.display_due_date
|
||||
try:
|
||||
self.timeinfo = TimeInfo(display_due_date_string, grace_period_string)
|
||||
except:
|
||||
log.error("Error parsing due date information in location {0}".format(location))
|
||||
raise
|
||||
self.display_due_date = self.timeinfo.display_due_date
|
||||
|
||||
# Used for progress / grading. Currently get credit just for
|
||||
# completion (doesn't matter if you self-assessed correct/incorrect).
|
||||
@@ -192,7 +177,7 @@ class CombinedOpenEndedV1Module():
|
||||
'rubric': definition['rubric'],
|
||||
'display_name': self.display_name,
|
||||
'accept_file_upload': self.accept_file_upload,
|
||||
'close_date' : self.close_date,
|
||||
'close_date' : self.timeinfo.close_date,
|
||||
's3_interface' : self.system.s3_interface,
|
||||
'skip_basic_checks' : self.skip_basic_checks,
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ class MockPeerGradingService(object):
|
||||
'max_score': 4})
|
||||
|
||||
def save_grade(self, location, grader_id, submission_id,
|
||||
score, feedback, submission_key):
|
||||
score, feedback, submission_key, rubric_scores, submission_flagged):
|
||||
return json.dumps({'success': True})
|
||||
|
||||
def is_student_calibrated(self, problem_location, grader_id):
|
||||
@@ -122,7 +122,8 @@ class MockPeerGradingService(object):
|
||||
'max_score': 4})
|
||||
|
||||
def save_calibration_essay(self, problem_location, grader_id,
|
||||
calibration_essay_id, submission_key, score, feedback):
|
||||
calibration_essay_id, submission_key, score,
|
||||
feedback, rubric_scores):
|
||||
return {'success': True, 'actual_score': 2}
|
||||
|
||||
def get_problem_list(self, course_id, grader_id):
|
||||
|
||||
@@ -3,6 +3,7 @@ import logging
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from datetime import datetime
|
||||
from pkg_resources import resource_string
|
||||
from .capa_module import ComplexEncoder
|
||||
from .editing_module import EditingDescriptor
|
||||
@@ -10,6 +11,8 @@ from .stringify import stringify_children
|
||||
from .x_module import XModule
|
||||
from .xml_module import XmlDescriptor
|
||||
from xmodule.modulestore import Location
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from timeinfo import TimeInfo
|
||||
|
||||
from xmodule.open_ended_grading_classes.peer_grading_service import PeerGradingService, GradingServiceError
|
||||
|
||||
@@ -52,6 +55,7 @@ class PeerGradingModule(XModule):
|
||||
self.system = system
|
||||
self.peer_gs = PeerGradingService(self.system.open_ended_grading_interface, self.system)
|
||||
|
||||
|
||||
self.use_for_single_location = self.metadata.get('use_for_single_location', USE_FOR_SINGLE_LOCATION)
|
||||
if isinstance(self.use_for_single_location, basestring):
|
||||
self.use_for_single_location = (self.use_for_single_location in TRUE_DICT)
|
||||
@@ -60,10 +64,28 @@ class PeerGradingModule(XModule):
|
||||
if isinstance(self.is_graded, basestring):
|
||||
self.is_graded = (self.is_graded in TRUE_DICT)
|
||||
|
||||
display_due_date_string = self.metadata.get('due', None)
|
||||
grace_period_string = self.metadata.get('graceperiod', None)
|
||||
|
||||
try:
|
||||
self.timeinfo = TimeInfo(display_due_date_string, grace_period_string)
|
||||
except:
|
||||
log.error("Error parsing due date information in location {0}".format(location))
|
||||
raise
|
||||
|
||||
self.display_due_date = self.timeinfo.display_due_date
|
||||
|
||||
self.link_to_location = self.metadata.get('link_to_location', USE_FOR_SINGLE_LOCATION)
|
||||
if self.use_for_single_location == True:
|
||||
#This will raise an exception if the location is invalid
|
||||
link_to_location_object = Location(self.link_to_location)
|
||||
try:
|
||||
self.linked_problem = modulestore().get_instance(self.system.course_id, self.link_to_location)
|
||||
except:
|
||||
log.error("Linked location {0} for peer grading module {1} does not exist".format(
|
||||
self.link_to_location, self.location))
|
||||
raise
|
||||
due_date = self.linked_problem.metadata.get('peer_grading_due', None)
|
||||
if due_date:
|
||||
self.metadata['due'] = due_date
|
||||
|
||||
self.ajax_url = self.system.ajax_url
|
||||
if not self.ajax_url.endswith("/"):
|
||||
@@ -75,6 +97,15 @@ class PeerGradingModule(XModule):
|
||||
#This could result in an exception, but not wrapping in a try catch block so it moves up the stack
|
||||
self.max_grade = int(self.max_grade)
|
||||
|
||||
def closed(self):
|
||||
return self._closed(self.timeinfo)
|
||||
|
||||
def _closed(self, timeinfo):
|
||||
if timeinfo.close_date is not None and datetime.utcnow() > timeinfo.close_date:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _err_response(self, msg):
|
||||
"""
|
||||
Return a HttpResponse with a json dump with success=False, and the given error message.
|
||||
@@ -94,6 +125,8 @@ class PeerGradingModule(XModule):
|
||||
Needs to be implemented by inheritors. Renders the HTML that students see.
|
||||
@return:
|
||||
"""
|
||||
if self.closed():
|
||||
return self.peer_grading_closed()
|
||||
if not self.use_for_single_location:
|
||||
return self.peer_grading()
|
||||
else:
|
||||
@@ -397,6 +430,16 @@ class PeerGradingModule(XModule):
|
||||
#This is a student_facing_error
|
||||
return self._err_response('')
|
||||
|
||||
def peer_grading_closed(self):
|
||||
'''
|
||||
Show the Peer grading closed template
|
||||
'''
|
||||
html = self.system.render_template('peer_grading/peer_grading_closed.html', {
|
||||
'use_for_single_location': self.use_for_single_location
|
||||
})
|
||||
return html
|
||||
|
||||
|
||||
def peer_grading(self, get=None):
|
||||
'''
|
||||
Show a peer grading interface
|
||||
@@ -425,6 +468,40 @@ class PeerGradingModule(XModule):
|
||||
error_text = "Could not get list of problems to peer grade. Please notify course staff."
|
||||
success = False
|
||||
|
||||
|
||||
def _find_corresponding_module_for_location(location):
|
||||
'''
|
||||
find the peer grading module that links to the given location
|
||||
'''
|
||||
try:
|
||||
return modulestore().get_instance(self.system.course_id, location)
|
||||
except:
|
||||
# the linked problem doesn't exist
|
||||
log.error("Problem {0} does not exist in this course".format(location))
|
||||
raise
|
||||
|
||||
|
||||
for problem in problem_list:
|
||||
problem_location = problem['location']
|
||||
descriptor = _find_corresponding_module_for_location(problem_location)
|
||||
if descriptor:
|
||||
problem['due'] = descriptor.metadata.get('peer_grading_due', None)
|
||||
grace_period_string = descriptor.metadata.get('graceperiod', None)
|
||||
try:
|
||||
problem_timeinfo = TimeInfo(problem['due'], grace_period_string)
|
||||
except:
|
||||
log.error("Malformed due date or grace period string for location {0}".format(problem_location))
|
||||
raise
|
||||
if self._closed(problem_timeinfo):
|
||||
problem['closed'] = True
|
||||
else:
|
||||
problem['closed'] = False
|
||||
else:
|
||||
# if we can't find the due date, assume that it doesn't have one
|
||||
problem['due'] = None
|
||||
problem['closed'] = False
|
||||
|
||||
|
||||
ajax_url = self.ajax_url
|
||||
html = self.system.render_template('peer_grading/peer_grading.html', {
|
||||
'course_id': self.system.course_id,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import json
|
||||
from mock import Mock
|
||||
from mock import Mock, MagicMock
|
||||
import unittest
|
||||
|
||||
from xmodule.open_ended_grading_classes.self_assessment_module import SelfAssessmentModule
|
||||
from xmodule.modulestore import Location
|
||||
from lxml import etree
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from . import test_system
|
||||
|
||||
@@ -64,13 +63,21 @@ class SelfAssessmentTest(unittest.TestCase):
|
||||
self.assertTrue("This is sample prompt text" in html)
|
||||
|
||||
def test_self_assessment_flow(self):
|
||||
raise SkipTest()
|
||||
responses = {'assessment': '0', 'score_list[]': ['0', '0']}
|
||||
def get_fake_item(name):
|
||||
return responses[name]
|
||||
|
||||
mock_query_dict = MagicMock()
|
||||
mock_query_dict.__getitem__.side_effect = get_fake_item
|
||||
mock_query_dict.getlist = get_fake_item
|
||||
|
||||
|
||||
self.assertEqual(self.module.get_score()['score'], 0)
|
||||
|
||||
self.module.save_answer({'student_answer': "I am an answer"}, test_system)
|
||||
self.assertEqual(self.module.state, self.module.ASSESSING)
|
||||
|
||||
self.module.save_assessment({'assessment': '0'}, test_system)
|
||||
self.module.save_assessment(mock_query_dict, test_system)
|
||||
self.assertEqual(self.module.state, self.module.DONE)
|
||||
|
||||
|
||||
@@ -80,5 +87,6 @@ class SelfAssessmentTest(unittest.TestCase):
|
||||
|
||||
# if we now assess as right, skip the REQUEST_HINT state
|
||||
self.module.save_answer({'student_answer': 'answer 4'}, test_system)
|
||||
self.module.save_assessment({'assessment': '1'}, test_system)
|
||||
responses['assessment'] = '1'
|
||||
self.module.save_assessment(mock_query_dict, test_system)
|
||||
self.assertEqual(self.module.state, self.module.DONE)
|
||||
|
||||
39
common/lib/xmodule/xmodule/timeinfo.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import dateutil
|
||||
import dateutil.parser
|
||||
import datetime
|
||||
from timeparse import parse_timedelta
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class TimeInfo(object):
|
||||
"""
|
||||
This is a simple object that calculates and stores datetime information for an XModule
|
||||
based on the due date string and the grace period string
|
||||
|
||||
So far it parses out three different pieces of time information:
|
||||
self.display_due_date - the 'official' due date that gets displayed to students
|
||||
self.grace_period - the length of the grace period
|
||||
self.close_date - the real due date
|
||||
|
||||
"""
|
||||
def __init__(self, display_due_date_string, grace_period_string):
|
||||
if display_due_date_string is not None:
|
||||
try:
|
||||
self.display_due_date = dateutil.parser.parse(display_due_date_string)
|
||||
except ValueError:
|
||||
log.error("Could not parse due date {0}".format(display_due_date_string))
|
||||
raise
|
||||
else:
|
||||
self.display_due_date = None
|
||||
|
||||
if grace_period_string is not None and self.display_due_date:
|
||||
try:
|
||||
self.grace_period = parse_timedelta(grace_period_string)
|
||||
self.close_date = self.display_due_date + self.grace_period
|
||||
except:
|
||||
log.error("Error parsing the grace period {0}".format(grace_period_string))
|
||||
raise
|
||||
else:
|
||||
self.grace_period = None
|
||||
self.close_date = self.display_due_date
|
||||
@@ -16,7 +16,7 @@ import courseware.tests.tests as ct
|
||||
from xmodule.modulestore.django import modulestore
|
||||
import xmodule.modulestore.django
|
||||
from nose import SkipTest
|
||||
from mock import patch, Mock
|
||||
from mock import patch, Mock, MagicMock
|
||||
import json
|
||||
from xmodule.x_module import ModuleSystem
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
@@ -169,23 +169,35 @@ class TestPeerGradingService(ct.PageLoader):
|
||||
|
||||
def test_get_next_submission_missing_location(self):
|
||||
data = {}
|
||||
r = self.peer_module.get_next_submission(data)
|
||||
d = r
|
||||
d = self.peer_module.get_next_submission(data)
|
||||
self.assertFalse(d['success'])
|
||||
self.assertEqual(d['error'], "Missing required keys: location")
|
||||
|
||||
def test_save_grade_success(self):
|
||||
raise SkipTest()
|
||||
data = 'rubric_scores[]=1|rubric_scores[]=2|location=' + self.location + '|submission_id=1|submission_key=fake key|score=2|feedback=feedback|submission_flagged=False'
|
||||
qdict = QueryDict(data.replace("|", "&"))
|
||||
data = {
|
||||
'rubric_scores[]': [0, 0],
|
||||
'location': self.location,
|
||||
'submission_id': 1,
|
||||
'submission_key': 'fake key',
|
||||
'score': 2,
|
||||
'feedback': 'feedback',
|
||||
'submission_flagged': 'false'
|
||||
}
|
||||
|
||||
qdict = MagicMock()
|
||||
def fake_get_item(key):
|
||||
return data[key]
|
||||
qdict.__getitem__.side_effect = fake_get_item
|
||||
qdict.getlist = fake_get_item
|
||||
qdict.keys = data.keys
|
||||
|
||||
r = self.peer_module.save_grade(qdict)
|
||||
d = r
|
||||
d = json.loads(r)
|
||||
self.assertTrue(d['success'])
|
||||
|
||||
def test_save_grade_missing_keys(self):
|
||||
data = {}
|
||||
r = self.peer_module.save_grade(data)
|
||||
d = r
|
||||
d = self.peer_module.save_grade(data)
|
||||
self.assertFalse(d['success'])
|
||||
self.assertTrue(d['error'].find('Missing required keys:') > -1)
|
||||
|
||||
@@ -198,8 +210,7 @@ class TestPeerGradingService(ct.PageLoader):
|
||||
|
||||
def test_is_calibrated_failure(self):
|
||||
data = {}
|
||||
r = self.peer_module.is_student_calibrated(data)
|
||||
d = r
|
||||
d = self.peer_module.is_student_calibrated(data)
|
||||
self.assertFalse(d['success'])
|
||||
self.assertFalse('calibrated' in d)
|
||||
|
||||
@@ -219,25 +230,36 @@ class TestPeerGradingService(ct.PageLoader):
|
||||
def test_show_calibration_essay_missing_key(self):
|
||||
data = {}
|
||||
|
||||
r = self.peer_module.show_calibration_essay(data)
|
||||
d = r
|
||||
d = self.peer_module.show_calibration_essay(data)
|
||||
|
||||
self.assertFalse(d['success'])
|
||||
self.assertEqual(d['error'], "Missing required keys: location")
|
||||
|
||||
def test_save_calibration_essay_success(self):
|
||||
raise SkipTest()
|
||||
data = 'rubric_scores[]=1|rubric_scores[]=2|location=' + self.location + '|submission_id=1|submission_key=fake key|score=2|feedback=feedback|submission_flagged=False'
|
||||
qdict = QueryDict(data.replace("|", "&"))
|
||||
r = self.peer_module.save_calibration_essay(qdict)
|
||||
d = r
|
||||
data = {
|
||||
'rubric_scores[]': [0, 0],
|
||||
'location': self.location,
|
||||
'submission_id': 1,
|
||||
'submission_key': 'fake key',
|
||||
'score': 2,
|
||||
'feedback': 'feedback',
|
||||
'submission_flagged': 'false'
|
||||
}
|
||||
|
||||
qdict = MagicMock()
|
||||
def fake_get_item(key):
|
||||
return data[key]
|
||||
qdict.__getitem__.side_effect = fake_get_item
|
||||
qdict.getlist = fake_get_item
|
||||
qdict.keys = data.keys
|
||||
|
||||
d = self.peer_module.save_calibration_essay(qdict)
|
||||
self.assertTrue(d['success'])
|
||||
self.assertTrue('actual_score' in d)
|
||||
|
||||
def test_save_calibration_essay_missing_keys(self):
|
||||
data = {}
|
||||
r = self.peer_module.save_calibration_essay(data)
|
||||
d = r
|
||||
d = self.peer_module.save_calibration_essay(data)
|
||||
self.assertFalse(d['success'])
|
||||
self.assertTrue(d['error'].find('Missing required keys:') > -1)
|
||||
self.assertFalse('actual_score' in d)
|
||||
|
||||
BIN
lms/static/images/university/anu/anu-cover.jpg
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
lms/static/images/university/anu/anu.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
lms/static/images/university/delft/delft-cover.jpg
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
lms/static/images/university/delft/delft.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
lms/static/images/university/epfl/epfl-cover.jpg
Normal file
|
After Width: | Height: | Size: 266 KiB |
BIN
lms/static/images/university/epfl/epfl.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
lms/static/images/university/mcgill/mcgill-cover.jpg
Normal file
|
After Width: | Height: | Size: 345 KiB |
BIN
lms/static/images/university/mcgill/mcgill.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
lms/static/images/university/rice/rice-cover.jpg
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
lms/static/images/university/rice/rice.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
lms/static/images/university/toronto/toronto-cover.jpg
Normal file
|
After Width: | Height: | Size: 200 KiB |
BIN
lms/static/images/university/toronto/toronto.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
@@ -1,6 +1,11 @@
|
||||
.home {
|
||||
padding: 0px;
|
||||
|
||||
> .container {
|
||||
@include box-sizing(border-box);
|
||||
width: flex-grid(12);
|
||||
}
|
||||
|
||||
> header {
|
||||
background: rgb(255,255,255);
|
||||
@include background-image(url('/static/images/homepage-bg.jpg'));
|
||||
@@ -175,9 +180,6 @@
|
||||
}
|
||||
|
||||
.university-partners {
|
||||
@include background-image(linear-gradient(180deg, rgba(245,245,245, 0) 0%,
|
||||
rgba(245,245,245, 1) 50%,
|
||||
rgba(245,245,245, 0) 100%));
|
||||
border-bottom: 1px solid rgb(210,210,210);
|
||||
margin-bottom: 0px;
|
||||
overflow: hidden;
|
||||
@@ -300,7 +302,6 @@
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 190px;
|
||||
position: relative;
|
||||
@include transition(all, 0.25s, ease-in-out);
|
||||
vertical-align: middle;
|
||||
@@ -324,6 +325,44 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.university-partners2x6 {
|
||||
@include box-sizing(border-box);
|
||||
width: flex-grid(12, 12);
|
||||
|
||||
.partners {
|
||||
@include box-sizing(border-box);
|
||||
@include clearfix();
|
||||
margin-left: 60px;
|
||||
padding: 12px 0;
|
||||
|
||||
.partner {
|
||||
@include box-sizing(border-box);
|
||||
width: flex-grid(2, 12);
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 0 12px;
|
||||
|
||||
a {
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.name > span {
|
||||
font-size: 1.0em;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.name {
|
||||
bottom: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more-info {
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
<section class="highlighted-courses">
|
||||
<h2>Explore free courses from <span class="edx">edX</span> universities</h2>
|
||||
|
||||
<section class="university-partners">
|
||||
<ol class="partners partners-primary">
|
||||
<section class="university-partners university-partners2x6">
|
||||
<ol class="partners">
|
||||
<li class="partner mit">
|
||||
<a href="${reverse('university_profile', args=['MITx'])}">
|
||||
<img src="${static.url('images/university/mit/mit.png')}" />
|
||||
@@ -65,7 +65,7 @@
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="partner last">
|
||||
<li class="partner">
|
||||
<a href="${reverse('university_profile', args=['BerkeleyX'])}">
|
||||
<img src="${static.url('images/university/berkeley/berkeley.png')}" />
|
||||
<div class="name">
|
||||
@@ -73,11 +73,6 @@
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<hr />
|
||||
|
||||
<ol class="partners">
|
||||
<li class="partner">
|
||||
<a href="${reverse('university_profile', args=['UTx'])}">
|
||||
<img src="${static.url('images/university/ut/ut-rollover_350x150.png')}" />
|
||||
@@ -86,6 +81,27 @@
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="partner">
|
||||
<a href="${reverse('university_profile', args=['McGillX'])}">
|
||||
<img src="${static.url('images/university/mcgill/mcgill.png')}" />
|
||||
<div class="name">
|
||||
<span>McGillX</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="partner">
|
||||
<a href="${reverse('university_profile', args=['ANUx'])}">
|
||||
<img src="${static.url('images/university/anu/anu.png')}" />
|
||||
<div class="name">
|
||||
<span>ANUx</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<hr />
|
||||
|
||||
<ol class="partners">
|
||||
<li class="partner">
|
||||
<a href="${reverse('university_profile', args=['WellesleyX'])}">
|
||||
<img src="${static.url('images/university/wellesley/wellesley-rollover_350x150.png')}" />
|
||||
@@ -94,7 +110,7 @@
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="partner last">
|
||||
<li class="partner">
|
||||
<a href="${reverse('university_profile', args=['GeorgetownX'])}">
|
||||
<img src="${static.url('images/university/georgetown/georgetown-rollover_350x150.png')}" />
|
||||
<div class="name">
|
||||
@@ -102,6 +118,38 @@
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="partner">
|
||||
<a href="${reverse('university_profile', args=['TorontoX'])}">
|
||||
<img src="${static.url('images/university/toronto/toronto.png')}" />
|
||||
<div class="name">
|
||||
<span>TorontoX</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="partner">
|
||||
<a href="${reverse('university_profile', args=['EPFLx'])}">
|
||||
<img src="${static.url('images/university/epfl/epfl.png')}" />
|
||||
<div class="name">
|
||||
<span>EPFLx</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="partner">
|
||||
<a href="${reverse('university_profile', args=['DelftX'])}">
|
||||
<img src="${static.url('images/university/delft/delft.png')}" />
|
||||
<div class="name">
|
||||
<span>DelftX</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="partner">
|
||||
<a href="${reverse('university_profile', args=['RiceX'])}">
|
||||
<img src="${static.url('images/university/rice/rice.png')}" />
|
||||
<div class="name">
|
||||
<span>RiceX</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<table class="problem-list">
|
||||
<tr>
|
||||
<th>Problem Name</th>
|
||||
<th>Due date</th>
|
||||
<th>Graded</th>
|
||||
<th>Available</th>
|
||||
<th>Required</th>
|
||||
@@ -22,7 +23,18 @@
|
||||
%for problem in problem_list:
|
||||
<tr data-graded="${problem['num_graded']}" data-required="${problem['num_required']}">
|
||||
<td class="problem-name">
|
||||
<a href="#problem" data-location="${problem['location']}" class="problem-button">${problem['problem_name']}</a>
|
||||
%if problem['closed']:
|
||||
${problem['problem_name']}
|
||||
%else:
|
||||
<a href="#problem" data-location="${problem['location']}" class="problem-button">${problem['problem_name']}</a>
|
||||
%endif
|
||||
</td>
|
||||
<td>
|
||||
% if problem['due']:
|
||||
${problem['due']}
|
||||
% else:
|
||||
No due date
|
||||
% endif
|
||||
</td>
|
||||
<td>
|
||||
${problem['num_graded']}
|
||||
|
||||
10
lms/templates/peer_grading/peer_grading_closed.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<section class="container peer-grading-container">
|
||||
<h2>Peer Grading</h2>
|
||||
<p>The due date has passed, and
|
||||
% if use_for_single_location:
|
||||
peer grading for this problem is closed at this time.
|
||||
%else:
|
||||
peer grading is closed at this time.
|
||||
%endif
|
||||
</p>
|
||||
</section>
|
||||
24
lms/templates/university_profile/anux.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<%inherit file="base.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%block name="title"><title>ANUx</title></%block>
|
||||
|
||||
<%block name="university_header">
|
||||
<header class="search" style="background: url('/static/images/university/anu/anu-cover.jpg')">
|
||||
<div class="inner-wrapper university-search">
|
||||
<hgroup>
|
||||
<div class="logo">
|
||||
<img src="${static.url('images/university/anu/anu.png')}" />
|
||||
</div>
|
||||
<h1>ANUx</h1>
|
||||
</hgroup>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</%block>
|
||||
|
||||
<%block name="university_description">
|
||||
<p>The Australian National University (ANU) is a celebrated place of intensive research, education and policy engagement. Our research has always been central to everything we do, shaping a holistic learning experience that goes beyond the classroom, giving students access to researchers who are among the best in their fields and to opportunities for development around Australia and the world.</p>
|
||||
</%block>
|
||||
|
||||
${parent.body()}
|
||||
24
lms/templates/university_profile/delftx.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<%inherit file="base.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%block name="title"><title>DelftX</title></%block>
|
||||
|
||||
<%block name="university_header">
|
||||
<header class="search" style="background: url('/static/images/university/delft/delft-cover.jpg')">
|
||||
<div class="inner-wrapper university-search">
|
||||
<hgroup>
|
||||
<div class="logo">
|
||||
<img src="${static.url('images/university/delft/delft.png')}" />
|
||||
</div>
|
||||
<h1>DelftX</h1>
|
||||
</hgroup>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</%block>
|
||||
|
||||
<%block name="university_description">
|
||||
<p>Delft University of Technology is the largest and oldest technological university in the Netherlands. Our research is inspired by the desire to increase fundamental understanding, as well as by societal challenges. We encourage our students to be independent thinkers so they will become engineers capable of solving complex problems. Our students have chosen Delft University of Technology because of our reputation for quality education and research.</p>
|
||||
</%block>
|
||||
|
||||
${parent.body()}
|
||||
27
lms/templates/university_profile/epflx.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<%inherit file="base.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%block name="title"><title>EPFLx</title></%block>
|
||||
|
||||
<%block name="university_header">
|
||||
<header class="search" style="background: url('/static/images/university/epfl/epfl-cover.jpg')">
|
||||
<div class="inner-wrapper university-search">
|
||||
<hgroup>
|
||||
<div class="logo">
|
||||
<img src="${static.url('images/university/epfl/epfl.png')}" />
|
||||
</div>
|
||||
<h1>EPFLx</h1>
|
||||
</hgroup>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</%block>
|
||||
|
||||
<%block name="university_description">
|
||||
<p>EPFL is one of the two Swiss Federal Institutes of Technology. With the status of a national school since 1969, the young engineering school has grown in many dimensions, to the extent of becoming one of the most famous European institutions of science and technology. It has three core missions: training, research and technology transfer. </p>
|
||||
|
||||
<p>EPFL is located in Lausanne in Switzerland, on the shores of the largest lake in Europe, Lake Geneva and at the foot of the Alps and Mont-Blanc. Its main campus brings together over 11,000 persons, students, researchers and staff in the same magical place. Because of its dynamism and rich student community, EPFL has been able to create a special spirit imbued with curiosity and simplicity. Daily interactions amongst students, researchers and entrepreneurs on campus give rise to new scientific, technological and architectural projects.
|
||||
</p>
|
||||
</%block>
|
||||
|
||||
${parent.body()}
|
||||
24
lms/templates/university_profile/mcgillx.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<%inherit file="base.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%block name="title"><title>McGillX</title></%block>
|
||||
|
||||
<%block name="university_header">
|
||||
<header class="search" style="background: url('/static/images/university/mcgill/mcgill-cover.jpg')">
|
||||
<div class="inner-wrapper university-search">
|
||||
<hgroup>
|
||||
<div class="logo">
|
||||
<img src="${static.url('images/university/mcgill/mcgill.png')}" />
|
||||
</div>
|
||||
<h1>McGillX</h1>
|
||||
</hgroup>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</%block>
|
||||
|
||||
<%block name="university_description">
|
||||
<p>McGill University is one of Canada's best-known institutions of higher learning and one of the leading universities in the world. McGill is located in vibrant multicultural Montreal, in the province of Quebec. Our 11 faculties and 11 professional schools offer more than 300 programs to some 38,000 graduate, undergraduate and continuing studies students. McGill ranks 1st in Canada among medical-doctoral universities (Maclean’s) and 18th in the world (QS World University Rankings).</p>
|
||||
</%block>
|
||||
|
||||
${parent.body()}
|
||||
24
lms/templates/university_profile/ricex.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<%inherit file="base.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%block name="title"><title>RiceX</title></%block>
|
||||
|
||||
<%block name="university_header">
|
||||
<header class="search" style="background: url('/static/images/university/rice/rice-cover.jpg')">
|
||||
<div class="inner-wrapper university-search">
|
||||
<hgroup>
|
||||
<div class="logo">
|
||||
<img src="${static.url('images/university/rice/rice.png')}" />
|
||||
</div>
|
||||
<h1>RiceX</h1>
|
||||
</hgroup>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</%block>
|
||||
|
||||
<%block name="university_description">
|
||||
<p>Located on a 300-acre forested campus in Houston, Rice University is consistently ranked among the nation's top 20 universities by U.S. News & World Report. Rice has highly respected schools of Architecture, Business, Continuing Studies, Engineering, Humanities, Music, Natural Sciences and Social Sciences and is home to the Baker Institute for Public Policy. With 3,708 undergraduates and 2,374 graduate students, Rice's undergraduate student-to-faculty ratio is 6-to-1. Its residential college system builds close-knit communities and lifelong friendships, just one reason why Rice has been ranked No. 1 for best quality of life multiple times by the Princeton Review and No. 2 for "best value" among private universities by Kiplinger's Personal Finance.</p>
|
||||
</%block>
|
||||
|
||||
${parent.body()}
|
||||
24
lms/templates/university_profile/template.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<%inherit file="base.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%block name="title"><title>SCHOOLX</title></%block>
|
||||
|
||||
<%block name="university_header">
|
||||
<header class="search" style="background: url('/static/images/university/SCHOOL/IMAGE.jpg')">
|
||||
<div class="inner-wrapper university-search">
|
||||
<hgroup>
|
||||
<div class="logo">
|
||||
<img src="${static.url('images/university/SCHOOL/IMAGE.png')}" />
|
||||
</div>
|
||||
<h1>SCHOOLX</h1>
|
||||
</hgroup>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</%block>
|
||||
|
||||
<%block name="university_description">
|
||||
<p></p>
|
||||
</%block>
|
||||
|
||||
${parent.body()}
|
||||
24
lms/templates/university_profile/torontox.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<%inherit file="base.html" />
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%block name="title"><title>TorontoX</title></%block>
|
||||
|
||||
<%block name="university_header">
|
||||
<header class="search" style="background: url('/static/images/university/toronto/toronto-cover.jpg')">
|
||||
<div class="inner-wrapper university-search">
|
||||
<hgroup>
|
||||
<div class="logo">
|
||||
<img src="${static.url('images/university/toronto/toronto.png')}" />
|
||||
</div>
|
||||
<h1>TorontoX</h1>
|
||||
</hgroup>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</%block>
|
||||
|
||||
<%block name="university_description">
|
||||
<p>Established in 1827, the University of Toronto is a vibrant and diverse academic community. It includes 80,000 students, 12,000 colleagues holding faculty appointments, 200 librarians, and 6,000 staff members across three distinctive campuses and at many partner sites, including world-renowned hospitals. With over 800 undergraduate programs, 150 graduate programs, and 40 professional programs, U of T attracts students of the highest calibre, from across Canada and from 160 countries around the world. The University is one of the most respected and influential institutions of higher education and advanced research in the world. Its strengths extend across the full range of disciplines: the 2012-13 Times Higher Education ranking groups the University of Toronto with Stanford, UC Berkeley, UCLA, Columbia, Cambridge, Oxford, the University of Melbourne, and the University of Michigan as the only institutions in the top 27 in all 6 broad disciplinary areas. The University is also consistently rated one of Canada’s Top 100 employers, and ranks with Harvard and Yale for the top university library resources in North America.</p>
|
||||
</%block>
|
||||
|
||||
${parent.body()}
|
||||
46
lms/urls.py
@@ -2,6 +2,7 @@ from django.conf import settings
|
||||
from django.conf.urls import patterns, include, url
|
||||
from django.contrib import admin
|
||||
from django.conf.urls.static import static
|
||||
from django.views.generic import RedirectView
|
||||
import django.contrib.auth.views
|
||||
|
||||
# Uncomment the next two lines to enable the admin:
|
||||
@@ -65,10 +66,47 @@ urlpatterns = ('',
|
||||
|
||||
url(r'^heartbeat$', include('heartbeat.urls')),
|
||||
|
||||
url(r'^university_profile/UTx$', 'courseware.views.static_university_profile', name="static_university_profile", kwargs={'org_id': 'UTx'}),
|
||||
url(r'^university_profile/WellesleyX$', 'courseware.views.static_university_profile', name="static_university_profile", kwargs={'org_id': 'WellesleyX'}),
|
||||
url(r'^university_profile/GeorgetownX$', 'courseware.views.static_university_profile', name="static_university_profile", kwargs={'org_id': 'GeorgetownX'}),
|
||||
url(r'^university_profile/(?P<org_id>[^/]+)$', 'courseware.views.university_profile', name="university_profile"),
|
||||
url(r'^university_profile/UTx$', 'courseware.views.static_university_profile',
|
||||
name="static_university_profile", kwargs={'org_id': 'UTx'}),
|
||||
url(r'^university_profile/WellesleyX$', 'courseware.views.static_university_profile',
|
||||
name="static_university_profile", kwargs={'org_id': 'WellesleyX'}),
|
||||
url(r'^university_profile/GeorgetownX$', 'courseware.views.static_university_profile',
|
||||
name="static_university_profile", kwargs={'org_id': 'GeorgetownX'}),
|
||||
|
||||
# Dan accidentally sent out a press release with lower case urls for McGill, Toronto,
|
||||
# Rice, ANU, Delft, and EPFL. Hence the redirects.
|
||||
url(r'^university_profile/McGillX$', 'courseware.views.static_university_profile',
|
||||
name="static_university_profile", kwargs={'org_id': 'McGillX'}),
|
||||
url(r'^university_profile/mcgillx$',
|
||||
RedirectView.as_view(url='/university_profile/McGillX')),
|
||||
|
||||
url(r'^university_profile/TorontoX$', 'courseware.views.static_university_profile',
|
||||
name="static_university_profile", kwargs={'org_id': 'TorontoX'}),
|
||||
url(r'^university_profile/torontox$',
|
||||
RedirectView.as_view(url='/university_profile/TorontoX')),
|
||||
|
||||
url(r'^university_profile/RiceX$', 'courseware.views.static_university_profile',
|
||||
name="static_university_profile", kwargs={'org_id': 'RiceX'}),
|
||||
url(r'^university_profile/ricex$',
|
||||
RedirectView.as_view(url='/university_profile/RiceX')),
|
||||
|
||||
url(r'^university_profile/ANUx$', 'courseware.views.static_university_profile',
|
||||
name="static_university_profile", kwargs={'org_id': 'ANUx'}),
|
||||
url(r'^university_profile/anux$',
|
||||
RedirectView.as_view(url='/university_profile/ANUx')),
|
||||
|
||||
url(r'^university_profile/DelftX$', 'courseware.views.static_university_profile',
|
||||
name="static_university_profile", kwargs={'org_id': 'DelftX'}),
|
||||
url(r'^university_profile/delftx$',
|
||||
RedirectView.as_view(url='/university_profile/DelftX')),
|
||||
|
||||
url(r'^university_profile/EPFLx$', 'courseware.views.static_university_profile',
|
||||
name="static_university_profile", kwargs={'org_id': 'EPFLx'}),
|
||||
url(r'^university_profile/epflx$',
|
||||
RedirectView.as_view(url='/university_profile/EPFLx')),
|
||||
|
||||
url(r'^university_profile/(?P<org_id>[^/]+)$', 'courseware.views.university_profile',
|
||||
name="university_profile"),
|
||||
|
||||
#Semi-static views (these need to be rendered and have the login bar, but don't change)
|
||||
url(r'^404$', 'static_template_view.views.render',
|
||||
|
||||