Commit 8f596e4d by Rocky Duan

Merge branch 'master' of github.com:MITx/mitx

Conflicts:
	lms/djangoapps/courseware/views.py
	lms/templates/profile.html
parents 5e8e90fb 7f2f4759
......@@ -37,7 +37,8 @@ REPOS = {
},
'content-mit-6002x': {
'branch': 'master',
'origin': 'git@github.com:MITx/6002x-fall-2012.git',
#'origin': 'git@github.com:MITx/6002x-fall-2012.git',
'origin': 'git@github.com:MITx/content-mit-6002x.git',
},
'6.00x': {
'branch': 'master',
......
......@@ -23,7 +23,8 @@ from django.http import HttpResponse, Http404
from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string
from django.core.urlresolvers import reverse
from BeautifulSoup import BeautifulSoup
#from BeautifulSoup import BeautifulSoup
from bs4 import BeautifulSoup
from django.core.cache import cache
from django_future.csrf import ensure_csrf_cookie
......
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.
......
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"):
"""
......
......@@ -203,6 +203,24 @@ case `uname -s` in
esac
;;
Darwin)
if [[ ! -w /usr/local ]]; then
cat<<EO
You need to be able to write to /usr/local for
the installation of brew and brew packages.
Either make sure the group you are in (most likely 'staff')
can write to that directory or simply execute the following
and re-run the script:
$ sudo chown -R $USER /usr/local
EO
exit 1
fi
command -v brew &>/dev/null || {
output "Installing brew"
/usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb)"
......@@ -238,7 +256,7 @@ case `uname -s` in
command -v coffee &>/dev/null || {
output "Installing coffee script"
curl http://npmjs.org/install.sh | sh
curl --insecure https://npmjs.org/install.sh | sh
npm install -g coffee-script
}
;;
......
"""
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()
......@@ -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,
......
......@@ -23,7 +23,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 django_comment_client.utils import get_discussion_title
from xmodule.modulestore import Location
......@@ -121,8 +120,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,
......@@ -132,7 +131,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)
......@@ -195,9 +194,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),
......
......@@ -4,6 +4,7 @@
<%block name="headextra">
<%static:css group='course'/>
</%block>
<%namespace name="profile_graphs" file="profile_graphs.js"/>
<%block name="title"><title>Profile - edX 6.002x</title></%block>
......@@ -110,9 +111,9 @@ $(function() {
</%block>
<%include file="navigation.html" args="active_page='profile'" />
<%include file="course_navigation.html" args="active_page='profile'" />
<section class="main-content">
<section class="container">
<div class="profile-wrapper">
<section class="course-info">
......@@ -126,8 +127,7 @@ $(function() {
%for chapter in courseware_summary:
%if not chapter['chapter'] == "hidden":
<li>
<h2><a href="javascript:void(0)">
${ chapter['chapter'] }</a></h2>
<h2>${ chapter['chapter'] }</h2>
<ol class="sections">
%for section in chapter['sections']:
......@@ -138,7 +138,7 @@ $(function() {
percentageString = "{0:.0%}".format( float(earned)/total) if earned > 0 and total > 0 else ""
%>
<h3><a href="javascript:void(0)">
<h3><a href="${reverse('courseware_section', kwargs={'course_id' : course.id, 'chapter' : chapter['chapter'], 'section' : section['section']})}">
${ section['section'] }</a> ${"({0:.3n}/{1:.3n}) {2}".format( float(earned), float(total), percentageString )}</h3>
${section['format']}
%if 'due' in section and section['due']!="":
......
......@@ -2,7 +2,6 @@ django<1.4
pip
numpy
scipy
matplotlib
markdown
pygments
lxml
......@@ -14,7 +13,8 @@ django_debug_toolbar
-e git://github.com/MITx/django-pipeline.git#egg=django-pipeline
django-staticfiles>=1.2.1
fs
beautifulsoup
beautifulsoup
beautifulsoup4
feedparser
requests
sympy
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment