Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion cms/djangoapps/contentstore/views/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from uuid import uuid4
from datetime import datetime
from pytz import UTC
import json

from collections import OrderedDict
from functools import partial
Expand Down Expand Up @@ -642,6 +643,7 @@ def safe_get_username(user_id):
release_date = get_default_time_display(xblock.start) if xblock.start != DEFAULT_START_DATE else None
published = modulestore().has_item(xblock.location, revision=ModuleStoreEnum.RevisionOption.published_only)

graders = CourseGradingModel.fetch(xblock.location.course_key).graders
xblock_info = {
"id": unicode(xblock.location),
"display_name": xblock.display_name_with_default,
Expand All @@ -652,7 +654,13 @@ def safe_get_username(user_id):
'studio_url': xblock_studio_url(xblock),
"released_to_students": datetime.now(UTC) > xblock.start,
"release_date": release_date,
"visibility_state": _compute_visibility_state(xblock, child_info, is_unit_with_changes) if not xblock.category == 'course' else None
"visibility_state": _compute_visibility_state(xblock, child_info, is_unit_with_changes) if not xblock.category == 'course' else None,
"start": xblock.fields['start'].to_json(xblock.start),
"graded": xblock.graded,
"due_date": get_default_time_display(xblock.due),
"due": xblock.fields['due'].to_json(xblock.due),
"format": xblock.format,
"course_graders": json.dumps([grader.get('type') for grader in graders]),
}
if data is not None:
xblock_info["data"] = data
Expand Down
5 changes: 5 additions & 0 deletions cms/djangoapps/contentstore/views/tests/test_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,11 @@ def validate_chapter_xblock_info(self, xblock_info, has_child_info=True):
self.assertEqual(xblock_info['display_name'], 'Week 1')
self.assertTrue(xblock_info['published'])
self.assertIsNone(xblock_info.get('edited_by', None))
self.assertEqual(xblock_info['course_graders'], '["Homework", "Lab", "Midterm Exam", "Final Exam"]')
self.assertEqual(xblock_info['start'], '2030-01-01T00:00:00Z')
self.assertEqual(xblock_info['graded'], False)
self.assertEqual(xblock_info['due'], None)
self.assertEqual(xblock_info['format'], None)

# Finally, validate the entire response for consistency
self.validate_xblock_info_consistency(xblock_info, has_child_info=has_child_info)
Expand Down
1 change: 1 addition & 0 deletions cms/static/coffee/spec/main.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ define([
"js/spec/models/component_template_spec",
"js/spec/models/explicit_url_spec",
"js/spec/models/group_configuration_spec",
"js/spec/models/xblock_info_spec",

"js/spec/utils/drag_and_drop_spec",
"js/spec/utils/handle_iframe_binding_spec",
Expand Down
4 changes: 2 additions & 2 deletions cms/static/js/base.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require(["domReady", "jquery", "underscore", "gettext", "js/views/feedback_notification", "js/views/feedback_prompt",
"js/utils/get_date", "js/utils/module", "js/utils/handle_iframe_binding", "js/utils/change_on_enter", "jquery.ui",
"js/utils/date_utils", "js/utils/module", "js/utils/handle_iframe_binding", "js/utils/change_on_enter", "jquery.ui",
"jquery.leanModal", "jquery.form", "jquery.smoothScroll"],
function(domReady, $, _, gettext, NotificationView, PromptView, DateUtils, ModuleUtils, IframeUtils, TriggerChangeEventOnEnter)
{
Expand Down Expand Up @@ -196,7 +196,7 @@ function saveSubsection() {
// get datetimes for start and due, stick into metadata
_(["start", "due"]).each(function(name) {

var datetime = DateUtils(
var datetime = DateUtils.getDate(
document.getElementById(name+"_date"),
document.getElementById(name+"_time")
);
Expand Down
92 changes: 75 additions & 17 deletions cms/static/js/models/xblock_info.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,52 @@
define(["backbone", "underscore", "js/utils/module"], function(Backbone, _, ModuleUtils) {
define(
['backbone', 'underscore', 'underscore.string', 'js/utils/module'],
function(Backbone, _, str, ModuleUtils) {
'use strict';
var XBlockInfo = Backbone.Model.extend({

urlRoot: ModuleUtils.urlRoot,

// NOTE: 'publish' is not an attribute on XBlockInfo, but it is used to signal the publish
// and discard changes actions. Therefore 'publish' cannot be introduced as an attribute.
defaults: {
"id": null,
"display_name": null,
"category": null,
"data": null,
"metadata" : null,
'id': null,
'display_name': null,
'category': null,
'data': null,
'metadata' : null,
/**
* The Studio URL for this xblock, or null if it doesn't have one.
*/
"studio_url": null,
'studio_url': null,
/**
* An optional object with information about the children as well as about
* the primary xblock type that is supported as a child.
*/
"child_info": null,
'child_info': null,
/**
* An optional object with information about each of the ancestors.
*/
"ancestor_info": null,
'ancestor_info': null,
/**
* Date of the last edit to this xblock or any of its descendants.
*/
"edited_on":null,
'edited_on':null,
/**
* User who last edited the xblock or any of its descendants.
*/
"edited_by":null,
'edited_by':null,
/**
* True iff a published version of the xblock exists.
*/
"published": null,
/**
* Date of the last publish of this xblock, or null if never published.
*/
"published_on": null,
'published_on': null,
/**
* User who last published the xblock, or null if never published.
*/
"published_by": null,
'published_by': null,
/**
* True if the xblock has changes.
* Note: this is not always provided as a performance optimization. It is only provided for
Expand All @@ -58,23 +61,53 @@ define(["backbone", "underscore", "js/utils/module"], function(Backbone, _, Modu
/**
* True iff the release date of the xblock is in the past.
*/
"released_to_students": null,
'released_to_students': null,
/**
* If the xblock is published, the date on which it will be released to students.
* This can be null if the release date is unscheduled.
*/
"release_date": null,
'release_date': null,
/**
* The xblock which is determining the release date. For instance, for a unit,
* this will either be the parent subsection or the grandparent section.
* This can be null if the release date is unscheduled.
*/
"release_date_from":null,
'release_date_from':null,
/**
* True if this xblock is currently visible to students. This is computed server-side
* so that the logic isn't duplicated on the client.
*/
"currently_visible_to_students": null
'currently_visible_to_students': null,
/**
* If xblock is graded, the date after which student assessment will be evaluated.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the format of the date?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: "What is the format of the date?"

* It has same format as release date, for example: 'Jan 02, 2015 at 00:00 UTC'.
*/
'due_date': null,
/**
* Grading policy for xblock.
*/
'format': null,
/**
* List of course graders names.
*/
'course_graders': null,
/**
* True if this xblock contributes to the final course grade.
*/
'graded': null,
/**
* The same as `release_date` but as an ISO-formatted date string.
*/
'start': null,
/**
* The same as `due_date` but as an ISO-formatted date string.
*/
'due': null
},

initialize: function () {
// Extend our Model by helper methods.
_.extend(this, this.getCategoryHelpers());
},

parse: function(response) {
Expand All @@ -100,6 +133,31 @@ define(["backbone", "underscore", "js/utils/module"], function(Backbone, _, Modu
hasChildren: function() {
var childInfo = this.get('child_info');
return childInfo && childInfo.children.length > 0;
},

/**
* Return a list of convenience methods to check affiliation to the category.
* @return {Array}
*/
getCategoryHelpers: function () {
var categories = ['course', 'chapter', 'sequential', 'vertical'],
helpers = {};

_.each(categories, function (item) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! @andy-armstrong, take a quick look.

helpers['is' + str.titleize(item)] = function () {
return this.get('category') === item;
};
}, this);

return helpers;
},

/**
* Check if we can edit current XBlock or not on Course Outline page.
* @return {Boolean}
*/
isEditableOnCourseOutline: function() {
return this.isSequential() || this.isChapter();
}
});
return XBlockInfo;
Expand Down
12 changes: 12 additions & 0 deletions cms/static/js/spec/models/xblock_info_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
define(['backbone', 'js/models/xblock_info'],
function(Backbone, XBlockInfo) {
describe('XblockInfo isEditableOnCourseOutline', function() {
it('works correct', function() {
expect(new XBlockInfo({'category': 'chapter'}).isEditableOnCourseOutline()).toBe(true);
expect(new XBlockInfo({'category': 'course'}).isEditableOnCourseOutline()).toBe(false);
expect(new XBlockInfo({'category': 'sequential'}).isEditableOnCourseOutline()).toBe(true);
expect(new XBlockInfo({'category': 'vertical'}).isEditableOnCourseOutline()).toBe(false);
});
});
}
);
Loading