diff --git a/.redcar/lucene/segments.gen b/.redcar/lucene/segments.gen
deleted file mode 100644
index 568652b956..0000000000
Binary files a/.redcar/lucene/segments.gen and /dev/null differ
diff --git a/.redcar/lucene_last_updated b/.redcar/lucene_last_updated
deleted file mode 100644
index 3692c8e076..0000000000
--- a/.redcar/lucene_last_updated
+++ /dev/null
@@ -1 +0,0 @@
-1360614836
diff --git a/.redcar/redcar.lock b/.redcar/redcar.lock
deleted file mode 100644
index 0677ede437..0000000000
--- a/.redcar/redcar.lock
+++ /dev/null
@@ -1 +0,0 @@
-10664: Locked by 10664 at Mon Feb 11 14:22:22 -0500 2013
diff --git a/.redcar/storage/cursor_saver.yaml b/.redcar/storage/cursor_saver.yaml
deleted file mode 100644
index ba9b833044..0000000000
--- a/.redcar/storage/cursor_saver.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-cursor_positions: []
-
-files_to_retain: 0
diff --git a/.redcar/tags.REMOVED.git-id b/.redcar/tags.REMOVED.git-id
deleted file mode 100644
index f8318e5f1f..0000000000
--- a/.redcar/tags.REMOVED.git-id
+++ /dev/null
@@ -1 +0,0 @@
-ce76efcea5f0a5b2238364f81d54f1d393853a1a
\ No newline at end of file
diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py
index b55dc13e58..14d1ee83ed 100644
--- a/cms/djangoapps/contentstore/views.py
+++ b/cms/djangoapps/contentstore/views.py
@@ -68,6 +68,10 @@ log = logging.getLogger(__name__)
COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video']
+ADVANCED_COMPONENT_TYPES = ['annotatable','combinedopenended', 'peergrading']
+ADVANCED_COMPONENT_CATEGORY = 'advanced'
+ADVANCED_COMPONENT_POLICY_KEY = 'advanced_modules'
+
# cdodge: these are categories which should not be parented, they are detached from the hierarchy
DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info']
@@ -281,10 +285,31 @@ def edit_unit(request, location):
component_templates = defaultdict(list)
+ # Check if there are any advanced modules specified in the course policy. These modules
+ # should be specified as a list of strings, where the strings are the names of the modules
+ # in ADVANCED_COMPONENT_TYPES that should be enabled for the course.
+ course_metadata = CourseMetadata.fetch(course.location)
+ course_advanced_keys = course_metadata.get(ADVANCED_COMPONENT_POLICY_KEY, [])
+
+ # Set component types according to course policy file
+ component_types = list(COMPONENT_TYPES)
+ if isinstance(course_advanced_keys, list):
+ course_advanced_keys = [c for c in course_advanced_keys if c in ADVANCED_COMPONENT_TYPES]
+ if len(course_advanced_keys) > 0:
+ component_types.append(ADVANCED_COMPONENT_CATEGORY)
+ else:
+ log.error("Improper format for course advanced keys! {0}".format(course_advanced_keys))
+
templates = modulestore().get_items(Location('i4x', 'edx', 'templates'))
for template in templates:
- if template.location.category in COMPONENT_TYPES:
- component_templates[template.location.category].append((
+ category = template.location.category
+
+ if category in course_advanced_keys:
+ category = ADVANCED_COMPONENT_CATEGORY
+
+ if category in component_types:
+ #This is a hack to create categories for different xmodules
+ component_templates[category].append((
template.display_name,
template.location.url(),
'markdown' in template.metadata,
diff --git a/cms/static/img/large-advanced-icon.png b/cms/static/img/large-advanced-icon.png
new file mode 100644
index 0000000000..c6a19ea5a9
Binary files /dev/null and b/cms/static/img/large-advanced-icon.png differ
diff --git a/cms/static/img/large-annotations-icon.png b/cms/static/img/large-annotations-icon.png
new file mode 100644
index 0000000000..249193521f
Binary files /dev/null and b/cms/static/img/large-annotations-icon.png differ
diff --git a/cms/static/img/large-openended-icon.png b/cms/static/img/large-openended-icon.png
new file mode 100644
index 0000000000..4d31815413
Binary files /dev/null and b/cms/static/img/large-openended-icon.png differ
diff --git a/cms/static/sass/_graphics.scss b/cms/static/sass/_graphics.scss
index 4ed22c570d..300cf3b692 100644
--- a/cms/static/sass/_graphics.scss
+++ b/cms/static/sass/_graphics.scss
@@ -254,6 +254,30 @@
background: url(../img/html-icon.png) center no-repeat;
}
+.large-openended-icon {
+ display: inline-block;
+ width: 100px;
+ height: 60px;
+ margin-right: 5px;
+ background: url(../img/large-openended-icon.png) center no-repeat;
+}
+
+.large-annotations-icon {
+ display: inline-block;
+ width: 100px;
+ height: 60px;
+ margin-right: 5px;
+ background: url(../img/large-annotations-icon.png) center no-repeat;
+}
+
+.large-advanced-icon {
+ display: inline-block;
+ width: 100px;
+ height: 60px;
+ margin-right: 5px;
+ background: url(../img/large-advanced-icon.png) center no-repeat;
+}
+
.large-textbook-icon {
display: inline-block;
width: 100px;
diff --git a/common/lib/capa/setup.py b/common/lib/capa/setup.py
index 15b3015930..9c724dec8b 100644
--- a/common/lib/capa/setup.py
+++ b/common/lib/capa/setup.py
@@ -4,5 +4,5 @@ setup(
name="capa",
version="0.1",
packages=find_packages(exclude=["tests"]),
- install_requires=['distribute', 'pyparsing'],
+ install_requires=['distribute==0.6.34', 'pyparsing==1.5.6'],
)
diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py
index a115a54376..ade5cc6ef6 100644
--- a/common/lib/xmodule/xmodule/capa_module.py
+++ b/common/lib/xmodule/xmodule/capa_module.py
@@ -333,6 +333,12 @@ class CapaModule(XModule):
reset_button = False
save_button = False
+ # If attempts=0 then show just check and reset buttons; this is for survey questions using capa
+ if self.max_attempts==0:
+ check_button = False
+ reset_button = True
+ save_button = True
+
# User submitted a problem, and hasn't reset. We don't want
# more submissions.
if self.lcp.done and self.rerandomize == "always":
@@ -630,11 +636,11 @@ class CapaModule(XModule):
event_info['answers'] = answers
# Too late. Cannot submit
- if self.closed():
+ if self.closed() and not self.max_attempts==0:
event_info['failure'] = 'closed'
self.system.track_function('save_problem_fail', event_info)
return {'success': False,
- 'error': "Problem is closed"}
+ 'msg': "Problem is closed"}
# Problem submitted. Student should reset before saving
# again.
@@ -642,13 +648,16 @@ class CapaModule(XModule):
event_info['failure'] = 'done'
self.system.track_function('save_problem_fail', event_info)
return {'success': False,
- 'error': "Problem needs to be reset prior to save."}
+ 'msg': "Problem needs to be reset prior to save"}
self.lcp.student_answers = answers
- # TODO: should this be save_problem_fail? Looks like success to me...
- self.system.track_function('save_problem_fail', event_info)
- return {'success': True}
+ self.system.track_function('save_problem_success', event_info)
+ msg = "Your answers have been saved"
+ if not self.max_attempts==0:
+ msg += " but not graded. Hit 'Check' to grade them."
+ return {'success': True,
+ 'msg': msg}
def reset_problem(self, get):
''' Changes problem state to unfinished -- removes student answers,
diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py
index ee69d925d0..659590b5b4 100644
--- a/common/lib/xmodule/xmodule/combined_open_ended_module.py
+++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py
@@ -4,9 +4,8 @@ from lxml import etree
from pkg_resources import resource_string
-from .editing_module import EditingDescriptor
+from xmodule.raw_module import RawDescriptor
from .x_module import XModule
-from .xml_module import XmlDescriptor
from xmodule.open_ended_grading_classes.combined_open_ended_modulev1 import CombinedOpenEndedV1Module, CombinedOpenEndedV1Descriptor
log = logging.getLogger("mitx.courseware")
@@ -134,7 +133,7 @@ class CombinedOpenEndedModule(XModule):
}
self.child_descriptor = descriptors[version_index](self.system)
- self.child_definition = descriptors[version_index].definition_from_xml(etree.fromstring(definition['xml_string']), self.system)
+ self.child_definition = descriptors[version_index].definition_from_xml(etree.fromstring(definition['data']), self.system)
self.child_module = modules[version_index](self.system, location, self.child_definition, self.child_descriptor,
instance_state = json.dumps(instance_state), metadata = self.metadata, static_data= static_data)
@@ -165,11 +164,11 @@ class CombinedOpenEndedModule(XModule):
return self.child_module.display_name
-class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
+class CombinedOpenEndedDescriptor(RawDescriptor):
"""
Module for adding combined open ended questions
"""
- mako_template = "widgets/html-edit.html"
+ mako_template = "widgets/raw-edit.html"
module_class = CombinedOpenEndedModule
filename_extension = "xml"
@@ -177,35 +176,3 @@ class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
has_score = True
template_dir_name = "combinedopenended"
- js = {'coffee': [resource_string(__name__, 'js/src/html/edit.coffee')]}
- js_module_name = "HTMLEditingDescriptor"
-
- @classmethod
- def definition_from_xml(cls, xml_object, system):
- """
- Pull out the individual tasks, the rubric, and the prompt, and parse
-
- Returns:
- {
- 'rubric': 'some-html',
- 'prompt': 'some-html',
- 'task_xml': dictionary of xml strings,
- }
- """
-
- return {'xml_string' : etree.tostring(xml_object), 'metadata' : xml_object.attrib}
-
-
- def definition_to_xml(self, resource_fs):
- '''Return an xml element representing this definition.'''
- elt = etree.Element('combinedopenended')
-
- def add_child(k):
- child_str = '<{tag}>{body}{tag}>'.format(tag=k, body=self.definition[k])
- child_node = etree.fromstring(child_str)
- elt.append(child_node)
-
- for child in ['task']:
- add_child(child)
-
- return elt
diff --git a/common/lib/xmodule/xmodule/js/src/capa/display.coffee b/common/lib/xmodule/xmodule/js/src/capa/display.coffee
index 57ff85298c..41c9b50891 100644
--- a/common/lib/xmodule/xmodule/js/src/capa/display.coffee
+++ b/common/lib/xmodule/xmodule/js/src/capa/display.coffee
@@ -262,9 +262,8 @@ class @Problem
save: =>
Logger.log 'problem_save', @answers
$.postWithPrefix "#{@url}/problem_save", @answers, (response) =>
- if response.success
- saveMessage = "Your answers have been saved but not graded. Hit 'Check' to grade them."
- @gentle_alert saveMessage
+ saveMessage = response.msg
+ @gentle_alert saveMessage
@updateProgress response
refreshMath: (event, element) =>
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 171441c562..5c3bfa5b2a 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
@@ -79,6 +79,9 @@ class CombinedOpenEndedV1Module():
INTERMEDIATE_DONE = 'intermediate_done'
DONE = 'done'
+ #Where the templates live for this problem
+ TEMPLATE_DIR = "combinedopenended"
+
def __init__(self, system, location, definition, descriptor,
instance_state=None, shared_state=None, metadata = None, static_data = None, **kwargs):
@@ -343,7 +346,7 @@ class CombinedOpenEndedV1Module():
Output: rendered html
"""
context = self.get_context()
- html = self.system.render_template('combined_open_ended.html', context)
+ html = self.system.render_template('{0}/combined_open_ended.html'.format(self.TEMPLATE_DIR), context)
return html
def get_html_nonsystem(self):
@@ -354,7 +357,7 @@ class CombinedOpenEndedV1Module():
Output: HTML rendered directly via Mako
"""
context = self.get_context()
- html = self.system.render_template('combined_open_ended.html', context)
+ html = self.system.render_template('{0}/combined_open_ended.html'.format(self.TEMPLATE_DIR), context)
return html
def get_html_base(self):
@@ -531,7 +534,7 @@ class CombinedOpenEndedV1Module():
'task_name' : 'Scored Rubric',
'class_name' : 'combined-rubric-container'
}
- html = self.system.render_template('combined_open_ended_results.html', context)
+ html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True}
def get_legend(self, get):
@@ -543,7 +546,7 @@ class CombinedOpenEndedV1Module():
context = {
'legend_list' : LEGEND_LIST,
}
- html = self.system.render_template('combined_open_ended_legend.html', context)
+ html = self.system.render_template('{0}/combined_open_ended_legend.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True}
def get_results(self, get):
@@ -574,7 +577,7 @@ class CombinedOpenEndedV1Module():
'submission_id' : ri['submission_ids'][i],
}
context_list.append(context)
- feedback_table = self.system.render_template('open_ended_result_table.html', {
+ feedback_table = self.system.render_template('{0}/open_ended_result_table.html'.format(self.TEMPLATE_DIR), {
'context_list' : context_list,
'grader_type_image_dict' : GRADER_TYPE_IMAGE_DICT,
'human_grader_types' : HUMAN_GRADER_TYPE,
@@ -586,7 +589,7 @@ class CombinedOpenEndedV1Module():
'task_name' : "Feedback",
'class_name' : "result-container",
}
- html = self.system.render_template('combined_open_ended_results.html', context)
+ html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True}
def get_status_ajax(self, get):
@@ -700,7 +703,7 @@ class CombinedOpenEndedV1Module():
'legend_list' : LEGEND_LIST,
'render_via_ajax' : render_via_ajax,
}
- status_html = self.system.render_template("combined_open_ended_status.html", context)
+ status_html = self.system.render_template("{0}/combined_open_ended_status.html".format(self.TEMPLATE_DIR), context)
return status_html
diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py
index f756b2b853..8d1bd376fb 100644
--- a/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py
+++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/combined_open_ended_rubric.py
@@ -30,6 +30,8 @@ class RubricParsingError(Exception):
class CombinedOpenEndedRubric(object):
+ TEMPLATE_DIR = "combinedopenended/openended"
+
def __init__ (self, system, view_only = False):
self.has_score = False
self.view_only = view_only
@@ -57,9 +59,9 @@ class CombinedOpenEndedRubric(object):
rubric_scores = [cat['score'] for cat in rubric_categories]
max_scores = map((lambda cat: cat['options'][-1]['points']), rubric_categories)
max_score = max(max_scores)
- rubric_template = 'open_ended_rubric.html'
+ rubric_template = '{0}/open_ended_rubric.html'.format(self.TEMPLATE_DIR)
if self.view_only:
- rubric_template = 'open_ended_view_only_rubric.html'
+ rubric_template = '{0}/open_ended_view_only_rubric.html'.format(self.TEMPLATE_DIR)
html = self.system.render_template(rubric_template,
{'categories': rubric_categories,
'has_score': self.has_score,
@@ -207,7 +209,7 @@ class CombinedOpenEndedRubric(object):
for grader_type in tuple[3]:
rubric_categories[i]['options'][j]['grader_types'].append(grader_type)
- html = self.system.render_template('open_ended_combined_rubric.html',
+ html = self.system.render_template('{0}/open_ended_combined_rubric.html'.format(self.TEMPLATE_DIR),
{'categories': rubric_categories,
'has_score': True,
'view_only': True,
diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py
index 974d23965f..aa805a5290 100644
--- a/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py
+++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/open_ended_module.py
@@ -40,6 +40,8 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
"""
+ TEMPLATE_DIR = "combinedopenended/openended"
+
def setup_response(self, system, location, definition, descriptor):
"""
Sets up the response type.
@@ -397,10 +399,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
rubric_scores = rubric_dict['rubric_scores']
if not response_items['success']:
- return system.render_template("open_ended_error.html",
+ return system.render_template("{0}/open_ended_error.html".format(self.TEMPLATE_DIR),
{'errors': feedback})
- feedback_template = system.render_template("open_ended_feedback.html", {
+ feedback_template = system.render_template("{0}/open_ended_feedback.html".format(self.TEMPLATE_DIR), {
'grader_type': response_items['grader_type'],
'score': "{0} / {1}".format(response_items['score'], self.max_score()),
'feedback': feedback,
@@ -558,7 +560,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
@return: Rendered html
"""
context = {'msg': feedback, 'id': "1", 'rows': 50, 'cols': 50}
- html = system.render_template('open_ended_evaluation.html', context)
+ html = system.render_template('{0}/open_ended_evaluation.html'.format(self.TEMPLATE_DIR), context)
return html
def handle_ajax(self, dispatch, get, system):
@@ -692,7 +694,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
'accept_file_upload': self.accept_file_upload,
'eta_message' : eta_string,
}
- html = system.render_template('open_ended.html', context)
+ html = system.render_template('{0}/open_ended.html'.format(self.TEMPLATE_DIR), context)
return html
diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py
index 50f9534717..31d26fd3c3 100644
--- a/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py
+++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/openendedchild.py
@@ -22,7 +22,7 @@ from xmodule.stringify import stringify_children
from xmodule.xml_module import XmlDescriptor
from xmodule.modulestore import Location
from capa.util import *
-from peer_grading_service import PeerGradingService
+from peer_grading_service import PeerGradingService, MockPeerGradingService
import controller_query_service
from datetime import datetime
@@ -106,8 +106,14 @@ class OpenEndedChild(object):
# Used for progress / grading. Currently get credit just for
# completion (doesn't matter if you self-assessed correct/incorrect).
self._max_score = static_data['max_score']
- self.peer_gs = PeerGradingService(system.open_ended_grading_interface, system)
- self.controller_qs = controller_query_service.ControllerQueryService(system.open_ended_grading_interface,system)
+ if system.open_ended_grading_interface:
+ self.peer_gs = PeerGradingService(system.open_ended_grading_interface, system)
+ self.controller_qs = controller_query_service.ControllerQueryService(system.open_ended_grading_interface,system)
+ else:
+ self.peer_gs = MockPeerGradingService()
+ self.controller_qs = None
+
+
self.system = system
@@ -461,11 +467,14 @@ class OpenEndedChild(object):
return success, allowed_to_submit, error_message
def get_eta(self):
- response = self.controller_qs.check_for_eta(self.location_string)
- try:
- response = json.loads(response)
- except:
- pass
+ if self.controller_qs:
+ response = self.controller_qs.check_for_eta(self.location_string)
+ try:
+ response = json.loads(response)
+ except:
+ pass
+ else:
+ return ""
success = response['success']
if isinstance(success, basestring):
diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py
index 7ecb3c4d5e..f4be426667 100644
--- a/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py
+++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/self_assessment_module.py
@@ -32,6 +32,8 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
"""
+ TEMPLATE_DIR = "combinedopenended/selfassessment"
+
def setup_response(self, system, location, definition, descriptor):
"""
Sets up the module
@@ -68,7 +70,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
'accept_file_upload': self.accept_file_upload,
}
- html = system.render_template('self_assessment_prompt.html', context)
+ html = system.render_template('{0}/self_assessment_prompt.html'.format(self.TEMPLATE_DIR), context)
return html
@@ -129,7 +131,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
#This is a dev_facing_error
raise ValueError("Self assessment module is in an illegal state '{0}'".format(self.state))
- return system.render_template('self_assessment_rubric.html', context)
+ return system.render_template('{0}/self_assessment_rubric.html'.format(self.TEMPLATE_DIR), context)
def get_hint_html(self, system):
"""
@@ -155,7 +157,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
#This is a dev_facing_error
raise ValueError("Self Assessment module is in an illegal state '{0}'".format(self.state))
- return system.render_template('self_assessment_hint.html', context)
+ return system.render_template('{0}/self_assessment_hint.html'.format(self.TEMPLATE_DIR), context)
def save_answer(self, get, system):
diff --git a/common/lib/xmodule/xmodule/peer_grading_module.py b/common/lib/xmodule/xmodule/peer_grading_module.py
index e262db5615..3eb18300c3 100644
--- a/common/lib/xmodule/xmodule/peer_grading_module.py
+++ b/common/lib/xmodule/xmodule/peer_grading_module.py
@@ -14,7 +14,7 @@ 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
+from xmodule.open_ended_grading_classes.peer_grading_service import PeerGradingService, GradingServiceError, MockPeerGradingService
log = logging.getLogger(__name__)
@@ -53,13 +53,28 @@ class PeerGradingModule(XModule):
#We need to set the location here so the child modules can use it
system.set('location', location)
self.system = system
- self.peer_gs = PeerGradingService(self.system.open_ended_grading_interface, self.system)
+ if(self.system.open_ended_grading_interface):
+ self.peer_gs = PeerGradingService(self.system.open_ended_grading_interface, self.system)
+ else:
+ self.peer_gs = MockPeerGradingService()
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)
+ self.link_to_location = self.metadata.get('link_to_location', USE_FOR_SINGLE_LOCATION)
+ if self.use_for_single_location == True:
+ 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.is_graded = self.metadata.get('is_graded', IS_GRADED)
if isinstance(self.is_graded, basestring):
self.is_graded = (self.is_graded in TRUE_DICT)
@@ -75,17 +90,6 @@ class PeerGradingModule(XModule):
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:
- 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("/"):
@@ -467,6 +471,9 @@ class PeerGradingModule(XModule):
#This is a student_facing_error
error_text = "Could not get list of problems to peer grade. Please notify course staff."
success = False
+ except:
+ log.exception("Could not contact peer grading service.")
+ success = False
def _find_corresponding_module_for_location(location):
@@ -562,7 +569,7 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor):
"""
Module for adding combined open ended questions
"""
- mako_template = "widgets/html-edit.html"
+ mako_template = "widgets/raw-edit.html"
module_class = PeerGradingModule
filename_extension = "xml"
@@ -585,7 +592,6 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor):
'task_xml': dictionary of xml strings,
}
"""
- log.debug("In definition")
expected_children = []
for child in expected_children:
if len(xml_object.xpath(child)) == 0:
@@ -606,13 +612,4 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor):
def definition_to_xml(self, resource_fs):
'''Return an xml element representing this definition.'''
elt = etree.Element('peergrading')
-
- def add_child(k):
- child_str = '<{tag}>{body}{tag}>'.format(tag=k, body=self.definition[k])
- child_node = etree.fromstring(child_str)
- elt.append(child_node)
-
- for child in ['task']:
- add_child(child)
-
return elt
diff --git a/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml b/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml
new file mode 100644
index 0000000000..f2aba0e18b
--- /dev/null
+++ b/common/lib/xmodule/xmodule/templates/combinedopenended/default.yaml
@@ -0,0 +1,44 @@
+---
+metadata:
+ display_name: Open Ended Response
+ max_attempts: 1
+ max_score: 1
+ is_graded: False
+ version: 1
+ display_name: Open Ended Response
+ skip_spelling_checks: False
+ accept_file_upload: False
+data: |
+
+
+
+
+ Category 1
+
+
+
+
+
+
+
Why is the sky blue?
+
+
+
+
+
+
+
+ Enter essay here.
+ This is the answer.
+ {"grader_settings" : "peer_grading.conf", "problem_id" : "700x/Demo"}
+
+
+
+
+
+
+children: []
diff --git a/common/lib/xmodule/xmodule/templates/peer_grading/default.yaml b/common/lib/xmodule/xmodule/templates/peer_grading/default.yaml
new file mode 100644
index 0000000000..cb8e29dfa2
--- /dev/null
+++ b/common/lib/xmodule/xmodule/templates/peer_grading/default.yaml
@@ -0,0 +1,13 @@
+---
+metadata:
+ display_name: Peer Grading Interface
+ attempts: 1
+ use_for_single_location: False
+ link_to_location: None
+ is_graded: False
+ max_grade: 1
+data: |
+
+
+
+children: []
diff --git a/lms/djangoapps/courseware/access.py b/lms/djangoapps/courseware/access.py
index 36ad388b43..eaf06d79dc 100644
--- a/lms/djangoapps/courseware/access.py
+++ b/lms/djangoapps/courseware/access.py
@@ -1,10 +1,10 @@
"""This file contains (or should), all access control logic for the courseware.
Ideally, it will be the only place that needs to know about any special settings
like DISABLE_START_DATES"""
-
import logging
import time
from datetime import datetime, timedelta
+from functools import partial
from django.conf import settings
from django.contrib.auth.models import Group
@@ -363,6 +363,30 @@ def _course_org_staff_group_name(location, course_context=None):
return 'staff_%s' % course_id.split('/')[0]
+def group_names_for(role, location, course_context=None):
+ """Returns the group names for a given role with this location. Plural
+ because it will return both the name we expect now as well as the legacy
+ group name we support for backwards compatibility. This should not check
+ the DB for existence of a group (like some of its callers do) because that's
+ a DB roundtrip, and we expect this might be invoked many times as we crawl
+ an XModule tree."""
+ loc = Location(location)
+ legacy_group_name = '{0}_{1}'.format(role, loc.course)
+
+ if loc.category == 'course':
+ course_id = loc.course_id
+ else:
+ if course_context is None:
+ raise CourseContextRequired()
+ course_id = course_context
+
+ group_name = '{0}_{1}'.format(role, course_id)
+
+ return [group_name, legacy_group_name]
+
+group_names_for_staff = partial(group_names_for, 'staff')
+group_names_for_instructor = partial(group_names_for, 'instructor')
+
def _course_staff_group_name(location, course_context=None):
"""
Get the name of the staff group for a location in the context of a course run.
@@ -375,33 +399,12 @@ def _course_staff_group_name(location, course_context=None):
using course_id rather than just the course number. So first check to see if the group name exists
"""
loc = Location(location)
- legacy_name = 'staff_%s' % loc.course
- if _does_course_group_name_exist(legacy_name):
- return legacy_name
+ group_name, legacy_group_name = group_names_for_staff(location, course_context)
- if loc.category == 'course':
- course_id = loc.course_id
- else:
- if course_context is None:
- raise CourseContextRequired()
- course_id = course_context
-
- return 'staff_%s' % course_id
-
-
-def course_beta_test_group_name(location):
- """
- Get the name of the beta tester group for a location. Right now, that's
- beta_testers_COURSE.
-
- location: something that can passed to Location.
- """
- return 'beta_testers_{0}'.format(Location(location).course)
-
-# nosetests thinks that anything with _test_ in the name is a test.
-# Correct this (https://nose.readthedocs.org/en/latest/finding_tests.html)
-course_beta_test_group_name.__test__ = False
+ if _does_course_group_name_exist(legacy_group_name):
+ return legacy_group_name
+ return group_name
def _course_org_instructor_group_name(location, course_context=None):
"""
@@ -437,18 +440,26 @@ def _course_instructor_group_name(location, course_context=None):
using course_id rather than just the course number. So first check to see if the group name exists
"""
loc = Location(location)
- legacy_name = 'instructor_%s' % loc.course
- if _does_course_group_name_exist(legacy_name):
- return legacy_name
+ group_name, legacy_group_name = group_names_for_instructor(location, course_context)
- if loc.category == 'course':
- course_id = loc.course_id
- else:
- if course_context is None:
- raise CourseContextRequired()
- course_id = course_context
+ if _does_course_group_name_exist(legacy_group_name):
+ return legacy_group_name
+
+ return group_name
+
+def course_beta_test_group_name(location):
+ """
+ Get the name of the beta tester group for a location. Right now, that's
+ beta_testers_COURSE.
+
+ location: something that can passed to Location.
+ """
+ return 'beta_testers_{0}'.format(Location(location).course)
+
+# nosetests thinks that anything with _test_ in the name is a test.
+# Correct this (https://nose.readthedocs.org/en/latest/finding_tests.html)
+course_beta_test_group_name.__test__ = False
- return 'instructor_%s' % course_id
def _has_global_staff_access(user):
@@ -540,23 +551,22 @@ def _has_access_to_location(user, location, access_level, course_context):
user_groups = [g.name for g in user.groups.all()]
if access_level == 'staff':
- staff_group = _course_staff_group_name(location, course_context)
- # org_staff_group is a group for an entire organization
- org_staff_group = _course_org_staff_group_name(location, course_context)
- if staff_group in user_groups or org_staff_group in user_groups:
- debug("Allow: user in group %s", staff_group)
- return True
- debug("Deny: user not in group %s", staff_group)
+ staff_groups = group_names_for_staff(location, course_context) + \
+ [_course_org_staff_group_name(location, course_context)]
+ for staff_group in staff_groups:
+ if staff_group in user_groups:
+ debug("Allow: user in group %s", staff_group)
+ return True
+ debug("Deny: user not in groups %s", staff_groups)
if access_level == 'instructor' or access_level == 'staff': # instructors get staff privileges
- instructor_group = _course_instructor_group_name(location, course_context)
- instructor_staff_group = _course_org_instructor_group_name(
- location, course_context)
- if instructor_group in user_groups or instructor_staff_group in user_groups:
- debug("Allow: user in group %s", instructor_group)
- return True
- debug("Deny: user not in group %s", instructor_group)
-
+ instructor_groups = group_names_for_instructor(location, course_context) + \
+ [_course_org_instructor_group_name(location, course_context)]
+ for instructor_group in instructor_groups:
+ if instructor_group in user_groups:
+ debug("Allow: user in group %s", instructor_group)
+ return True
+ debug("Deny: user not in groups %s", instructor_groups)
else:
log.debug("Error in access._has_access_to_location access_level=%s unknown" % access_level)
diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py
index fb351e1c01..8b48572818 100644
--- a/lms/djangoapps/courseware/views.py
+++ b/lms/djangoapps/courseware/views.py
@@ -86,7 +86,8 @@ def render_accordion(request, course, chapter, section):
Returns the html string'''
# grab the table of contents
- toc = toc_for_course(request.user, request, course, chapter, section)
+ user = User.objects.prefetch_related("groups").get(id=request.user.id)
+ toc = toc_for_course(user, request, course, chapter, section)
context = dict([('toc', toc),
('course_id', course.id),
@@ -250,23 +251,24 @@ def index(request, course_id, chapter=None, section=None,
- HTTPresponse
"""
- course = get_course_with_access(request.user, course_id, 'load', depth=2)
- staff_access = has_access(request.user, course, 'staff')
- registered = registered_for_course(course, request.user)
+ user = User.objects.prefetch_related("groups").get(id=request.user.id)
+ course = get_course_with_access(user, course_id, 'load', depth=2)
+ staff_access = has_access(user, course, 'staff')
+ registered = registered_for_course(course, user)
if not registered:
# TODO (vshnayder): do course instructors need to be registered to see course?
- log.debug('User %s tried to view course %s but is not enrolled' % (request.user, course.location.url()))
+ log.debug('User %s tried to view course %s but is not enrolled' % (user, course.location.url()))
return redirect(reverse('about_course', args=[course.id]))
try:
student_module_cache = StudentModuleCache.cache_for_descriptor_descendents(
- course.id, request.user, course, depth=2)
+ course.id, user, course, depth=2)
# Has this student been in this course before?
first_time = student_module_cache.lookup(course_id, 'course', course.location.url()) is None
# Load the module for the course
- course_module = get_module_for_descriptor(request.user, request, course, student_module_cache, course.id)
+ course_module = get_module_for_descriptor(user, request, course, student_module_cache, course.id)
if course_module is None:
log.warning('If you see this, something went wrong: if we got this'
' far, should have gotten a course module for this user')
@@ -288,7 +290,7 @@ def index(request, course_id, chapter=None, section=None,
chapter_descriptor = course.get_child_by(lambda m: m.url_name == chapter)
if chapter_descriptor is not None:
- instance_module = get_instance_module(course_id, request.user, course_module, student_module_cache)
+ instance_module = get_instance_module(course_id, user, course_module, student_module_cache)
save_child_position(course_module, chapter, instance_module)
else:
raise Http404('No chapter descriptor found with name {}'.format(chapter))
@@ -307,9 +309,9 @@ def index(request, course_id, chapter=None, section=None,
# Load all descendants of the section, because we're going to display its
# html, which in general will need all of its children
section_module_cache = StudentModuleCache.cache_for_descriptor_descendents(
- course.id, request.user, section_descriptor, depth=None)
+ course.id, user, section_descriptor, depth=None)
- section_module = get_module(request.user, request, section_descriptor.location,
+ section_module = get_module(user, request, section_descriptor.location,
section_module_cache, course.id, position=position, depth=None)
if section_module is None:
# User may be trying to be clever and access something
@@ -317,12 +319,12 @@ def index(request, course_id, chapter=None, section=None,
raise Http404
# Save where we are in the chapter
- instance_module = get_instance_module(course_id, request.user, chapter_module, student_module_cache)
+ instance_module = get_instance_module(course_id, user, chapter_module, student_module_cache)
save_child_position(chapter_module, section, instance_module)
# check here if this section *is* a timed module.
if section_module.category == 'timelimit':
- timer_context = update_timelimit_module(request.user, course_id, student_module_cache,
+ timer_context = update_timelimit_module(user, course_id, student_module_cache,
section_descriptor, section_module)
if 'timer_expiration_duration' in timer_context:
context.update(timer_context)
@@ -363,7 +365,7 @@ def index(request, course_id, chapter=None, section=None,
log.exception("Error in index view: user={user}, course={course},"
" chapter={chapter} section={section}"
"position={position}".format(
- user=request.user,
+ user=user,
course=course,
chapter=chapter,
section=section,
diff --git a/lms/djangoapps/dashboard/views.py b/lms/djangoapps/dashboard/views.py
index f5929f241b..e74d462432 100644
--- a/lms/djangoapps/dashboard/views.py
+++ b/lms/djangoapps/dashboard/views.py
@@ -1,39 +1,82 @@
# Create your views here.
import json
from datetime import datetime
-from django.http import HttpResponse, Http404
+from django.http import Http404
+from mitxmako.shortcuts import render_to_response
+
+from student.models import CourseEnrollment, CourseEnrollmentAllowed
+from django.contrib.auth.models import User
def dictfetchall(cursor):
- '''Returns all rows from a cursor as a dict.
+ '''Returns a list of all rows from a cursor as a column: result dict.
Borrowed from Django documentation'''
desc = cursor.description
- return [
- dict(zip([col[0] for col in desc], row))
- for row in cursor.fetchall()
- ]
+ table=[]
+ table.append([col[0] for col in desc])
+ table = table + cursor.fetchall()
+ print "Table: " + str(table)
+ return table
+def SQL_query_to_list(cursor, query_string):
+ cursor.execute(query_string)
+ raw_result=dictfetchall(cursor)
+ print raw_result
+ return raw_result
def dashboard(request):
"""
- Quick hack to show staff enrollment numbers. This should be
- replaced with a real dashboard later. This version is a short-term
- bandaid for the next couple weeks.
+ Slightly less hackish hack to show staff enrollment numbers and other
+ simple queries.
+
+ All queries here should be indexed and simple. Mostly, this means don't
+ touch courseware_studentmodule, as tempting as it may be.
+
"""
if not request.user.is_staff:
raise Http404
- queries = []
- queries.append("select count(user_id) as students, course_id from student_courseenrollment group by course_id order by students desc;")
- queries.append("select count(distinct user_id) as unique_students from student_courseenrollment;")
- queries.append("select registrations, count(registrations) from (select count(user_id) as registrations from student_courseenrollment group by user_id) as registrations_per_user group by registrations;")
+ # results are passed to the template. The template knows how to render
+ # two types of results: scalars and tables. Scalars should be represented
+ # as "Visible Title": Value and tables should be lists of lists where each
+ # inner list represents a single row of the table
+ results = {"scalars":{},"tables":{}}
+ # count how many users we have
+ results["scalars"]["Unique Usernames"]=User.objects.filter().count()
+ results["scalars"]["Activated Usernames"]=User.objects.filter(is_active=1).count()
+
+ # count how many enrollments we have
+ results["scalars"]["Total Enrollments Across All Courses"]=CourseEnrollment.objects.count()
+
+ # establish a direct connection to the database (for executing raw SQL)
from django.db import connection
cursor = connection.cursor()
- results = []
- for query in queries:
- cursor.execute(query)
- results.append(dictfetchall(cursor))
+ # define the queries that will generate our user-facing tables
+ # table queries need not take the form of raw SQL, but do in this case since
+ # the MySQL backend for django isn't very friendly with group by or distinct
+ table_queries = {}
+ table_queries["course enrollments"]= \
+ "select "+ \
+ "course_id as Course, "+ \
+ "count(user_id) as Students " + \
+ "from student_courseenrollment "+ \
+ "group by course_id "+ \
+ "order by students desc;"
+ table_queries["number of students in each number of classes"]= \
+ "select registrations as 'Registered for __ Classes' , "+ \
+ "count(registrations) as Users "+ \
+ "from (select count(user_id) as registrations "+ \
+ "from student_courseenrollment "+ \
+ "group by user_id) as registrations_per_user "+ \
+ "group by registrations;"
- return HttpResponse(json.dumps(results, indent=4))
+ # add the result for each of the table_queries to the results object
+ for query in table_queries.keys():
+ cursor.execute(table_queries[query])
+ results["tables"][query] = SQL_query_to_list(cursor, table_queries[query])
+
+ context={"results":results}
+
+ return render_to_response("admin_dashboard.html",context)
diff --git a/lms/djangoapps/open_ended_grading/views.py b/lms/djangoapps/open_ended_grading/views.py
index 76919d4103..55e8088c3f 100644
--- a/lms/djangoapps/open_ended_grading/views.py
+++ b/lms/djangoapps/open_ended_grading/views.py
@@ -89,14 +89,24 @@ def peer_grading(request, course_id):
Show a peer grading interface
'''
+ #Get the current course
course = get_course_with_access(request.user, course_id, 'load')
course_id_parts = course.id.split("/")
- course_id_norun = "/".join(course_id_parts[0:2])
- pg_location = "i4x://" + course_id_norun + "/peergrading/init"
+ false_dict = [False,"False", "false", "FALSE"]
+ #Reverse the base course url
base_course_url = reverse('courses')
try:
- problem_url_parts = search.path_to_location(modulestore(), course.id, pg_location)
+ #TODO: This will not work with multiple runs of a course. Make it work. The last key in the Location passed
+ #to get_items is called revision. Is this the same as run?
+ #Get the peer grading modules currently in the course
+ items = modulestore().get_items(['i4x', None, course_id_parts[1], 'peergrading', None])
+ #See if any of the modules are centralized modules (ie display info from multiple problems)
+ items = [i for i in items if i.metadata.get("use_for_single_location", True) in false_dict]
+ #Get the first one
+ 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)
problem_url = generate_problem_url(problem_url_parts, base_course_url)
return HttpResponseRedirect(problem_url)
diff --git a/lms/static/files/edx-identity.zip b/lms/static/files/edx-identity.zip
index 22b95f70f5..c0394bf06a 100644
Binary files a/lms/static/files/edx-identity.zip and b/lms/static/files/edx-identity.zip differ
diff --git a/lms/templates/admin_dashboard.html b/lms/templates/admin_dashboard.html
new file mode 100644
index 0000000000..6a903a3f94
--- /dev/null
+++ b/lms/templates/admin_dashboard.html
@@ -0,0 +1,44 @@
+<%namespace name='static' file='static_content.html'/>
+
+<%inherit file="main.html" />
+
+
+
+
+
+
+
edX-wide Summary
+
+ % for key in results["scalars"]:
+
+
${key}
+
${results["scalars"][key]}
+
+ % endfor
+
+
+
+ % for table in results["tables"]:
+
+
+
${table}
+
+
+ % for column in results["tables"][table][0]:
+
${column}
+ % endfor
+
+ % for row in results["tables"][table][1:]:
+
+ % for column in row:
+
${column}
+ % endfor
+
+ % endfor
+
+
+
+ % endfor
+
+
+
diff --git a/lms/templates/combined_open_ended.html b/lms/templates/combinedopenended/combined_open_ended.html
similarity index 100%
rename from lms/templates/combined_open_ended.html
rename to lms/templates/combinedopenended/combined_open_ended.html
diff --git a/lms/templates/combined_open_ended_legend.html b/lms/templates/combinedopenended/combined_open_ended_legend.html
similarity index 100%
rename from lms/templates/combined_open_ended_legend.html
rename to lms/templates/combinedopenended/combined_open_ended_legend.html
diff --git a/lms/templates/combined_open_ended_results.html b/lms/templates/combinedopenended/combined_open_ended_results.html
similarity index 100%
rename from lms/templates/combined_open_ended_results.html
rename to lms/templates/combinedopenended/combined_open_ended_results.html
diff --git a/lms/templates/combined_open_ended_status.html b/lms/templates/combinedopenended/combined_open_ended_status.html
similarity index 100%
rename from lms/templates/combined_open_ended_status.html
rename to lms/templates/combinedopenended/combined_open_ended_status.html
diff --git a/lms/templates/open_ended_result_table.html b/lms/templates/combinedopenended/open_ended_result_table.html
similarity index 100%
rename from lms/templates/open_ended_result_table.html
rename to lms/templates/combinedopenended/open_ended_result_table.html
diff --git a/lms/templates/open_ended.html b/lms/templates/combinedopenended/openended/open_ended.html
similarity index 100%
rename from lms/templates/open_ended.html
rename to lms/templates/combinedopenended/openended/open_ended.html
diff --git a/lms/templates/open_ended_combined_rubric.html b/lms/templates/combinedopenended/openended/open_ended_combined_rubric.html
similarity index 100%
rename from lms/templates/open_ended_combined_rubric.html
rename to lms/templates/combinedopenended/openended/open_ended_combined_rubric.html
diff --git a/lms/templates/open_ended_error.html b/lms/templates/combinedopenended/openended/open_ended_error.html
similarity index 100%
rename from lms/templates/open_ended_error.html
rename to lms/templates/combinedopenended/openended/open_ended_error.html
diff --git a/lms/templates/open_ended_evaluation.html b/lms/templates/combinedopenended/openended/open_ended_evaluation.html
similarity index 100%
rename from lms/templates/open_ended_evaluation.html
rename to lms/templates/combinedopenended/openended/open_ended_evaluation.html
diff --git a/lms/templates/open_ended_feedback.html b/lms/templates/combinedopenended/openended/open_ended_feedback.html
similarity index 100%
rename from lms/templates/open_ended_feedback.html
rename to lms/templates/combinedopenended/openended/open_ended_feedback.html
diff --git a/lms/templates/open_ended_rubric.html b/lms/templates/combinedopenended/openended/open_ended_rubric.html
similarity index 100%
rename from lms/templates/open_ended_rubric.html
rename to lms/templates/combinedopenended/openended/open_ended_rubric.html
diff --git a/lms/templates/open_ended_view_only_rubric.html b/lms/templates/combinedopenended/openended/open_ended_view_only_rubric.html
similarity index 100%
rename from lms/templates/open_ended_view_only_rubric.html
rename to lms/templates/combinedopenended/openended/open_ended_view_only_rubric.html
diff --git a/lms/templates/self_assessment_hint.html b/lms/templates/combinedopenended/selfassessment/self_assessment_hint.html
similarity index 100%
rename from lms/templates/self_assessment_hint.html
rename to lms/templates/combinedopenended/selfassessment/self_assessment_hint.html
diff --git a/lms/templates/self_assessment_prompt.html b/lms/templates/combinedopenended/selfassessment/self_assessment_prompt.html
similarity index 100%
rename from lms/templates/self_assessment_prompt.html
rename to lms/templates/combinedopenended/selfassessment/self_assessment_prompt.html
diff --git a/lms/templates/self_assessment_rubric.html b/lms/templates/combinedopenended/selfassessment/self_assessment_rubric.html
similarity index 100%
rename from lms/templates/self_assessment_rubric.html
rename to lms/templates/combinedopenended/selfassessment/self_assessment_rubric.html
diff --git a/lms/templates/static_templates/faq.html b/lms/templates/static_templates/faq.html
index a0d8c6d577..8b4561da7e 100644
--- a/lms/templates/static_templates/faq.html
+++ b/lms/templates/static_templates/faq.html
@@ -21,12 +21,12 @@
What is edX?
edX is a not-for-profit enterprise of its founding partners, the Massachusetts Institute of Technology (MIT) and Harvard University that offers online learning to on-campus students and to millions of people around the world. To do so, edX is building an open-source online learning platform and hosts an online web portal at www.edx.org for online education.
-
EdX currently offers HarvardX, MITx and BerkeleyX classes online for free. Beginning in fall 2013, edX will offer WellesleyX and GeorgetownX classes online for free. The University of Texas System includes nine universities and six health institutions. The edX institutions aim to extend their collective reach to build a global community of online students. Along with offering online courses, the three universities undertake research on how students learn and how technology can transform learning both on-campus and online throughout the world.
+
EdX currently offers HarvardX, MITx and BerkeleyX classes online for free. Beginning in fall 2013, edX will offer WellesleyX , GeorgetownX and the University of Texas System classes online for free. The UT System includes nine universities and six health institutions. In 2014, edX will further expand its consortium, including several international schools, when it begins offering courses from École Polytechnique Fédérale de Lausanne, McGill University, University of Toronto, Australian National University, Delft University of Technology, and Rice University. The edX institutions aim to extend their collective reach to build a global community of online students. Along with offering online courses, the three universities undertake research on how students learn and how technology can transform learning both on-campus and online throughout the world.
Will edX be adding additional X Universities?
-
More than 200 institutions from around the world have expressed interest in collaborating with edX since Harvard and MIT announced its creation in May. EdX is focused above all on quality and developing the best not-for-profit model for online education. In addition to providing online courses on the edX platform, the "X University" Consortium will be a forum in which members can share experiences around online learning. Harvard, MIT, UC Berkeley, the University of Texas system and the other consortium members will work collaboratively to establish the "X University" Consortium, whose membership will expand to include additional "X Universities". Each member of the consortium will offer courses on the edX platform as an "X University." The gathering of many universities' educational content together on one site will enable learners worldwide to access the offered course content of any participating university from a single website, and to use a set of online educational tools shared by all participating universities.
+
More than 200 institutions from around the world have expressed interest in collaborating with edX since Harvard and MIT announced its creation in May. EdX is focused above all on quality and developing the best not-for-profit model for online education. In addition to providing online courses on the edX platform, the "X University" Consortium will be a forum in which members can share experiences around online learning. Harvard, MIT, UC Berkeley, the University of Texas system and the other consortium members will work collaboratively to establish the "X University" Consortium, whose membership will expand to include additional "X Universities." As noted above, edX’s newest consortium members include Wellesley, Georgetown, École Polytechnique Fédérale de Lausanne, McGill University, University of Toronto, Australian National University, Delft University of Technology, and Rice University. Each member of the consortium will offer courses on the edX platform as an "X University." The gathering of many universities' educational content together on one site will enable learners worldwide to access the offered course content of any participating university from a single website, and to use a set of online educational tools shared by all participating universities.
edX will actively explore the addition of other institutions from around the world to the edX platform, and looks forward to adding more "X Universities."
@@ -50,7 +50,7 @@
What will the scope of the online courses be? How many? Which faculty?
-
Our goal is to offer a wide variety of courses across disciplines. There are currently nine courses offered for Fall 2012.
+
Our goal is to offer a wide variety of courses across disciplines. There are currently fifteen offered on the edX platform.
Who is the learner? Domestic or international? Age range?
There’s no need to delete you account. An old, unused edX account with no course completions associated with it will disappear.
+
+
I made a mistake creating my username – how do I fix it?
+
+
In most cases it would simplest and fastest to create a new account. Your old unused account will vanish naturally. If you were not aware of your mistake until much later, you should send us a detailed change request and we will do our best to edit your username. Please bear in mind that usernames are unique, and the one you want may be taken.
+
+
I am experiencing problems with the display. E.g., There are tools missing from the course display, or I am unable to view video.