diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py
index dfac1ac9c6..7e0019205e 100644
--- a/common/lib/xmodule/xmodule/course_module.py
+++ b/common/lib/xmodule/xmodule/course_module.py
@@ -1,7 +1,9 @@
+from fs.errors import ResourceNotFoundError
import time
import dateutil.parser
import logging
+from xmodule.graders import load_grading_policy
from xmodule.modulestore import Location
from xmodule.seq_module import SequenceDescriptor, SequenceModule
@@ -14,6 +16,9 @@ class CourseDescriptor(SequenceDescriptor):
def __init__(self, system, definition=None, **kwargs):
super(CourseDescriptor, self).__init__(system, definition, **kwargs)
+
+ self._grader = None
+ self._grade_cutoffs = None
try:
self.start = time.strptime(self.metadata["start"], "%Y-%m-%dT%H:%M")
@@ -27,7 +32,34 @@ class CourseDescriptor(SequenceDescriptor):
def has_started(self):
return time.gmtime() > self.start
-
+
+ @property
+ def grader(self):
+ self.__load_grading_policy()
+ return self._grader
+
+ @property
+ def grade_cutoffs(self):
+ self.__load_grading_policy()
+ return self._grade_cutoffs
+
+
+ def __load_grading_policy(self):
+ if not self._grader or not self._grade_cutoffs:
+ policy_string = ""
+
+ try:
+ with self.system.resources_fs.open("grading_policy.json") as grading_policy_file:
+ policy_string = grading_policy_file.read()
+ except (IOError, ResourceNotFoundError):
+ log.warning("Unable to load course settings file from grading_policy.json in course " + self.id)
+
+ grading_policy = load_grading_policy(policy_string)
+
+ self._grader = grading_policy['GRADER']
+ self._grade_cutoffs = grading_policy['GRADE_CUTOFFS']
+
+
@staticmethod
def id_to_location(course_id):
'''Convert the given course_id (org/course/name) to a location object.
diff --git a/common/lib/xmodule/xmodule/graders.py b/common/lib/xmodule/xmodule/graders.py
index 4473c8f1b9..fca862aa9f 100644
--- a/common/lib/xmodule/xmodule/graders.py
+++ b/common/lib/xmodule/xmodule/graders.py
@@ -1,4 +1,5 @@
import abc
+import json
import logging
from collections import namedtuple
@@ -9,6 +10,69 @@ log = logging.getLogger("mitx.courseware")
# Section either indicates the name of the problem or the name of the section
Score = namedtuple("Score", "earned possible graded section")
+def load_grading_policy(course_policy_string):
+ """
+ This loads a grading policy from a string (usually read from a file),
+ which can be a JSON object or an empty string.
+
+ The JSON object can have the keys GRADER and GRADE_CUTOFFS. If either is
+ missing, it reverts to the default.
+ """
+
+ default_policy_string = """
+ {
+ "GRADER" : [
+ {
+ "type" : "Homework",
+ "min_count" : 12,
+ "drop_count" : 2,
+ "short_label" : "HW",
+ "weight" : 0.15
+ },
+ {
+ "type" : "Lab",
+ "min_count" : 12,
+ "drop_count" : 2,
+ "category" : "Labs",
+ "weight" : 0.15
+ },
+ {
+ "type" : "Midterm",
+ "name" : "Midterm Exam",
+ "short_label" : "Midterm",
+ "weight" : 0.3
+ },
+ {
+ "type" : "Final",
+ "name" : "Final Exam",
+ "short_label" : "Final",
+ "weight" : 0.4
+ }
+ ],
+ "GRADE_CUTOFFS" : {
+ "A" : 0.87,
+ "B" : 0.7,
+ "C" : 0.6
+ }
+ }
+ """
+
+ # Load the global settings as a dictionary
+ grading_policy = json.loads(default_policy_string)
+
+ # Load the course policies as a dictionary
+ course_policy = {}
+ if course_policy_string:
+ course_policy = json.loads(course_policy_string)
+
+ # Override any global settings with the course settings
+ grading_policy.update(course_policy)
+
+ # Here is where we should parse any configurations, so that we can fail early
+ grading_policy['GRADER'] = grader_from_conf(grading_policy['GRADER'])
+
+ return grading_policy
+
def aggregate_scores(scores, section_name="summary"):
"""
diff --git a/lms/djangoapps/courseware/course_settings.py b/lms/djangoapps/courseware/course_settings.py
deleted file mode 100644
index 5b2348bee6..0000000000
--- a/lms/djangoapps/courseware/course_settings.py
+++ /dev/null
@@ -1,91 +0,0 @@
-"""
-Course settings module. All settings in the global_settings are
-first applied, and then any settings in the settings.DATA_DIR/course_settings.json
-are applied. A setting must be in ALL_CAPS.
-
-Settings are used by calling
-
-from courseware.course_settings import course_settings
-
-Note that courseware.course_settings.course_settings is not a module -- it's an object. So
-importing individual settings is not possible:
-
-from courseware.course_settings.course_settings import GRADER # This won't work.
-
-"""
-import json
-import logging
-
-from django.conf import settings
-
-from xmodule import graders
-
-log = logging.getLogger("mitx.courseware")
-
-global_settings_json = """
-{
- "GRADER" : [
- {
- "type" : "Homework",
- "min_count" : 12,
- "drop_count" : 2,
- "short_label" : "HW",
- "weight" : 0.15
- },
- {
- "type" : "Lab",
- "min_count" : 12,
- "drop_count" : 2,
- "category" : "Labs",
- "weight" : 0.15
- },
- {
- "type" : "Midterm",
- "name" : "Midterm Exam",
- "short_label" : "Midterm",
- "weight" : 0.3
- },
- {
- "type" : "Final",
- "name" : "Final Exam",
- "short_label" : "Final",
- "weight" : 0.4
- }
- ],
- "GRADE_CUTOFFS" : {
- "A" : 0.87,
- "B" : 0.7,
- "C" : 0.6
- }
-}
-"""
-
-
-class Settings(object):
- def __init__(self):
-
- # Load the global settings as a dictionary
- global_settings = json.loads(global_settings_json)
-
- # Load the course settings as a dictionary
- course_settings = {}
- try:
- # TODO: this doesn't work with multicourse
- with open(settings.DATA_DIR + "/course_settings.json") as course_settings_file:
- course_settings_string = course_settings_file.read()
- course_settings = json.loads(course_settings_string)
- except IOError:
- log.warning("Unable to load course settings file from " + str(settings.DATA_DIR) + "/course_settings.json")
-
- # Override any global settings with the course settings
- global_settings.update(course_settings)
-
- # Now, set the properties from the course settings on ourselves
- for setting in global_settings:
- setting_value = global_settings[setting]
- setattr(self, setting, setting_value)
-
- # Here is where we should parse any configurations, so that we can fail early
- self.GRADER = graders.grader_from_conf(self.GRADER)
-
-course_settings = Settings()
diff --git a/lms/djangoapps/courseware/grades.py b/lms/djangoapps/courseware/grades.py
index 66aff08dca..5a817e3d6c 100644
--- a/lms/djangoapps/courseware/grades.py
+++ b/lms/djangoapps/courseware/grades.py
@@ -3,7 +3,6 @@ import logging
from django.conf import settings
-from courseware.course_settings import course_settings
from xmodule import graders
from xmodule.graders import Score
from models import StudentModule
@@ -11,7 +10,7 @@ from models import StudentModule
_log = logging.getLogger("mitx.courseware")
-def grade_sheet(student, course, student_module_cache):
+def grade_sheet(student, course, grader, student_module_cache):
"""
This pulls a summary of all problems in the course. It returns a dictionary with two datastructures:
@@ -78,7 +77,6 @@ def grade_sheet(student, course, student_module_cache):
'chapter': c.metadata.get('display_name'),
'sections': sections})
- grader = course_settings.GRADER
grade_summary = grader.grade(totaled_scores)
return {'courseware_summary': chapters,
diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py
index 00fde8a84c..7281ab01ad 100644
--- a/lms/djangoapps/courseware/views.py
+++ b/lms/djangoapps/courseware/views.py
@@ -19,7 +19,6 @@ from django.views.decorators.cache import cache_control
from module_render import toc_for_course, get_module, get_section
from models import StudentModuleCache
from student.models import UserProfile
-from multicourse import multicourse_settings
from xmodule.modulestore import Location
from xmodule.modulestore.exceptions import InvalidLocationError, ItemNotFoundError, NoPathToItem
from xmodule.modulestore.django import modulestore
@@ -110,8 +109,8 @@ def profile(request, course_id, student_id=None):
user_info = UserProfile.objects.get(user=student)
student_module_cache = StudentModuleCache(request.user, course)
- course, _, _, _ = get_module(request.user, request, course.location, student_module_cache)
-
+ course_module, _, _, _ = get_module(request.user, request, course.location, student_module_cache)
+
context = {'name': user_info.name,
'username': student.username,
'location': user_info.location,
@@ -121,7 +120,7 @@ def profile(request, course_id, student_id=None):
'format_url_params': format_url_params,
'csrf': csrf(request)['csrf_token']
}
- context.update(grades.grade_sheet(student, course, student_module_cache))
+ context.update(grades.grade_sheet(student, course_module, course.grader, student_module_cache))
return render_to_response('profile.html', context)
@@ -184,9 +183,6 @@ def index(request, course_id, chapter=None, section=None,
chapter = clean(chapter)
section = clean(section)
- if settings.ENABLE_MULTICOURSE:
- settings.MODULESTORE['default']['OPTIONS']['data_dir'] = settings.DATA_DIR + multicourse_settings.get_course_xmlpath(course)
-
context = {
'csrf': csrf(request)['csrf_token'],
'accordion': render_accordion(request, course, chapter, section),
diff --git a/lms/templates/profile.html b/lms/templates/profile.html
index 1e3bde5969..6cda85fb03 100644
--- a/lms/templates/profile.html
+++ b/lms/templates/profile.html
@@ -4,6 +4,7 @@
<%block name="headextra">
<%static:css group='course'/>
%block>
+
<%namespace name="profile_graphs" file="profile_graphs.js"/>
<%block name="title">
Profile - edX 6.002x%block>
@@ -110,9 +111,9 @@ $(function() {
%block>
-<%include file="navigation.html" args="active_page='profile'" />
+<%include file="course_navigation.html" args="active_page='profile'" />
-
+
@@ -126,8 +127,7 @@ $(function() {
%for chapter in courseware_summary:
%if not chapter['chapter'] == "hidden":
-
+ ${ chapter['chapter'] }
%for section in chapter['sections']:
@@ -138,7 +138,7 @@ $(function() {
percentageString = "{0:.0%}".format( float(earned)/total) if earned > 0 and total > 0 else ""
%>
-