diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index fcf4a13fd6..ee69d925d0 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -208,4 +208,4 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor): for child in ['task']: add_child(child) - return elt \ No newline at end of file + return elt diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py index 47461639e0..9fe1531489 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_modulev1.py @@ -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, } diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/peer_grading_service.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/peer_grading_service.py index 1fabf6d323..42c54f0463 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/peer_grading_service.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/peer_grading_service.py @@ -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): diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py index a321374c07..c6fbd8e5df 100644 --- a/common/lib/xmodule/xmodule/peer_grading_module.py +++ b/common/lib/xmodule/xmodule/peer_grading_module.py @@ -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, diff --git a/common/lib/xmodule/xmodule/tests/test_self_assessment.py b/common/lib/xmodule/xmodule/tests/test_self_assessment.py index 83f6c0a8bc..f197aebc08 100644 --- a/common/lib/xmodule/xmodule/tests/test_self_assessment.py +++ b/common/lib/xmodule/xmodule/tests/test_self_assessment.py @@ -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) diff --git a/common/lib/xmodule/xmodule/timeinfo.py b/common/lib/xmodule/xmodule/timeinfo.py new file mode 100644 index 0000000000..6c6a72e700 --- /dev/null +++ b/common/lib/xmodule/xmodule/timeinfo.py @@ -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 diff --git a/lms/djangoapps/open_ended_grading/tests.py b/lms/djangoapps/open_ended_grading/tests.py index 63048c78d4..d452883ebb 100644 --- a/lms/djangoapps/open_ended_grading/tests.py +++ b/lms/djangoapps/open_ended_grading/tests.py @@ -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) diff --git a/lms/static/images/university/anu/anu-cover.jpg b/lms/static/images/university/anu/anu-cover.jpg new file mode 100644 index 0000000000..591df9791d Binary files /dev/null and b/lms/static/images/university/anu/anu-cover.jpg differ diff --git a/lms/static/images/university/anu/anu.png b/lms/static/images/university/anu/anu.png new file mode 100644 index 0000000000..5caeb0b180 Binary files /dev/null and b/lms/static/images/university/anu/anu.png differ diff --git a/lms/static/images/university/delft/delft-cover.jpg b/lms/static/images/university/delft/delft-cover.jpg new file mode 100644 index 0000000000..73092d7596 Binary files /dev/null and b/lms/static/images/university/delft/delft-cover.jpg differ diff --git a/lms/static/images/university/delft/delft.png b/lms/static/images/university/delft/delft.png new file mode 100644 index 0000000000..03c566e91f Binary files /dev/null and b/lms/static/images/university/delft/delft.png differ diff --git a/lms/static/images/university/epfl/epfl-cover.jpg b/lms/static/images/university/epfl/epfl-cover.jpg new file mode 100644 index 0000000000..42b188c925 Binary files /dev/null and b/lms/static/images/university/epfl/epfl-cover.jpg differ diff --git a/lms/static/images/university/epfl/epfl.png b/lms/static/images/university/epfl/epfl.png new file mode 100644 index 0000000000..6725340a1a Binary files /dev/null and b/lms/static/images/university/epfl/epfl.png differ diff --git a/lms/static/images/university/mcgill/mcgill-cover.jpg b/lms/static/images/university/mcgill/mcgill-cover.jpg new file mode 100644 index 0000000000..4d213c8c92 Binary files /dev/null and b/lms/static/images/university/mcgill/mcgill-cover.jpg differ diff --git a/lms/static/images/university/mcgill/mcgill.png b/lms/static/images/university/mcgill/mcgill.png new file mode 100644 index 0000000000..5a0bcb56ad Binary files /dev/null and b/lms/static/images/university/mcgill/mcgill.png differ diff --git a/lms/static/images/university/rice/rice-cover.jpg b/lms/static/images/university/rice/rice-cover.jpg new file mode 100644 index 0000000000..2266a72c6d Binary files /dev/null and b/lms/static/images/university/rice/rice-cover.jpg differ diff --git a/lms/static/images/university/rice/rice.png b/lms/static/images/university/rice/rice.png new file mode 100644 index 0000000000..95865f2dba Binary files /dev/null and b/lms/static/images/university/rice/rice.png differ diff --git a/lms/static/images/university/toronto/toronto-cover.jpg b/lms/static/images/university/toronto/toronto-cover.jpg new file mode 100644 index 0000000000..0d3434659e Binary files /dev/null and b/lms/static/images/university/toronto/toronto-cover.jpg differ diff --git a/lms/static/images/university/toronto/toronto.png b/lms/static/images/university/toronto/toronto.png new file mode 100644 index 0000000000..86851b9468 Binary files /dev/null and b/lms/static/images/university/toronto/toronto.png differ diff --git a/lms/static/sass/multicourse/_home.scss b/lms/static/sass/multicourse/_home.scss index 669bd889b0..b5546aa470 100644 --- a/lms/static/sass/multicourse/_home.scss +++ b/lms/static/sass/multicourse/_home.scss @@ -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 { diff --git a/lms/templates/index.html b/lms/templates/index.html index d08ba09e61..3e6d22cc91 100644 --- a/lms/templates/index.html +++ b/lms/templates/index.html @@ -47,8 +47,8 @@

Explore free courses from edX universities

-
-
    +
    +
    1. @@ -65,7 +65,7 @@
    2. -
    3. +
    4. @@ -73,11 +73,6 @@
    5. -
    - -
    - -
    1. @@ -86,6 +81,27 @@
    2. +
    3. + + +
      + McGillX +
      +
      +
    4. +
    5. + + +
      + ANUx +
      +
      +
    6. +
    + +
    + +
    1. @@ -94,7 +110,7 @@
    2. -
    3. +
    4. @@ -102,6 +118,38 @@
    5. +
    6. + + +
      + TorontoX +
      +
      +
    7. +
    8. + + +
      + EPFLx +
      +
      +
    9. +
    10. + + +
      + DelftX +
      +
      +
    11. +
    12. + + +
      + RiceX +
      +
      +
    diff --git a/lms/templates/peer_grading/peer_grading.html b/lms/templates/peer_grading/peer_grading.html index d309b4486c..0485b698b2 100644 --- a/lms/templates/peer_grading/peer_grading.html +++ b/lms/templates/peer_grading/peer_grading.html @@ -14,6 +14,7 @@ + @@ -22,7 +23,18 @@ %for problem in problem_list: +
    Problem NameDue date Graded Available Required
    - ${problem['problem_name']} + %if problem['closed']: + ${problem['problem_name']} + %else: + ${problem['problem_name']} + %endif + + % if problem['due']: + ${problem['due']} + % else: + No due date + % endif ${problem['num_graded']} diff --git a/lms/templates/peer_grading/peer_grading_closed.html b/lms/templates/peer_grading/peer_grading_closed.html new file mode 100644 index 0000000000..712ad8b380 --- /dev/null +++ b/lms/templates/peer_grading/peer_grading_closed.html @@ -0,0 +1,10 @@ +
    +

    Peer Grading

    +

    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 +

    +
    diff --git a/lms/templates/university_profile/anux.html b/lms/templates/university_profile/anux.html new file mode 100644 index 0000000000..c19310c70f --- /dev/null +++ b/lms/templates/university_profile/anux.html @@ -0,0 +1,24 @@ +<%inherit file="base.html" /> +<%namespace name='static' file='../static_content.html'/> + +<%block name="title">ANUx + +<%block name="university_header"> + + + + +<%block name="university_description"> +

    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.

    + + +${parent.body()} diff --git a/lms/templates/university_profile/delftx.html b/lms/templates/university_profile/delftx.html new file mode 100644 index 0000000000..feb3092dd9 --- /dev/null +++ b/lms/templates/university_profile/delftx.html @@ -0,0 +1,24 @@ +<%inherit file="base.html" /> +<%namespace name='static' file='../static_content.html'/> + +<%block name="title">DelftX + +<%block name="university_header"> + + + + +<%block name="university_description"> +

    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.

    + + +${parent.body()} diff --git a/lms/templates/university_profile/epflx.html b/lms/templates/university_profile/epflx.html new file mode 100644 index 0000000000..5119a223de --- /dev/null +++ b/lms/templates/university_profile/epflx.html @@ -0,0 +1,27 @@ +<%inherit file="base.html" /> +<%namespace name='static' file='../static_content.html'/> + +<%block name="title">EPFLx + +<%block name="university_header"> + + + + +<%block name="university_description"> +

    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.

    + +

    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. +

    + + +${parent.body()} diff --git a/lms/templates/university_profile/mcgillx.html b/lms/templates/university_profile/mcgillx.html new file mode 100644 index 0000000000..ca0801aa3b --- /dev/null +++ b/lms/templates/university_profile/mcgillx.html @@ -0,0 +1,24 @@ +<%inherit file="base.html" /> +<%namespace name='static' file='../static_content.html'/> + +<%block name="title">McGillX + +<%block name="university_header"> + + + + +<%block name="university_description"> +

    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).

    + + +${parent.body()} diff --git a/lms/templates/university_profile/ricex.html b/lms/templates/university_profile/ricex.html new file mode 100644 index 0000000000..36acea2836 --- /dev/null +++ b/lms/templates/university_profile/ricex.html @@ -0,0 +1,24 @@ +<%inherit file="base.html" /> +<%namespace name='static' file='../static_content.html'/> + +<%block name="title">RiceX + +<%block name="university_header"> + + + + +<%block name="university_description"> +

    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.

    + + +${parent.body()} diff --git a/lms/templates/university_profile/template.html b/lms/templates/university_profile/template.html new file mode 100644 index 0000000000..44fc3f3ab4 --- /dev/null +++ b/lms/templates/university_profile/template.html @@ -0,0 +1,24 @@ +<%inherit file="base.html" /> +<%namespace name='static' file='../static_content.html'/> + +<%block name="title">SCHOOLX + +<%block name="university_header"> + + + + +<%block name="university_description"> +

    + + +${parent.body()} diff --git a/lms/templates/university_profile/torontox.html b/lms/templates/university_profile/torontox.html new file mode 100644 index 0000000000..f696460fc5 --- /dev/null +++ b/lms/templates/university_profile/torontox.html @@ -0,0 +1,24 @@ +<%inherit file="base.html" /> +<%namespace name='static' file='../static_content.html'/> + +<%block name="title">TorontoX + +<%block name="university_header"> + + + + +<%block name="university_description"> +

    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.

    + + +${parent.body()} diff --git a/lms/urls.py b/lms/urls.py index fc42577085..764e2f8f0c 100644 --- a/lms/urls.py +++ b/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[^/]+)$', '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[^/]+)$', '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',