Merge remote-tracking branch 'edx/master' into ok-merge-from-master
This commit is contained in:
1
AUTHORS
1
AUTHORS
@@ -146,3 +146,4 @@ Ben Weeks <benweeks@mit.edu>
|
||||
David Bodor <david.gabor.bodor@gmail.com>
|
||||
Sébastien Hinderer <Sebastien.Hinderer@inria.fr>
|
||||
Kristin Stephens <ksteph@cs.berkeley.edu>
|
||||
Ben Patterson <bpatterson@edx.org>
|
||||
|
||||
@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
|
||||
in roughly chronological order, most recent first. Add your entries at or near
|
||||
the top. Include a label indicating the component affected.
|
||||
|
||||
Blades: Fix displaying transcripts on touch devices. BLD-1033.
|
||||
|
||||
Blades: Tolerance expressed in percentage now computes correctly. BLD-522.
|
||||
|
||||
Studio: Support add, delete and duplicate on the container page. STUD-1490.
|
||||
|
||||
@@ -10,10 +10,11 @@ Feature: Course export
|
||||
Then I get an error dialog
|
||||
And I can click to go to the unit with the error
|
||||
|
||||
Scenario: User is directed to problem with & in it when export fails
|
||||
Given I am in Studio editing a new unit
|
||||
When I add a "Blank Advanced Problem" "Advanced Problem" component
|
||||
And I edit and enter an ampersand
|
||||
And I export the course
|
||||
Then I get an error dialog
|
||||
And I can click to go to the unit with the error
|
||||
# Disabling due to failure on master. 05/21/2014 TODO: fix
|
||||
# Scenario: User is directed to problem with & in it when export fails
|
||||
# Given I am in Studio editing a new unit
|
||||
# When I add a "Blank Advanced Problem" "Advanced Problem" component
|
||||
# And I edit and enter an ampersand
|
||||
# And I export the course
|
||||
# Then I get an error dialog
|
||||
# And I can click to go to the unit with the error
|
||||
|
||||
@@ -105,26 +105,28 @@ Feature: CMS.HTML Editor
|
||||
<li>zzzz<ol>
|
||||
"""
|
||||
|
||||
Scenario: Can switch from Visual Editor to Raw
|
||||
Given I have created a Blank HTML Page
|
||||
When I edit the component and select the Raw Editor
|
||||
And I save the page
|
||||
When I edit the page
|
||||
And type "fancy html" into the Raw Editor
|
||||
And I save the page
|
||||
Then the page text contains:
|
||||
"""
|
||||
fancy html
|
||||
"""
|
||||
# Skipping in master due to brittleness JZ 05/22/2014
|
||||
# Scenario: Can switch from Visual Editor to Raw
|
||||
# Given I have created a Blank HTML Page
|
||||
# When I edit the component and select the Raw Editor
|
||||
# And I save the page
|
||||
# When I edit the page
|
||||
# And type "fancy html" into the Raw Editor
|
||||
# And I save the page
|
||||
# Then the page text contains:
|
||||
# """
|
||||
# fancy html
|
||||
# """
|
||||
|
||||
Scenario: Can switch from Raw Editor to Visual
|
||||
Given I have created a raw HTML component
|
||||
And I edit the component and select the Visual Editor
|
||||
And I save the page
|
||||
When I edit the page
|
||||
And type "less fancy html" in the code editor and press OK
|
||||
And I save the page
|
||||
Then the page text contains:
|
||||
"""
|
||||
less fancy html
|
||||
"""
|
||||
# Skipping in master due to brittleness JZ 05/22/2014
|
||||
# Scenario: Can switch from Raw Editor to Visual
|
||||
# Given I have created a raw HTML component
|
||||
# And I edit the component and select the Visual Editor
|
||||
# And I save the page
|
||||
# When I edit the page
|
||||
# And type "less fancy html" in the code editor and press OK
|
||||
# And I save the page
|
||||
# Then the page text contains:
|
||||
# """
|
||||
# less fancy html
|
||||
# """
|
||||
|
||||
@@ -6,9 +6,12 @@ Feature: CMS.Upload Files
|
||||
@skip_safari
|
||||
Scenario: Users can upload files
|
||||
Given I am at the files and upload page of a Studio course
|
||||
When I upload the file "test"
|
||||
When I upload the file "test" by clicking "Upload your first asset"
|
||||
Then I should see the file "test" was uploaded
|
||||
And The url for the file "test" is valid
|
||||
When I upload the file "test2"
|
||||
Then I should see the file "test2" was uploaded
|
||||
And The url for the file "test2" is valid
|
||||
|
||||
# Uploading isn't working on safari with sauce labs
|
||||
@skip_safari
|
||||
|
||||
@@ -25,8 +25,11 @@ def go_to_uploads(_step):
|
||||
|
||||
|
||||
@step(u'I upload the( test)? file "([^"]*)"$')
|
||||
def upload_file(_step, is_test_file, file_name):
|
||||
world.click_link('Upload New File')
|
||||
def upload_file(_step, is_test_file, file_name, button_text=None):
|
||||
if button_text:
|
||||
world.click_link(button_text)
|
||||
else:
|
||||
world.click_link('Upload New File')
|
||||
|
||||
if not is_test_file:
|
||||
_write_test_file(file_name, "test file")
|
||||
@@ -39,6 +42,11 @@ def upload_file(_step, is_test_file, file_name):
|
||||
world.css_click(close_css)
|
||||
|
||||
|
||||
@step(u'I upload the file "([^"]*)" by clicking "([^"]*)"')
|
||||
def upload_file_on_button_press(_step, file_name, button_text=None):
|
||||
upload_file(_step, '', file_name, button_text)
|
||||
|
||||
|
||||
@step(u'I upload the files "([^"]*)"$')
|
||||
def upload_files(_step, files_string):
|
||||
# files_string should be comma separated with no spaces.
|
||||
|
||||
@@ -16,6 +16,8 @@ from contentstore.utils import get_modulestore, reverse_course_url
|
||||
from .access import has_course_access
|
||||
from xmodule.course_module import CourseDescriptor
|
||||
|
||||
from django.utils.translation import ugettext
|
||||
|
||||
__all__ = ['checklists_handler']
|
||||
|
||||
|
||||
@@ -76,7 +78,7 @@ def checklists_handler(request, course_key_string, checklist_index=None):
|
||||
course_module.save()
|
||||
get_modulestore(course_module.location).update_item(course_module, request.user.id)
|
||||
expanded_checklist = expand_checklist_action_url(course_module, persisted_checklist)
|
||||
return JsonResponse(expanded_checklist)
|
||||
return JsonResponse(localize_checklist_text(expanded_checklist))
|
||||
else:
|
||||
return HttpResponseBadRequest(
|
||||
("Could not save checklist state because the checklist index "
|
||||
@@ -96,7 +98,7 @@ def expand_all_action_urls(course_module):
|
||||
"""
|
||||
expanded_checklists = []
|
||||
for checklist in course_module.checklists:
|
||||
expanded_checklists.append(expand_checklist_action_url(course_module, checklist))
|
||||
expanded_checklists.append(localize_checklist_text(expand_checklist_action_url(course_module, checklist)))
|
||||
return expanded_checklists
|
||||
|
||||
|
||||
@@ -121,3 +123,20 @@ def expand_checklist_action_url(course_module, checklist):
|
||||
item['action_url'] = reverse_course_url(urlconf_map[action_url], course_module.id)
|
||||
|
||||
return expanded_checklist
|
||||
|
||||
def localize_checklist_text(checklist):
|
||||
"""
|
||||
Localize texts for a given checklist and returns the modified version.
|
||||
|
||||
The method does an in-place operation so the input checklist is modified directly.
|
||||
"""
|
||||
# Localize checklist name
|
||||
checklist['short_description'] = ugettext(checklist['short_description'])
|
||||
|
||||
# Localize checklist items
|
||||
for item in checklist.get('items'):
|
||||
item['short_description'] = ugettext(item['short_description'])
|
||||
item['long_description'] = ugettext(item['long_description'])
|
||||
item['action_text'] = ugettext(item['action_text']) if item['action_text'] != "" else u""
|
||||
|
||||
return checklist
|
||||
|
||||
@@ -220,6 +220,7 @@ define([
|
||||
|
||||
"js/spec/views/baseview_spec",
|
||||
"js/spec/views/paging_spec",
|
||||
"js/spec/views/assets_spec",
|
||||
|
||||
"js/spec/views/container_spec",
|
||||
"js/spec/views/unit_spec",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
define ["jasmine", "js/spec_helpers/create_sinon", "squire"],
|
||||
(jasmine, create_sinon, Squire) ->
|
||||
define ["jquery", "jasmine", "js/spec_helpers/create_sinon", "squire"],
|
||||
($, jasmine, create_sinon, Squire) ->
|
||||
|
||||
feedbackTpl = readFixtures('system-feedback.underscore')
|
||||
assetLibraryTpl = readFixtures('asset-library.underscore')
|
||||
@@ -236,6 +236,33 @@ define ["jasmine", "js/spec_helpers/create_sinon", "squire"],
|
||||
create_sinon.respondWithJson(requests, @mockAssetsResponse)
|
||||
return requests
|
||||
|
||||
$.fn.fileupload = ->
|
||||
return ''
|
||||
|
||||
clickEvent = (html_selector) ->
|
||||
$(html_selector).click()
|
||||
|
||||
it "should show upload modal on clicking upload asset button", ->
|
||||
spyOn(@view, "showUploadModal")
|
||||
setup.call(this)
|
||||
expect(@view.showUploadModal).not.toHaveBeenCalled()
|
||||
@view.showUploadModal(clickEvent(".upload-button"))
|
||||
expect(@view.showUploadModal).toHaveBeenCalled()
|
||||
|
||||
it "should show file selection menu on choose file button", ->
|
||||
spyOn(@view, "showFileSelectionMenu")
|
||||
setup.call(this)
|
||||
expect(@view.showFileSelectionMenu).not.toHaveBeenCalled()
|
||||
@view.showFileSelectionMenu(clickEvent(".choose-file-button"))
|
||||
expect(@view.showFileSelectionMenu).toHaveBeenCalled()
|
||||
|
||||
it "should hide upload modal on clicking close button", ->
|
||||
spyOn(@view, "hideModal")
|
||||
setup.call(this)
|
||||
expect(@view.hideModal).not.toHaveBeenCalled()
|
||||
@view.hideModal(clickEvent(".close-button"))
|
||||
expect(@view.hideModal).toHaveBeenCalled()
|
||||
|
||||
it "should show a status indicator while loading", ->
|
||||
appendSetFixtures('<div class="ui-loading"/>')
|
||||
expect($('.ui-loading').is(':visible')).toBe(true)
|
||||
|
||||
129
cms/static/js/spec/views/assets_spec.js
Normal file
129
cms/static/js/spec/views/assets_spec.js
Normal file
@@ -0,0 +1,129 @@
|
||||
define([ "jquery", "js/spec_helpers/create_sinon", "js/views/asset", "js/views/assets",
|
||||
"js/models/asset", "js/collections/asset" ],
|
||||
function ($, create_sinon, AssetView, AssetsView, AssetModel, AssetCollection) {
|
||||
|
||||
describe("Assets", function() {
|
||||
var assetsView, mockEmptyAssetsResponse, mockAssetUploadResponse,
|
||||
assetLibraryTpl, assetTpl, pagingFooterTpl, pagingHeaderTpl, uploadModalTpl;
|
||||
|
||||
assetLibraryTpl = readFixtures('asset-library.underscore');
|
||||
assetTpl = readFixtures('asset.underscore');
|
||||
pagingHeaderTpl = readFixtures('paging-header.underscore');
|
||||
pagingFooterTpl = readFixtures('paging-footer.underscore');
|
||||
uploadModalTpl = readFixtures('asset-upload-modal.underscore');
|
||||
|
||||
beforeEach(function () {
|
||||
setFixtures($("<script>", { id: "asset-library-tpl", type: "text/template" }).text(assetLibraryTpl));
|
||||
appendSetFixtures($("<script>", { id: "asset-tpl", type: "text/template" }).text(assetTpl));
|
||||
appendSetFixtures($("<script>", { id: "paging-header-tpl", type: "text/template" }).text(pagingHeaderTpl));
|
||||
appendSetFixtures($("<script>", { id: "paging-footer-tpl", type: "text/template" }).text(pagingFooterTpl));
|
||||
appendSetFixtures(uploadModalTpl);
|
||||
appendSetFixtures(sandbox({ id: "asset_table_body" }));
|
||||
|
||||
var collection = new AssetCollection();
|
||||
collection.url = "assets-url";
|
||||
assetsView = new AssetsView({
|
||||
collection: collection,
|
||||
el: $('#asset_table_body')
|
||||
});
|
||||
assetsView.render();
|
||||
});
|
||||
|
||||
var mockAsset = {
|
||||
display_name: "dummy.jpg",
|
||||
url: 'actual_asset_url',
|
||||
portable_url: 'portable_url',
|
||||
date_added: 'date',
|
||||
thumbnail: null,
|
||||
locked: false,
|
||||
id: 'id_1'
|
||||
};
|
||||
|
||||
mockEmptyAssetsResponse = {
|
||||
assets: [],
|
||||
start: 0,
|
||||
end: 0,
|
||||
page: 0,
|
||||
pageSize: 5,
|
||||
totalCount: 0
|
||||
};
|
||||
|
||||
mockAssetUploadResponse = {
|
||||
asset: mockAsset,
|
||||
msg: "Upload completed"
|
||||
};
|
||||
|
||||
$.fn.fileupload = function() {
|
||||
return '';
|
||||
};
|
||||
|
||||
var event = {}
|
||||
event.target = {"value": "dummy.jpg"};
|
||||
|
||||
describe("AssetsView", function () {
|
||||
var setup;
|
||||
setup = function() {
|
||||
var requests;
|
||||
requests = create_sinon.requests(this);
|
||||
assetsView.setPage(0);
|
||||
create_sinon.respondWithJson(requests, mockEmptyAssetsResponse);
|
||||
return requests;
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
window.analytics = jasmine.createSpyObj('analytics', ['track']);
|
||||
window.course_location_analytics = jasmine.createSpy();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
delete window.analytics;
|
||||
delete window.course_location_analytics;
|
||||
});
|
||||
|
||||
it('shows the upload modal when clicked on "Upload your first asset" button', function () {
|
||||
expect(assetsView).toBeDefined();
|
||||
appendSetFixtures('<div class="ui-loading"/>');
|
||||
expect($('.ui-loading').is(':visible')).toBe(true);
|
||||
expect($('.upload-button').is(':visible')).toBe(false);
|
||||
setup.call(this);
|
||||
expect($('.ui-loading').is(':visible')).toBe(false);
|
||||
expect($('.upload-button').is(':visible')).toBe(true);
|
||||
|
||||
expect($('.upload-modal').is(':visible')).toBe(false);
|
||||
$('a:contains("Upload your first asset")').click();
|
||||
expect($('.upload-modal').is(':visible')).toBe(true);
|
||||
|
||||
$('.close-button').click();
|
||||
expect($('.upload-modal').is(':visible')).toBe(false);
|
||||
});
|
||||
|
||||
it('uploads file properly', function () {
|
||||
var requests = setup.call(this);
|
||||
expect(assetsView).toBeDefined();
|
||||
spyOn(assetsView, "addAsset").andCallFake(function () {
|
||||
assetsView.collection.add(mockAssetUploadResponse.asset);
|
||||
assetsView.renderPageItems();
|
||||
assetsView.setPage(0);
|
||||
});
|
||||
|
||||
$('a:contains("Upload your first asset")').click();
|
||||
expect($('.upload-modal').is(':visible')).toBe(true);
|
||||
|
||||
$('.choose-file-button').click();
|
||||
$("input[type=file]").change();
|
||||
expect($('.upload-modal h1').text()).toContain("Uploading");
|
||||
|
||||
assetsView.showUploadFeedback(event, 100);
|
||||
expect($('div.progress-bar').text()).toContain("100%");
|
||||
|
||||
assetsView.displayFinishedUpload(mockAssetUploadResponse);
|
||||
expect($('div.progress-bar').text()).toContain("Upload completed");
|
||||
$('.close-button').click();
|
||||
expect($('.upload-modal').is(':visible')).toBe(false);
|
||||
|
||||
expect($('#asset_table_body').html()).toContain("dummy.jpg");
|
||||
expect(assetsView.collection.length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,13 @@
|
||||
define(["jquery", "underscore", "gettext", "js/views/paging", "js/views/asset", "js/views/paging_header", "js/views/paging_footer"],
|
||||
function($, _, gettext, PagingView, AssetView, PagingHeader, PagingFooter) {
|
||||
define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging", "js/views/asset",
|
||||
"js/views/paging_header", "js/views/paging_footer", "js/utils/modal"],
|
||||
function($, _, gettext, AssetModel, PagingView, AssetView, PagingHeader, PagingFooter, ModalUtils) {
|
||||
|
||||
var AssetsView = PagingView.extend({
|
||||
// takes AssetCollection as model
|
||||
|
||||
events : {
|
||||
"click .column-sort-link": "onToggleColumn"
|
||||
"click .column-sort-link": "onToggleColumn",
|
||||
"click .upload-button": "showUploadModal"
|
||||
},
|
||||
|
||||
initialize : function() {
|
||||
@@ -17,6 +19,8 @@ define(["jquery", "underscore", "gettext", "js/views/paging", "js/views/asset",
|
||||
this.registerSortableColumn('js-asset-date-col', gettext('Date Added'), 'date_added', 'desc');
|
||||
this.setInitialSortColumn('js-asset-date-col');
|
||||
this.showLoadingIndicator();
|
||||
this.setPage(0);
|
||||
assetsView = this;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@@ -24,6 +28,14 @@ define(["jquery", "underscore", "gettext", "js/views/paging", "js/views/asset",
|
||||
return this;
|
||||
},
|
||||
|
||||
afterRender: function(){
|
||||
// Bind events with html elements
|
||||
$('li a.upload-button').on('click', this.showUploadModal);
|
||||
$('.upload-modal .close-button').on('click', this.hideModal);
|
||||
$('.upload-modal .choose-file-button').on('click', this.showFileSelectionMenu);
|
||||
return this;
|
||||
},
|
||||
|
||||
getTableBody: function() {
|
||||
var tableBody = this.tableBody;
|
||||
if (!tableBody) {
|
||||
@@ -47,9 +59,9 @@ define(["jquery", "underscore", "gettext", "js/views/paging", "js/views/asset",
|
||||
|
||||
renderPageItems: function() {
|
||||
var self = this,
|
||||
assets = this.collection,
|
||||
hasAssets = assets.length > 0,
|
||||
tableBody = this.getTableBody();
|
||||
assets = this.collection,
|
||||
hasAssets = assets.length > 0,
|
||||
tableBody = this.getTableBody();
|
||||
tableBody.empty();
|
||||
if (hasAssets) {
|
||||
assets.each(
|
||||
@@ -91,6 +103,92 @@ define(["jquery", "underscore", "gettext", "js/views/paging", "js/views/asset",
|
||||
onToggleColumn: function(event) {
|
||||
var columnName = event.target.id;
|
||||
this.toggleSortOrder(columnName);
|
||||
},
|
||||
|
||||
hideModal: function (event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
$('.file-input').unbind('change.startUpload');
|
||||
ModalUtils.hideModal();
|
||||
},
|
||||
|
||||
showUploadModal: function (event) {
|
||||
var self = assetsView;
|
||||
event.preventDefault();
|
||||
self.resetUploadModal();
|
||||
ModalUtils.showModal();
|
||||
$('.file-input').bind('change', self.startUpload);
|
||||
$('.upload-modal .file-chooser').fileupload({
|
||||
dataType: 'json',
|
||||
type: 'POST',
|
||||
maxChunkSize: 100 * 1000 * 1000, // 100 MB
|
||||
autoUpload: true,
|
||||
progressall: function(event, data) {
|
||||
var percentComplete = parseInt((100 * data.loaded) / data.total, 10);
|
||||
self.showUploadFeedback(event, percentComplete);
|
||||
},
|
||||
maxFileSize: 100 * 1000 * 1000, // 100 MB
|
||||
maxNumberofFiles: 100,
|
||||
add: function(event, data) {
|
||||
data.process().done(function () {
|
||||
data.submit();
|
||||
});
|
||||
},
|
||||
done: function(event, data) {
|
||||
self.displayFinishedUpload(data.result);
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
showFileSelectionMenu: function(event) {
|
||||
event.preventDefault();
|
||||
$('.file-input').click();
|
||||
},
|
||||
|
||||
startUpload: function (event) {
|
||||
var file = event.target.value;
|
||||
|
||||
$('.upload-modal h1').text(gettext('Uploading…'));
|
||||
$('.upload-modal .file-name').html(file.substring(file.lastIndexOf("\\") + 1));
|
||||
$('.upload-modal .choose-file-button').hide();
|
||||
$('.upload-modal .progress-bar').removeClass('loaded').show();
|
||||
},
|
||||
|
||||
resetUploadModal: function () {
|
||||
// Reset modal so it no longer displays information about previously
|
||||
// completed uploads.
|
||||
var percentVal = '0%';
|
||||
$('.upload-modal .progress-fill').width(percentVal);
|
||||
$('.upload-modal .progress-fill').html(percentVal);
|
||||
$('.upload-modal .progress-bar').hide();
|
||||
|
||||
$('.upload-modal .file-name').show();
|
||||
$('.upload-modal .file-name').html('');
|
||||
$('.upload-modal .choose-file-button').text(gettext('Choose File'));
|
||||
$('.upload-modal .embeddable-xml-input').val('');
|
||||
$('.upload-modal .embeddable').hide();
|
||||
},
|
||||
|
||||
showUploadFeedback: function (event, percentComplete) {
|
||||
var percentVal = percentComplete + '%';
|
||||
$('.upload-modal .progress-fill').width(percentVal);
|
||||
$('.upload-modal .progress-fill').html(percentVal);
|
||||
},
|
||||
|
||||
displayFinishedUpload: function (resp) {
|
||||
var asset = resp.asset;
|
||||
|
||||
$('.upload-modal h1').text(gettext('Upload New File'));
|
||||
$('.upload-modal .embeddable-xml-input').val(asset.portable_url);
|
||||
$('.upload-modal .embeddable').show();
|
||||
$('.upload-modal .file-name').hide();
|
||||
$('.upload-modal .progress-fill').html(resp.msg);
|
||||
$('.upload-modal .choose-file-button').text(gettext('Load Another File')).show();
|
||||
$('.upload-modal .progress-fill').width('100%');
|
||||
|
||||
assetsView.addAsset(new AssetModel(asset));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -19,112 +19,16 @@
|
||||
|
||||
<%block name="jsextra">
|
||||
<script type="text/javascript">
|
||||
require(["domReady", "jquery", "js/models/asset", "js/collections/asset",
|
||||
"js/views/assets", "js/views/feedback_prompt",
|
||||
"js/views/feedback_notification", "js/views/paging_header", "js/views/paging_footer",
|
||||
"js/utils/modal", "jquery.fileupload"],
|
||||
function(domReady, $, AssetModel, AssetCollection, AssetsView, PromptView, NotificationView,
|
||||
PagingHeader, PagingFooter, ModalUtils) {
|
||||
var assets = new AssetCollection();
|
||||
require(["jquery", "js/collections/asset", "js/views/assets", "jquery.fileupload"],
|
||||
function($, AssetCollection, AssetsView) {
|
||||
|
||||
assets.url = "${asset_callback_url}";
|
||||
var assetsView = new AssetsView({collection: assets, el: $('.assets-wrapper')});
|
||||
assetsView.render();
|
||||
assetsView.setPage(0);
|
||||
var assets = new AssetCollection();
|
||||
assets.url = "${asset_callback_url}";
|
||||
var assetsView = new AssetsView({collection: assets, el: $('.assets-wrapper')});
|
||||
assetsView.render();
|
||||
|
||||
var hideModal = function (e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
$('.file-input').unbind('change.startUpload');
|
||||
ModalUtils.hideModal();
|
||||
};
|
||||
|
||||
var showUploadModal = function (e) {
|
||||
e.preventDefault();
|
||||
resetUploadModal();
|
||||
ModalUtils.showModal();
|
||||
$('.file-input').bind('change', startUpload);
|
||||
$('.upload-modal .file-chooser').fileupload({
|
||||
dataType: 'json',
|
||||
type: 'POST',
|
||||
maxChunkSize: 100 * 1000 * 1000, // 100 MB
|
||||
autoUpload: true,
|
||||
progressall: function(e, data) {
|
||||
var percentComplete = parseInt((100 * data.loaded) / data.total, 10);
|
||||
showUploadFeedback(e, percentComplete);
|
||||
},
|
||||
maxFileSize: 100 * 1000 * 1000, // 100 MB
|
||||
maxNumberofFiles: 100,
|
||||
add: function(e, data) {
|
||||
data.process().done(function () {
|
||||
data.submit();
|
||||
});
|
||||
},
|
||||
done: function(e, data) {
|
||||
displayFinishedUpload(data.result);
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
var showFileSelectionMenu = function(e) {
|
||||
e.preventDefault();
|
||||
$('.file-input').click();
|
||||
};
|
||||
|
||||
var startUpload = function (e) {
|
||||
var file = e.target.value;
|
||||
|
||||
$('.upload-modal h1').text("${_(u'Uploading…')}");
|
||||
$('.upload-modal .file-name').html(file.substring(file.lastIndexOf("\\") + 1));
|
||||
$('.upload-modal .choose-file-button').hide();
|
||||
$('.upload-modal .progress-bar').removeClass('loaded').show();
|
||||
};
|
||||
|
||||
var resetUploadModal = function () {
|
||||
// Reset modal so it no longer displays information about previously
|
||||
// completed uploads.
|
||||
var percentVal = '0%';
|
||||
$('.upload-modal .progress-fill').width(percentVal);
|
||||
$('.upload-modal .progress-fill').html(percentVal);
|
||||
$('.upload-modal .progress-bar').hide();
|
||||
|
||||
$('.upload-modal .file-name').show();
|
||||
$('.upload-modal .file-name').html('');
|
||||
$('.upload-modal .choose-file-button').text("${_('Choose File')}");
|
||||
$('.upload-modal .embeddable-xml-input').val('');
|
||||
$('.upload-modal .embeddable').hide();
|
||||
};
|
||||
|
||||
var showUploadFeedback = function (event, percentComplete) {
|
||||
var percentVal = percentComplete + '%';
|
||||
$('.upload-modal .progress-fill').width(percentVal);
|
||||
$('.upload-modal .progress-fill').html(percentVal);
|
||||
};
|
||||
|
||||
var displayFinishedUpload = function (resp) {
|
||||
var asset = resp.asset;
|
||||
|
||||
$('.upload-modal h1').text("${_('Upload New File')}");
|
||||
$('.upload-modal .embeddable-xml-input').val(asset.portable_url);
|
||||
$('.upload-modal .embeddable').show();
|
||||
$('.upload-modal .file-name').hide();
|
||||
$('.upload-modal .progress-fill').html(resp.msg);
|
||||
$('.upload-modal .choose-file-button').text("${_('Load Another File')}").show();
|
||||
$('.upload-modal .progress-fill').width('100%');
|
||||
|
||||
assetsView.addAsset(new AssetModel(asset));
|
||||
};
|
||||
|
||||
domReady(function() {
|
||||
$('.uploads .upload-button').bind('click', showUploadModal);
|
||||
$('.upload-modal .close-button').bind('click', hideModal);
|
||||
$('.upload-modal .choose-file-button').bind('click', showFileSelectionMenu);
|
||||
});
|
||||
|
||||
}); // end of require()
|
||||
</script>
|
||||
}); // end of require()
|
||||
</script>
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
%>
|
||||
<%block name="title">Course Checklists</%block>
|
||||
<%block name="title">${_("Course Checklists")}</%block>
|
||||
<%block name="bodyclass">is-signedin course view-checklists</%block>
|
||||
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
@@ -64,7 +64,7 @@ require(["domReady!", "jquery", "js/collections/checklist", "js/views/checklist"
|
||||
</div>
|
||||
|
||||
<div class="bit">
|
||||
<h3 class="title title-3">Studio checklists</h3>
|
||||
<h3 class="title title-3">${_("Studio checklists")}</h3>
|
||||
<nav class="nav-page checklists-current">
|
||||
<ol>
|
||||
% for checklist in checklists:
|
||||
|
||||
19
cms/templates/js/asset-upload-modal.underscore
Normal file
19
cms/templates/js/asset-upload-modal.underscore
Normal file
@@ -0,0 +1,19 @@
|
||||
<div class="upload-modal modal" style="display: none;">
|
||||
<a href="#" class="close-button"><i class="icon-remove-sign"></i> <span class="sr"><%= gettext('close') %></span></a>
|
||||
<div class="modal-body">
|
||||
<h1 class="title"><%= gettext("Upload New File") %></h1>
|
||||
<p class="file-name">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill"></div>
|
||||
</div>
|
||||
<div class="embeddable">
|
||||
<label>URL:</label>
|
||||
<input type="text" class="embeddable-xml-input" value='' readonly>
|
||||
</div>
|
||||
<form class="file-chooser" action="asset-url"
|
||||
method="post" enctype="multipart/form-data">
|
||||
<a href="#" class="choose-file-button"><%= gettext("Choose File") %></a>
|
||||
<input type="file" class="file-input" name="file">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,4 +1,4 @@
|
||||
<section id="inputtype_${id}" class="capa_inputtype" >
|
||||
<div id="inputtype_${id}" class="capa_inputtype">
|
||||
<div class="drag_and_drop_problem_div" id="drag_and_drop_div_${id}"
|
||||
data-plain-id="${id}">
|
||||
</div>
|
||||
@@ -29,4 +29,4 @@
|
||||
% if status in ['unsubmitted', 'correct', 'incorrect', 'incomplete']:
|
||||
</div>
|
||||
% endif
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -10,9 +10,12 @@ import textwrap
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
class AnnotatableFields(object):
|
||||
data = String(help="XML data for the annotation", scope=Scope.content,
|
||||
data = String(help=_("XML data for the annotation"), scope=Scope.content,
|
||||
default=textwrap.dedent(
|
||||
"""\
|
||||
<annotatable>
|
||||
@@ -32,8 +35,8 @@ class AnnotatableFields(object):
|
||||
</annotatable>
|
||||
"""))
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="Display name for this module",
|
||||
display_name=_("Display Name"),
|
||||
help=_("Display name for this module"),
|
||||
scope=Scope.settings,
|
||||
default='Annotation',
|
||||
)
|
||||
|
||||
@@ -32,6 +32,9 @@ from .util.duedate import get_extended_due_date
|
||||
|
||||
log = logging.getLogger("edx.courseware")
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
# Generate this many different variants of problems with rerandomize=per_student
|
||||
NUM_RANDOMIZATION_BINS = 20
|
||||
@@ -86,101 +89,103 @@ class CapaFields(object):
|
||||
Define the possible fields for a Capa problem
|
||||
"""
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="This name appears in the horizontal navigation at the top of the page.",
|
||||
display_name=_("Display Name"),
|
||||
help=_("This name appears in the horizontal navigation at the top of the page."),
|
||||
scope=Scope.settings,
|
||||
# it'd be nice to have a useful default but it screws up other things; so,
|
||||
# use display_name_with_default for those
|
||||
default="Blank Advanced Problem"
|
||||
)
|
||||
attempts = Integer(help="Number of attempts taken by the student on this problem",
|
||||
default=0, scope=Scope.user_state)
|
||||
attempts = Integer(
|
||||
help=_("Number of attempts taken by the student on this problem"),
|
||||
default=0,
|
||||
scope=Scope.user_state)
|
||||
max_attempts = Integer(
|
||||
display_name="Maximum Attempts",
|
||||
help=("Defines the number of times a student can try to answer this problem. "
|
||||
display_name=_("Maximum Attempts"),
|
||||
help=_("Defines the number of times a student can try to answer this problem. "
|
||||
"If the value is not set, infinite attempts are allowed."),
|
||||
values={"min": 0}, scope=Scope.settings
|
||||
)
|
||||
due = Date(help="Date that this problem is due by", scope=Scope.settings)
|
||||
due = Date(help=_("Date that this problem is due by"), scope=Scope.settings)
|
||||
extended_due = Date(
|
||||
help="Date that this problem is due by for a particular student. This "
|
||||
help=_("Date that this problem is due by for a particular student. This "
|
||||
"can be set by an instructor, and will override the global due "
|
||||
"date if it is set to a date that is later than the global due "
|
||||
"date.",
|
||||
"date."),
|
||||
default=None,
|
||||
scope=Scope.user_state,
|
||||
)
|
||||
graceperiod = Timedelta(
|
||||
help="Amount of time after the due date that submissions will be accepted",
|
||||
help=_("Amount of time after the due date that submissions will be accepted"),
|
||||
scope=Scope.settings
|
||||
)
|
||||
showanswer = String(
|
||||
display_name="Show Answer",
|
||||
help=("Defines when to show the answer to the problem. "
|
||||
display_name=_("Show Answer"),
|
||||
help=_("Defines when to show the answer to the problem. "
|
||||
"A default value can be set in Advanced Settings."),
|
||||
scope=Scope.settings,
|
||||
default="finished",
|
||||
values=[
|
||||
{"display_name": "Always", "value": "always"},
|
||||
{"display_name": "Answered", "value": "answered"},
|
||||
{"display_name": "Attempted", "value": "attempted"},
|
||||
{"display_name": "Closed", "value": "closed"},
|
||||
{"display_name": "Finished", "value": "finished"},
|
||||
{"display_name": "Past Due", "value": "past_due"},
|
||||
{"display_name": "Never", "value": "never"}]
|
||||
{"display_name": _("Always"), "value": "always"},
|
||||
{"display_name": _("Answered"), "value": "answered"},
|
||||
{"display_name": _("Attempted"), "value": "attempted"},
|
||||
{"display_name": _("Closed"), "value": "closed"},
|
||||
{"display_name": _("Finished"), "value": "finished"},
|
||||
{"display_name": _("Past Due"), "value": "past_due"},
|
||||
{"display_name": _("Never"), "value": "never"}]
|
||||
)
|
||||
force_save_button = Boolean(
|
||||
help="Whether to force the save button to appear on the page",
|
||||
help=_("Whether to force the save button to appear on the page"),
|
||||
scope=Scope.settings,
|
||||
default=False
|
||||
)
|
||||
rerandomize = Randomization(
|
||||
display_name="Randomization",
|
||||
help="Defines how often inputs are randomized when a student loads the problem. "
|
||||
display_name=_("Randomization"),
|
||||
help=_("Defines how often inputs are randomized when a student loads the problem. "
|
||||
"This setting only applies to problems that can have randomly generated numeric values. "
|
||||
"A default value can be set in Advanced Settings.",
|
||||
"A default value can be set in Advanced Settings."),
|
||||
default="never",
|
||||
scope=Scope.settings,
|
||||
values=[
|
||||
{"display_name": "Always", "value": "always"},
|
||||
{"display_name": "On Reset", "value": "onreset"},
|
||||
{"display_name": "Never", "value": "never"},
|
||||
{"display_name": "Per Student", "value": "per_student"}
|
||||
{"display_name": _("Always"), "value": "always"},
|
||||
{"display_name": _("On Reset"), "value": "onreset"},
|
||||
{"display_name": _("Never"), "value": "never"},
|
||||
{"display_name": _("Per Student"), "value": "per_student"}
|
||||
]
|
||||
)
|
||||
data = String(help="XML data for the problem", scope=Scope.content, default="<problem></problem>")
|
||||
correct_map = Dict(help="Dictionary with the correctness of current student answers",
|
||||
data = String(help=_("XML data for the problem"), scope=Scope.content, default="<problem></problem>")
|
||||
correct_map = Dict(help=_("Dictionary with the correctness of current student answers"),
|
||||
scope=Scope.user_state, default={})
|
||||
input_state = Dict(help="Dictionary for maintaining the state of inputtypes", scope=Scope.user_state)
|
||||
student_answers = Dict(help="Dictionary with the current student responses", scope=Scope.user_state)
|
||||
done = Boolean(help="Whether the student has answered the problem", scope=Scope.user_state)
|
||||
seed = Integer(help="Random seed for this student", scope=Scope.user_state)
|
||||
last_submission_time = Date(help="Last submission time", scope=Scope.user_state)
|
||||
input_state = Dict(help=_("Dictionary for maintaining the state of inputtypes"), scope=Scope.user_state)
|
||||
student_answers = Dict(help=_("Dictionary with the current student responses"), scope=Scope.user_state)
|
||||
done = Boolean(help=_("Whether the student has answered the problem"), scope=Scope.user_state)
|
||||
seed = Integer(help=_("Random seed for this student"), scope=Scope.user_state)
|
||||
last_submission_time = Date(help=_("Last submission time"), scope=Scope.user_state)
|
||||
submission_wait_seconds = Integer(
|
||||
display_name="Timer Between Attempts",
|
||||
help="Seconds a student must wait between submissions for a problem with multiple attempts.",
|
||||
display_name=_("Timer Between Attempts"),
|
||||
help=_("Seconds a student must wait between submissions for a problem with multiple attempts."),
|
||||
scope=Scope.settings,
|
||||
default=0)
|
||||
weight = Float(
|
||||
display_name="Problem Weight",
|
||||
help=("Defines the number of points each problem is worth. "
|
||||
display_name=_("Problem Weight"),
|
||||
help=_("Defines the number of points each problem is worth. "
|
||||
"If the value is not set, each response field in the problem is worth one point."),
|
||||
values={"min": 0, "step": .1},
|
||||
scope=Scope.settings
|
||||
)
|
||||
markdown = String(help="Markdown source of this module", default=None, scope=Scope.settings)
|
||||
markdown = String(help=_("Markdown source of this module"), default=None, scope=Scope.settings)
|
||||
source_code = String(
|
||||
help="Source code for LaTeX and Word problems. This feature is not well-supported.",
|
||||
help=_("Source code for LaTeX and Word problems. This feature is not well-supported."),
|
||||
scope=Scope.settings
|
||||
)
|
||||
text_customization = Dict(
|
||||
help="String customization substitutions for particular locations",
|
||||
help=_("String customization substitutions for particular locations"),
|
||||
scope=Scope.settings
|
||||
# TODO: someday it should be possible to not duplicate this definition here
|
||||
# and in inheritance.py
|
||||
)
|
||||
use_latex_compiler = Boolean(
|
||||
help="Enable LaTeX templates?",
|
||||
help=_("Enable LaTeX templates?"),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
|
||||
@@ -13,6 +13,9 @@ import textwrap
|
||||
|
||||
log = logging.getLogger("edx.courseware")
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
V1_SETTINGS_ATTRIBUTES = [
|
||||
"display_name",
|
||||
"max_attempts",
|
||||
@@ -190,131 +193,136 @@ class VersionInteger(Integer):
|
||||
|
||||
class CombinedOpenEndedFields(object):
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="This name appears in the horizontal navigation at the top of the page.",
|
||||
display_name=_("Display Name"),
|
||||
help=_("This name appears in the horizontal navigation at the top of the page."),
|
||||
default="Open Response Assessment",
|
||||
scope=Scope.settings
|
||||
)
|
||||
current_task_number = Integer(
|
||||
help="Current task that the student is on.",
|
||||
help=_("Current task that the student is on."),
|
||||
default=0,
|
||||
scope=Scope.user_state
|
||||
)
|
||||
old_task_states = List(
|
||||
help=("A list of lists of state dictionaries for student states that are saved."
|
||||
help=_("A list of lists of state dictionaries for student states that are saved."
|
||||
"This field is only populated if the instructor changes tasks after"
|
||||
"the module is created and students have attempted it (for example changes a self assessed problem to "
|
||||
"self and peer assessed."),
|
||||
scope = Scope.user_state
|
||||
)
|
||||
task_states = List(
|
||||
help="List of state dictionaries of each task within this module.",
|
||||
help=_("List of state dictionaries of each task within this module."),
|
||||
scope=Scope.user_state
|
||||
)
|
||||
state = String(
|
||||
help="Which step within the current task that the student is on.",
|
||||
help=_("Which step within the current task that the student is on."),
|
||||
default="initial",
|
||||
scope=Scope.user_state
|
||||
)
|
||||
graded = Boolean(
|
||||
display_name="Graded",
|
||||
help='Defines whether the student gets credit for this problem. Credit is based on peer grades of this problem.',
|
||||
display_name=_("Graded"),
|
||||
help=_("Defines whether the student gets credit for this problem. Credit is based on peer grades of this problem."),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
student_attempts = Integer(
|
||||
help="Number of attempts taken by the student on this problem",
|
||||
help=_("Number of attempts taken by the student on this problem"),
|
||||
default=0,
|
||||
scope=Scope.user_state
|
||||
)
|
||||
ready_to_reset = Boolean(
|
||||
help="If the problem is ready to be reset or not.",
|
||||
help=_("If the problem is ready to be reset or not."),
|
||||
default=False,
|
||||
scope=Scope.user_state
|
||||
)
|
||||
max_attempts = Integer(
|
||||
display_name="Maximum Attempts",
|
||||
help="The number of times the student can try to answer this problem.",
|
||||
display_name=_("Maximum Attempts"),
|
||||
help=_("The number of times the student can try to answer this problem."),
|
||||
default=1,
|
||||
scope=Scope.settings,
|
||||
values={"min": 1 }
|
||||
)
|
||||
accept_file_upload = Boolean(
|
||||
display_name="Allow File Uploads",
|
||||
help="Whether or not the student can submit files as a response.",
|
||||
display_name=_("Allow File Uploads"),
|
||||
help=_("Whether or not the student can submit files as a response."),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
skip_spelling_checks = Boolean(
|
||||
display_name="Disable Quality Filter",
|
||||
help="If False, the Quality Filter is enabled and submissions with poor spelling, short length, or poor grammar will not be peer reviewed.",
|
||||
display_name=_("Disable Quality Filter"),
|
||||
help=_("If False, the Quality Filter is enabled and submissions with poor spelling, short length, or poor grammar will not be peer reviewed."),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
due = Date(
|
||||
help="Date that this problem is due by",
|
||||
help=_("Date that this problem is due by"),
|
||||
scope=Scope.settings
|
||||
)
|
||||
extended_due = Date(
|
||||
help="Date that this problem is due by for a particular student. This "
|
||||
help=_("Date that this problem is due by for a particular student. This "
|
||||
"can be set by an instructor, and will override the global due "
|
||||
"date if it is set to a date that is later than the global due "
|
||||
"date.",
|
||||
"date."),
|
||||
default=None,
|
||||
scope=Scope.user_state,
|
||||
)
|
||||
graceperiod = Timedelta(
|
||||
help="Amount of time after the due date that submissions will be accepted",
|
||||
help=_("Amount of time after the due date that submissions will be accepted"),
|
||||
scope=Scope.settings
|
||||
)
|
||||
version = VersionInteger(help="Current version number", default=DEFAULT_VERSION, scope=Scope.settings)
|
||||
data = String(help="XML data for the problem", scope=Scope.content,
|
||||
version = VersionInteger(
|
||||
help=_("Current version number"),
|
||||
default=DEFAULT_VERSION,
|
||||
scope=Scope.settings)
|
||||
data = String(
|
||||
help=_("XML data for the problem"),
|
||||
scope=Scope.content,
|
||||
default=DEFAULT_DATA)
|
||||
weight = Float(
|
||||
display_name="Problem Weight",
|
||||
help="Defines the number of points each problem is worth. If the value is not set, each problem is worth one point.",
|
||||
display_name=_("Problem Weight"),
|
||||
help=_("Defines the number of points each problem is worth. If the value is not set, each problem is worth one point."),
|
||||
scope=Scope.settings,
|
||||
values={"min": 0, "step": ".1"},
|
||||
default=1
|
||||
)
|
||||
min_to_calibrate = Integer(
|
||||
display_name="Minimum Peer Grading Calibrations",
|
||||
help="The minimum number of calibration essays each student will need to complete for peer grading.",
|
||||
display_name=_("Minimum Peer Grading Calibrations"),
|
||||
help=_("The minimum number of calibration essays each student will need to complete for peer grading."),
|
||||
default=3,
|
||||
scope=Scope.settings,
|
||||
values={"min": 1, "max": 20, "step": "1"}
|
||||
)
|
||||
max_to_calibrate = Integer(
|
||||
display_name="Maximum Peer Grading Calibrations",
|
||||
help="The maximum number of calibration essays each student will need to complete for peer grading.",
|
||||
display_name=_("Maximum Peer Grading Calibrations"),
|
||||
help=_("The maximum number of calibration essays each student will need to complete for peer grading."),
|
||||
default=6,
|
||||
scope=Scope.settings,
|
||||
values={"min": 1, "max": 20, "step": "1"}
|
||||
)
|
||||
peer_grader_count = Integer(
|
||||
display_name="Peer Graders per Response",
|
||||
help="The number of peers who will grade each submission.",
|
||||
display_name=_("Peer Graders per Response"),
|
||||
help=_("The number of peers who will grade each submission."),
|
||||
default=3,
|
||||
scope=Scope.settings,
|
||||
values={"min": 1, "step": "1", "max": 5}
|
||||
)
|
||||
required_peer_grading = Integer(
|
||||
display_name="Required Peer Grading",
|
||||
help="The number of other students each student making a submission will have to grade.",
|
||||
display_name=_("Required Peer Grading"),
|
||||
help=_("The number of other students each student making a submission will have to grade."),
|
||||
default=3,
|
||||
scope=Scope.settings,
|
||||
values={"min": 1, "step": "1", "max": 5}
|
||||
)
|
||||
peer_grade_finished_submissions_when_none_pending = Boolean(
|
||||
display_name='Allow "overgrading" of peer submissions',
|
||||
help=("EXPERIMENTAL FEATURE. Allow students to peer grade submissions that already have the requisite number of graders, "
|
||||
display_name=_('Allow "overgrading" of peer submissions'),
|
||||
help=_("EXPERIMENTAL FEATURE. Allow students to peer grade submissions that already have the requisite number of graders, "
|
||||
"but ONLY WHEN all submissions they are eligible to grade already have enough graders. "
|
||||
"This is intended for use when settings for `Required Peer Grading` > `Peer Graders per Response`"),
|
||||
default=False,
|
||||
scope=Scope.settings,
|
||||
)
|
||||
markdown = String(
|
||||
help="Markdown source of this module",
|
||||
help=_("Markdown source of this module"),
|
||||
default=textwrap.dedent("""\
|
||||
[prompt]
|
||||
<h3>Censorship in the Libraries</h3>
|
||||
|
||||
@@ -22,6 +22,9 @@ from django.utils.timezone import UTC
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
class StringOrDate(Date):
|
||||
def from_json(self, value):
|
||||
@@ -223,7 +226,7 @@ class CourseFields(object):
|
||||
}},
|
||||
scope=Scope.content)
|
||||
show_calculator = Boolean(help="Whether to show the calculator in this course", default=False, scope=Scope.settings)
|
||||
display_name = String(help="Display name for this module", default="Empty", display_name="Display Name", scope=Scope.settings)
|
||||
display_name = String(help="Display name for this module", default="Empty", display_name=_("Display Name"), scope=Scope.settings)
|
||||
course_edit_method = String(help="Method with which this course is edited.", default="Studio", scope=Scope.settings)
|
||||
show_chat = Boolean(help="Whether to show the chat widget in this course", default=False, scope=Scope.settings)
|
||||
tabs = CourseTabList(help="List of tabs to enable in this course", scope=Scope.settings, default=[])
|
||||
@@ -245,117 +248,117 @@ class CourseFields(object):
|
||||
has_children = True
|
||||
checklists = List(scope=Scope.settings,
|
||||
default=[
|
||||
{"short_description": "Getting Started With Studio",
|
||||
"items": [{"short_description": "Add Course Team Members",
|
||||
"long_description": "Grant your collaborators permission to edit your course so you can work together.",
|
||||
{"short_description": _("Getting Started With Studio"),
|
||||
"items": [{"short_description": _("Add Course Team Members"),
|
||||
"long_description": _("Grant your collaborators permission to edit your course so you can work together."),
|
||||
"is_checked": False,
|
||||
"action_url": "ManageUsers",
|
||||
"action_text": "Edit Course Team",
|
||||
"action_text": _("Edit Course Team"),
|
||||
"action_external": False},
|
||||
{"short_description": "Set Important Dates for Your Course",
|
||||
"long_description": "Establish your course's student enrollment and launch dates on the Schedule and Details page.",
|
||||
{"short_description": _("Set Important Dates for Your Course"),
|
||||
"long_description": _("Establish your course's student enrollment and launch dates on the Schedule and Details page."),
|
||||
"is_checked": False,
|
||||
"action_url": "SettingsDetails",
|
||||
"action_text": "Edit Course Details & Schedule",
|
||||
"action_text": _("Edit Course Details & Schedule"),
|
||||
"action_external": False},
|
||||
{"short_description": "Draft Your Course's Grading Policy",
|
||||
"long_description": "Set up your assignment types and grading policy even if you haven't created all your assignments.",
|
||||
{"short_description": _("Draft Your Course's Grading Policy"),
|
||||
"long_description": _("Set up your assignment types and grading policy even if you haven't created all your assignments."),
|
||||
"is_checked": False,
|
||||
"action_url": "SettingsGrading",
|
||||
"action_text": "Edit Grading Settings",
|
||||
"action_text": _("Edit Grading Settings"),
|
||||
"action_external": False},
|
||||
{"short_description": "Explore the Other Studio Checklists",
|
||||
"long_description": "Discover other available course authoring tools, and find help when you need it.",
|
||||
{"short_description": _("Explore the Other Studio Checklists"),
|
||||
"long_description": _("Discover other available course authoring tools, and find help when you need it."),
|
||||
"is_checked": False,
|
||||
"action_url": "",
|
||||
"action_text": "",
|
||||
"action_external": False}]},
|
||||
{"short_description": "Draft a Rough Course Outline",
|
||||
"items": [{"short_description": "Create Your First Section and Subsection",
|
||||
"long_description": "Use your course outline to build your first Section and Subsection.",
|
||||
{"short_description": _("Draft a Rough Course Outline"),
|
||||
"items": [{"short_description": _("Create Your First Section and Subsection"),
|
||||
"long_description": _("Use your course outline to build your first Section and Subsection."),
|
||||
"is_checked": False,
|
||||
"action_url": "CourseOutline",
|
||||
"action_text": "Edit Course Outline",
|
||||
"action_text": _("Edit Course Outline"),
|
||||
"action_external": False},
|
||||
{"short_description": "Set Section Release Dates",
|
||||
"long_description": "Specify the release dates for each Section in your course. Sections become visible to students on their release dates.",
|
||||
{"short_description": _("Set Section Release Dates"),
|
||||
"long_description": _("Specify the release dates for each Section in your course. Sections become visible to students on their release dates."),
|
||||
"is_checked": False,
|
||||
"action_url": "CourseOutline",
|
||||
"action_text": "Edit Course Outline",
|
||||
"action_text": _("Edit Course Outline"),
|
||||
"action_external": False},
|
||||
{"short_description": "Designate a Subsection as Graded",
|
||||
"long_description": "Set a Subsection to be graded as a specific assignment type. Assignments within graded Subsections count toward a student's final grade.",
|
||||
{"short_description": _("Designate a Subsection as Graded"),
|
||||
"long_description": _("Set a Subsection to be graded as a specific assignment type. Assignments within graded Subsections count toward a student's final grade."),
|
||||
"is_checked": False,
|
||||
"action_url": "CourseOutline",
|
||||
"action_text": "Edit Course Outline",
|
||||
"action_text": _("Edit Course Outline"),
|
||||
"action_external": False},
|
||||
{"short_description": "Reordering Course Content",
|
||||
"long_description": "Use drag and drop to reorder the content in your course.",
|
||||
{"short_description": _("Reordering Course Content"),
|
||||
"long_description": _("Use drag and drop to reorder the content in your course."),
|
||||
"is_checked": False,
|
||||
"action_url": "CourseOutline",
|
||||
"action_text": "Edit Course Outline",
|
||||
"action_text": _("Edit Course Outline"),
|
||||
"action_external": False},
|
||||
{"short_description": "Renaming Sections",
|
||||
"long_description": "Rename Sections by clicking the Section name from the Course Outline.",
|
||||
{"short_description": _("Renaming Sections"),
|
||||
"long_description": _("Rename Sections by clicking the Section name from the Course Outline."),
|
||||
"is_checked": False,
|
||||
"action_url": "CourseOutline",
|
||||
"action_text": "Edit Course Outline",
|
||||
"action_text": _("Edit Course Outline"),
|
||||
"action_external": False},
|
||||
{"short_description": "Deleting Course Content",
|
||||
"long_description": "Delete Sections, Subsections, or Units you don't need anymore. Be careful, as there is no Undo function.",
|
||||
{"short_description": _("Deleting Course Content"),
|
||||
"long_description": _("Delete Sections, Subsections, or Units you don't need anymore. Be careful, as there is no Undo function."),
|
||||
"is_checked": False,
|
||||
"action_url": "CourseOutline",
|
||||
"action_text": "Edit Course Outline",
|
||||
"action_text": _("Edit Course Outline"),
|
||||
"action_external": False},
|
||||
{"short_description": "Add an Instructor-Only Section to Your Outline",
|
||||
"long_description": "Some course authors find using a section for unsorted, in-progress work useful. To do this, create a section and set the release date to the distant future.",
|
||||
{"short_description": _("Add an Instructor-Only Section to Your Outline"),
|
||||
"long_description": _("Some course authors find using a section for unsorted, in-progress work useful. To do this, create a section and set the release date to the distant future."),
|
||||
"is_checked": False,
|
||||
"action_url": "CourseOutline",
|
||||
"action_text": "Edit Course Outline",
|
||||
"action_text": _("Edit Course Outline"),
|
||||
"action_external": False}]},
|
||||
{"short_description": "Explore edX's Support Tools",
|
||||
"items": [{"short_description": "Explore the Studio Help Forum",
|
||||
"long_description": "Access the Studio Help forum from the menu that appears when you click your user name in the top right corner of Studio.",
|
||||
{"short_description": _("Explore edX's Support Tools"),
|
||||
"items": [{"short_description": _("Explore the Studio Help Forum"),
|
||||
"long_description": _("Access the Studio Help forum from the menu that appears when you click your user name in the top right corner of Studio."),
|
||||
"is_checked": False,
|
||||
"action_url": "http://help.edge.edx.org/",
|
||||
"action_text": "Visit Studio Help",
|
||||
"action_text": _("Visit Studio Help"),
|
||||
"action_external": True},
|
||||
{"short_description": "Enroll in edX 101",
|
||||
"long_description": "Register for edX 101, edX's primer for course creation.",
|
||||
{"short_description": _("Enroll in edX 101"),
|
||||
"long_description": _("Register for edX 101, edX's primer for course creation."),
|
||||
"is_checked": False,
|
||||
"action_url": "https://edge.edx.org/courses/edX/edX101/How_to_Create_an_edX_Course/about",
|
||||
"action_text": "Register for edX 101",
|
||||
"action_text": _("Register for edX 101"),
|
||||
"action_external": True},
|
||||
{"short_description": "Download the Studio Documentation",
|
||||
"long_description": "Download the searchable Studio reference documentation in PDF form.",
|
||||
{"short_description": _("Download the Studio Documentation"),
|
||||
"long_description": _("Download the searchable Studio reference documentation in PDF form."),
|
||||
"is_checked": False,
|
||||
"action_url": "http://files.edx.org/Getting_Started_with_Studio.pdf",
|
||||
"action_text": "Download Documentation",
|
||||
"action_text": _("Download Documentation"),
|
||||
"action_external": True}]},
|
||||
{"short_description": "Draft Your Course About Page",
|
||||
"items": [{"short_description": "Draft a Course Description",
|
||||
"long_description": "Courses on edX have an About page that includes a course video, description, and more. Draft the text students will read before deciding to enroll in your course.",
|
||||
{"short_description": _("Draft Your Course About Page"),
|
||||
"items": [{"short_description": _("Draft a Course Description"),
|
||||
"long_description": _("Courses on edX have an About page that includes a course video, description, and more. Draft the text students will read before deciding to enroll in your course."),
|
||||
"is_checked": False,
|
||||
"action_url": "SettingsDetails",
|
||||
"action_text": "Edit Course Schedule & Details",
|
||||
"action_text": _("Edit Course Schedule & Details"),
|
||||
"action_external": False},
|
||||
{"short_description": "Add Staff Bios",
|
||||
"long_description": "Showing prospective students who their instructor will be is helpful. Include staff bios on the course About page.",
|
||||
{"short_description": _("Add Staff Bios"),
|
||||
"long_description": _("Showing prospective students who their instructor will be is helpful. Include staff bios on the course About page."),
|
||||
"is_checked": False,
|
||||
"action_url": "SettingsDetails",
|
||||
"action_text": "Edit Course Schedule & Details",
|
||||
"action_text": _("Edit Course Schedule & Details"),
|
||||
"action_external": False},
|
||||
{"short_description": "Add Course FAQs",
|
||||
"long_description": "Include a short list of frequently asked questions about your course.",
|
||||
{"short_description": _("Add Course FAQs"),
|
||||
"long_description": _("Include a short list of frequently asked questions about your course."),
|
||||
"is_checked": False,
|
||||
"action_url": "SettingsDetails",
|
||||
"action_text": "Edit Course Schedule & Details",
|
||||
"action_text": _("Edit Course Schedule & Details"),
|
||||
"action_external": False},
|
||||
{"short_description": "Add Course Prerequisites",
|
||||
"long_description": "Let students know what knowledge and/or skills they should have before they enroll in your course.",
|
||||
{"short_description": _("Add Course Prerequisites"),
|
||||
"long_description": _("Let students know what knowledge and/or skills they should have before they enroll in your course."),
|
||||
"is_checked": False,
|
||||
"action_url": "SettingsDetails",
|
||||
"action_text": "Edit Course Schedule & Details",
|
||||
"action_text": _("Edit Course Schedule & Details"),
|
||||
"action_external": False}]}
|
||||
])
|
||||
info_sidebar_name = String(scope=Scope.settings, default='Course Handouts')
|
||||
|
||||
@@ -6,30 +6,33 @@ from xmodule.editing_module import MetadataOnlyEditingDescriptor
|
||||
from xblock.fields import String, Scope
|
||||
from uuid import uuid4
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
class DiscussionFields(object):
|
||||
discussion_id = String(scope=Scope.settings, default="$$GUID$$")
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="Display name for this module",
|
||||
display_name=_("Display Name"),
|
||||
help=_("Display name for this module"),
|
||||
default="Discussion",
|
||||
scope=Scope.settings
|
||||
)
|
||||
data = String(
|
||||
help="XML data for the problem",
|
||||
help=_("XML data for the problem"),
|
||||
scope=Scope.content,
|
||||
default="<discussion></discussion>"
|
||||
)
|
||||
discussion_category = String(
|
||||
display_name="Category",
|
||||
display_name=_("Category"),
|
||||
default="Week 1",
|
||||
help="A category name for the discussion. This name appears in the left pane of the discussion forum for the course.",
|
||||
help=_("A category name for the discussion. This name appears in the left pane of the discussion forum for the course."),
|
||||
scope=Scope.settings
|
||||
)
|
||||
discussion_target = String(
|
||||
display_name="Subcategory",
|
||||
display_name=_("Subcategory"),
|
||||
default="Topic-Level Student-Visible Label",
|
||||
help="A subcategory name for the discussion. This name appears in the left pane of the discussion forum for the course.",
|
||||
help=_("A subcategory name for the discussion. This name appears in the left pane of the discussion forum for the course."),
|
||||
scope=Scope.settings
|
||||
)
|
||||
sort_key = String(scope=Scope.settings)
|
||||
|
||||
@@ -19,26 +19,29 @@ from xblock.core import XBlock
|
||||
|
||||
log = logging.getLogger("edx.courseware")
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
class HtmlFields(object):
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="This name appears in the horizontal navigation at the top of the page.",
|
||||
display_name=_("Display Name"),
|
||||
help=_("This name appears in the horizontal navigation at the top of the page."),
|
||||
scope=Scope.settings,
|
||||
# it'd be nice to have a useful default but it screws up other things; so,
|
||||
# use display_name_with_default for those
|
||||
default="Text"
|
||||
)
|
||||
data = String(help="Html contents to display for this module", default=u"", scope=Scope.content)
|
||||
source_code = String(help="Source code for LaTeX documents. This feature is not well-supported.", scope=Scope.settings)
|
||||
data = String(help=_("Html contents to display for this module"), default=u"", scope=Scope.content)
|
||||
source_code = String(help=_("Source code for LaTeX documents. This feature is not well-supported."), scope=Scope.settings)
|
||||
use_latex_compiler = Boolean(
|
||||
help="Enable LaTeX templates?",
|
||||
help=_("Enable LaTeX templates?"),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
editor = String(
|
||||
help="Select Visual to enter content and have the editor automatically create the HTML. Select Raw to edit HTML directly. If you change this setting, you must save the component and then re-open it for editing.",
|
||||
display_name="Editor",
|
||||
help=_("Select Visual to enter content and have the editor automatically create the HTML. Select Raw to edit HTML directly. If you change this setting, you must save the component and then re-open it for editing."),
|
||||
display_name=_("Editor"),
|
||||
default="visual",
|
||||
values=[
|
||||
{"display_name": "Visual", "value": "visual"},
|
||||
@@ -234,12 +237,12 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor):
|
||||
|
||||
class AboutFields(object):
|
||||
display_name = String(
|
||||
help="Display name for this module",
|
||||
help=_("Display name for this module"),
|
||||
scope=Scope.settings,
|
||||
default="overview",
|
||||
)
|
||||
data = String(
|
||||
help="Html contents to display for this module",
|
||||
help=_("Html contents to display for this module"),
|
||||
default=u"",
|
||||
scope=Scope.content
|
||||
)
|
||||
@@ -268,8 +271,8 @@ class StaticTabFields(object):
|
||||
The overrides for Static Tabs
|
||||
"""
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="This name appears in the horizontal navigation at the top of the page.",
|
||||
display_name=_("Display Name"),
|
||||
help=_("This name appears in the horizontal navigation at the top of the page."),
|
||||
scope=Scope.settings,
|
||||
default="Empty",
|
||||
)
|
||||
@@ -278,7 +281,7 @@ class StaticTabFields(object):
|
||||
<p>Add the content you want students to see on this page.</p>
|
||||
"""),
|
||||
scope=Scope.content,
|
||||
help="HTML for the additional pages"
|
||||
help=_("HTML for the additional pages")
|
||||
)
|
||||
|
||||
|
||||
@@ -305,12 +308,12 @@ class CourseInfoFields(object):
|
||||
Field overrides
|
||||
"""
|
||||
items = List(
|
||||
help="List of course update items",
|
||||
help=_("List of course update items"),
|
||||
default=[],
|
||||
scope=Scope.content
|
||||
)
|
||||
data = String(
|
||||
help="Html contents to display for this module",
|
||||
help=_("Html contents to display for this module"),
|
||||
default=u"<ol></ol>",
|
||||
scope=Scope.content
|
||||
)
|
||||
|
||||
@@ -236,18 +236,18 @@
|
||||
|
||||
it('render the caption', function () {
|
||||
runs(function () {
|
||||
var captionsData;
|
||||
var captionsData = jasmine.stubbedCaption,
|
||||
items = $('.subtitles li[data-index]');
|
||||
|
||||
captionsData = jasmine.stubbedCaption;
|
||||
$('.subtitles li[data-index]').each(
|
||||
function (index, link) {
|
||||
_.each(captionsData.text, function (text, index) {
|
||||
var item = items.eq(index);
|
||||
|
||||
expect($(link)).toHaveData('index', index);
|
||||
expect($(link)).toHaveData(
|
||||
expect(item).toHaveData('index', index);
|
||||
expect(item).toHaveData(
|
||||
'start', captionsData.start[index]
|
||||
);
|
||||
expect($(link)).toHaveAttr('tabindex', 0);
|
||||
expect($(link)).toHaveText(captionsData.text[index]);
|
||||
expect(item).toHaveAttr('tabindex', 0);
|
||||
expect(item).toHaveText(text);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -324,13 +324,39 @@
|
||||
$.fn.scrollTo.reset();
|
||||
});
|
||||
|
||||
it('show explaination message', function () {
|
||||
it('show explanation message', function () {
|
||||
expect($('.subtitles li')).toHaveHtml(
|
||||
'Caption will be displayed when you start playing ' +
|
||||
'the video.'
|
||||
);
|
||||
});
|
||||
|
||||
it('show captions on play', function () {
|
||||
runs(function () {
|
||||
state.el.trigger('play');
|
||||
});
|
||||
|
||||
waitsFor(function () {
|
||||
return state.videoCaption.rendered;
|
||||
}, 'Captions are not rendered', WAIT_TIMEOUT);
|
||||
|
||||
runs(function () {
|
||||
var captionsData = jasmine.stubbedCaption,
|
||||
items = $('.subtitles li[data-index]');
|
||||
|
||||
_.each(captionsData.text, function (text, index) {
|
||||
var item = items.eq(index);
|
||||
|
||||
expect(item).toHaveData('index', index);
|
||||
expect(item).toHaveData(
|
||||
'start', captionsData.start[index]
|
||||
);
|
||||
expect(item).toHaveAttr('tabindex', 0);
|
||||
expect(item).toHaveText(text);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not set rendered to true', function () {
|
||||
expect(state.videoCaption.rendered).toBeFalsy();
|
||||
});
|
||||
|
||||
@@ -119,10 +119,10 @@ function (Sjson, AsyncProcess) {
|
||||
'caption:update': function (event, time) {
|
||||
self.updatePlayTime(time);
|
||||
},
|
||||
'ended': this.pause,
|
||||
'ended': this.pause.bind(this),
|
||||
'fullscreen': this.onResize.bind(this),
|
||||
'pause': this.pause,
|
||||
'play': this.play,
|
||||
'pause': this.pause.bind(this),
|
||||
'play': this.play.bind(this)
|
||||
});
|
||||
|
||||
if ((state.videoType === 'html5') && (state.config.autohideHtml5)) {
|
||||
|
||||
@@ -75,6 +75,9 @@ from xblock.fields import Boolean, Float
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
DOCS_ANCHOR_TAG = (
|
||||
"<a target='_blank'"
|
||||
"href='http://edx.readthedocs.org/projects/ca/en/latest/exercises_tools/lti_component.html'>"
|
||||
@@ -103,8 +106,8 @@ class LTIFields(object):
|
||||
https://github.com/idan/oauthlib/blob/master/oauthlib/oauth1/rfc5849/signature.py#L136
|
||||
"""
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help=(
|
||||
display_name=_("Display Name"),
|
||||
help=_(
|
||||
"Enter the name that students see for this component. "
|
||||
"Analytics reports may also use the display name to identify this component."
|
||||
),
|
||||
@@ -112,8 +115,8 @@ class LTIFields(object):
|
||||
default="LTI",
|
||||
)
|
||||
lti_id = String(
|
||||
display_name="LTI ID",
|
||||
help=(
|
||||
display_name=_("LTI ID"),
|
||||
help=_(
|
||||
"Enter the LTI ID for the external LTI provider. "
|
||||
"This value must be the same LTI ID that you entered in the "
|
||||
"LTI Passports setting on the Advanced Settings page."
|
||||
@@ -123,8 +126,8 @@ class LTIFields(object):
|
||||
scope=Scope.settings
|
||||
)
|
||||
launch_url = String(
|
||||
display_name="LTI URL",
|
||||
help=(
|
||||
display_name=_("LTI URL"),
|
||||
help=_(
|
||||
"Enter the URL of the external tool that this component launches. "
|
||||
"This setting is only used when Hide External Tool is set to False."
|
||||
"<br />See " + DOCS_ANCHOR_TAG + " for more details on this setting."
|
||||
@@ -132,16 +135,16 @@ class LTIFields(object):
|
||||
default='http://www.example.com',
|
||||
scope=Scope.settings)
|
||||
custom_parameters = List(
|
||||
display_name="Custom Parameters",
|
||||
help=(
|
||||
display_name=_("Custom Parameters"),
|
||||
help=_(
|
||||
"Add the key/value pair for any custom parameters, such as the page your e-book should open to or "
|
||||
"the background color for this component."
|
||||
"<br />See " + DOCS_ANCHOR_TAG + " for more details on this setting."
|
||||
),
|
||||
scope=Scope.settings)
|
||||
open_in_a_new_page = Boolean(
|
||||
display_name="Open in New Page",
|
||||
help=(
|
||||
display_name=_("Open in New Page"),
|
||||
help=_(
|
||||
"Select True if you want students to click a link that opens the LTI tool in a new window. "
|
||||
"Select False if you want the LTI content to open in an IFrame in the current page. "
|
||||
"This setting is only used when Hide External Tool is set to False. "
|
||||
@@ -150,16 +153,16 @@ class LTIFields(object):
|
||||
scope=Scope.settings
|
||||
)
|
||||
has_score = Boolean(
|
||||
display_name="Scored",
|
||||
help=(
|
||||
display_name=_("Scored"),
|
||||
help=_(
|
||||
"Select True if this component will receive a numerical score from the external LTI system."
|
||||
),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
weight = Float(
|
||||
display_name="Weight",
|
||||
help=(
|
||||
display_name=_("Weight"),
|
||||
help=_(
|
||||
"Enter the number of points possible for this component. "
|
||||
"The default value is 1.0. "
|
||||
"This setting is only used when Scored is set to True."
|
||||
@@ -169,18 +172,18 @@ class LTIFields(object):
|
||||
values={"min": 0},
|
||||
)
|
||||
module_score = Float(
|
||||
help="The score kept in the xblock KVS -- duplicate of the published score in django DB",
|
||||
help=_("The score kept in the xblock KVS -- duplicate of the published score in django DB"),
|
||||
default=None,
|
||||
scope=Scope.user_state
|
||||
)
|
||||
score_comment = String(
|
||||
help="Comment as returned from grader, LTI2.0 spec",
|
||||
help=_("Comment as returned from grader, LTI2.0 spec"),
|
||||
default="",
|
||||
scope=Scope.user_state
|
||||
)
|
||||
hide_launch = Boolean(
|
||||
display_name="Hide External Tool",
|
||||
help=(
|
||||
display_name=_("Hide External Tool"),
|
||||
help=_(
|
||||
"Select True if you want to use this component as a placeholder for syncing with an external grading "
|
||||
"system rather than launch an external tool. "
|
||||
"This setting hides the Launch button and any IFrames for this component."
|
||||
|
||||
@@ -21,6 +21,9 @@ from xmodule.open_ended_grading_classes.grading_service_module import GradingSer
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
EXTERNAL_GRADER_NO_CONTACT_ERROR = "Failed to contact external graders. Please notify course staff."
|
||||
MAX_ALLOWED_FEEDBACK_LENGTH = 5000
|
||||
@@ -28,57 +31,57 @@ MAX_ALLOWED_FEEDBACK_LENGTH = 5000
|
||||
|
||||
class PeerGradingFields(object):
|
||||
use_for_single_location = Boolean(
|
||||
display_name="Show Single Problem",
|
||||
help='When True, only the single problem specified by "Link to Problem Location" is shown. '
|
||||
'When False, a panel is displayed with all problems available for peer grading.',
|
||||
display_name=_("Show Single Problem"),
|
||||
help=_('When True, only the single problem specified by "Link to Problem Location" is shown. '
|
||||
'When False, a panel is displayed with all problems available for peer grading.'),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
link_to_location = Reference(
|
||||
display_name="Link to Problem Location",
|
||||
help='The location of the problem being graded. Only used when "Show Single Problem" is True.',
|
||||
display_name=_("Link to Problem Location"),
|
||||
help=_('The location of the problem being graded. Only used when "Show Single Problem" is True.'),
|
||||
default="",
|
||||
scope=Scope.settings
|
||||
)
|
||||
graded = Boolean(
|
||||
display_name="Graded",
|
||||
help='Defines whether the student gets credit for grading this problem. Only used when "Show Single Problem" is True.',
|
||||
display_name=_("Graded"),
|
||||
help=_('Defines whether the student gets credit for grading this problem. Only used when "Show Single Problem" is True.'),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
due = Date(
|
||||
help="Due date that should be displayed.",
|
||||
help=_("Due date that should be displayed."),
|
||||
scope=Scope.settings)
|
||||
extended_due = Date(
|
||||
help="Date that this problem is due by for a particular student. This "
|
||||
help=_("Date that this problem is due by for a particular student. This "
|
||||
"can be set by an instructor, and will override the global due "
|
||||
"date if it is set to a date that is later than the global due "
|
||||
"date.",
|
||||
"date."),
|
||||
default=None,
|
||||
scope=Scope.user_state,
|
||||
)
|
||||
graceperiod = Timedelta(
|
||||
help="Amount of grace to give on the due date.",
|
||||
help=_("Amount of grace to give on the due date."),
|
||||
scope=Scope.settings
|
||||
)
|
||||
student_data_for_location = Dict(
|
||||
help="Student data for a given peer grading problem.",
|
||||
help=_("Student data for a given peer grading problem."),
|
||||
scope=Scope.user_state
|
||||
)
|
||||
weight = Float(
|
||||
display_name="Problem Weight",
|
||||
help="Defines the number of points each problem is worth. If the value is not set, each problem is worth one point.",
|
||||
display_name=_("Problem Weight"),
|
||||
help=_("Defines the number of points each problem is worth. If the value is not set, each problem is worth one point."),
|
||||
scope=Scope.settings, values={"min": 0, "step": ".1"},
|
||||
default=1
|
||||
)
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="Display name for this module",
|
||||
display_name=_("Display Name"),
|
||||
help=_("Display name for this module"),
|
||||
scope=Scope.settings,
|
||||
default="Peer Grading Interface"
|
||||
)
|
||||
data = String(
|
||||
help="Html contents to display for this module",
|
||||
help=_("Html contents to display for this module"),
|
||||
default='<peergrading></peergrading>',
|
||||
scope=Scope.content
|
||||
)
|
||||
|
||||
@@ -10,10 +10,13 @@ from xmodule.annotator_token import retrieve_token
|
||||
|
||||
import textwrap
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
class AnnotatableFields(object):
|
||||
"""Fields for `TextModule` and `TextDescriptor`."""
|
||||
data = String(help="XML data for the annotation", scope=Scope.content, default=textwrap.dedent("""\
|
||||
data = String(help=_("XML data for the annotation"), scope=Scope.content, default=textwrap.dedent("""\
|
||||
<annotatable>
|
||||
<instructions>
|
||||
<p>
|
||||
@@ -26,25 +29,25 @@ class AnnotatableFields(object):
|
||||
</annotatable>
|
||||
"""))
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="Display name for this module",
|
||||
display_name=_("Display Name"),
|
||||
help=_("Display name for this module"),
|
||||
scope=Scope.settings,
|
||||
default='Text Annotation',
|
||||
)
|
||||
instructor_tags = String(
|
||||
display_name="Tags for Assignments",
|
||||
help="Add tags that automatically highlight in a certain color using the comma-separated form, i.e. imagery:red,parallelism:blue",
|
||||
display_name=_("Tags for Assignments"),
|
||||
help=_("Add tags that automatically highlight in a certain color using the comma-separated form, i.e. imagery:red,parallelism:blue"),
|
||||
scope=Scope.settings,
|
||||
default='imagery:red,parallelism:blue',
|
||||
)
|
||||
source = String(
|
||||
display_name="Source/Citation",
|
||||
help="Optional for citing source of any material used. Automatic citation can be done using <a href=\"http://easybib.com\">EasyBib</a>",
|
||||
display_name=_("Source/Citation"),
|
||||
help=_("Optional for citing source of any material used. Automatic citation can be done using <a href=\"http://easybib.com\">EasyBib</a>"),
|
||||
scope=Scope.settings,
|
||||
default='None',
|
||||
)
|
||||
annotation_storage_url = String(help="Location of Annotation backend", scope=Scope.settings, default="http://your_annotation_storage.com", display_name="Url for Annotation Storage")
|
||||
annotation_token_secret = String(help="Secret string for annotation storage", scope=Scope.settings, default="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", display_name="Secret Token String for Annotation")
|
||||
annotation_storage_url = String(help=_("Location of Annotation backend"), scope=Scope.settings, default="http://your_annotation_storage.com", display_name=_("Url for Annotation Storage"))
|
||||
annotation_token_secret = String(help=_("Secret string for annotation storage"), scope=Scope.settings, default="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", display_name=_("Secret Token String for Annotation"))
|
||||
|
||||
|
||||
class TextAnnotationModule(AnnotatableFields, XModule):
|
||||
|
||||
@@ -11,10 +11,13 @@ from xmodule.annotator_token import retrieve_token
|
||||
|
||||
import textwrap
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
class AnnotatableFields(object):
|
||||
""" Fields for `VideoModule` and `VideoDescriptor`. """
|
||||
data = String(help="XML data for the annotation", scope=Scope.content, default=textwrap.dedent("""\
|
||||
data = String(help=_("XML data for the annotation"), scope=Scope.content, default=textwrap.dedent("""\
|
||||
<annotatable>
|
||||
<instructions>
|
||||
<p>
|
||||
@@ -24,15 +27,15 @@ class AnnotatableFields(object):
|
||||
</annotatable>
|
||||
"""))
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="Display name for this module",
|
||||
display_name=_("Display Name"),
|
||||
help=_("Display name for this module"),
|
||||
scope=Scope.settings,
|
||||
default='Video Annotation',
|
||||
)
|
||||
sourceurl = String(help="The external source URL for the video.", display_name="Source URL", scope=Scope.settings, default="http://video-js.zencoder.com/oceans-clip.mp4")
|
||||
poster_url = String(help="Poster Image URL", display_name="Poster URL", scope=Scope.settings, default="")
|
||||
annotation_storage_url = String(help="Location of Annotation backend", scope=Scope.settings, default="http://your_annotation_storage.com", display_name="Url for Annotation Storage")
|
||||
annotation_token_secret = String(help="Secret string for annotation storage", scope=Scope.settings, default="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", display_name="Secret Token String for Annotation")
|
||||
sourceurl = String(help=_("The external source URL for the video."), display_name=_("Source URL"), scope=Scope.settings, default="http://video-js.zencoder.com/oceans-clip.mp4")
|
||||
poster_url = String(help=_("Poster Image URL"), display_name=_("Poster URL"), scope=Scope.settings, default="")
|
||||
annotation_storage_url = String(help=_("Location of Annotation backend"), scope=Scope.settings, default="http://your_annotation_storage.com", display_name=_("Url for Annotation Storage"))
|
||||
annotation_token_secret = String(help=_("Secret string for annotation storage"), scope=Scope.settings, default="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", display_name=_("Secret Token String for Annotation"))
|
||||
|
||||
class VideoAnnotationModule(AnnotatableFields, XModule):
|
||||
'''Video Annotation Module'''
|
||||
|
||||
@@ -18,6 +18,9 @@ from xblock.fields import Scope, Dict, Boolean, List, Integer, String
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Make '_' a no-op so we can scrape strings
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
def pretty_bool(value):
|
||||
"""Check value for possible `True` value.
|
||||
@@ -32,49 +35,49 @@ def pretty_bool(value):
|
||||
class WordCloudFields(object):
|
||||
"""XFields for word cloud."""
|
||||
display_name = String(
|
||||
display_name="Display Name",
|
||||
help="Display name for this module",
|
||||
display_name=_("Display Name"),
|
||||
help=_("Display name for this module"),
|
||||
scope=Scope.settings,
|
||||
default="Word cloud"
|
||||
)
|
||||
num_inputs = Integer(
|
||||
display_name="Inputs",
|
||||
help="Number of text boxes available for students to input words/sentences.",
|
||||
display_name=_("Inputs"),
|
||||
help=_("Number of text boxes available for students to input words/sentences."),
|
||||
scope=Scope.settings,
|
||||
default=5,
|
||||
values={"min": 1}
|
||||
)
|
||||
num_top_words = Integer(
|
||||
display_name="Maximum Words",
|
||||
help="Maximum number of words to be displayed in generated word cloud.",
|
||||
display_name=_("Maximum Words"),
|
||||
help=_("Maximum number of words to be displayed in generated word cloud."),
|
||||
scope=Scope.settings,
|
||||
default=250,
|
||||
values={"min": 1}
|
||||
)
|
||||
display_student_percents = Boolean(
|
||||
display_name="Show Percents",
|
||||
help="Statistics are shown for entered words near that word.",
|
||||
display_name=_("Show Percents"),
|
||||
help=_("Statistics are shown for entered words near that word."),
|
||||
scope=Scope.settings,
|
||||
default=True
|
||||
)
|
||||
|
||||
# Fields for descriptor.
|
||||
submitted = Boolean(
|
||||
help="Whether this student has posted words to the cloud.",
|
||||
help=_("Whether this student has posted words to the cloud."),
|
||||
scope=Scope.user_state,
|
||||
default=False
|
||||
)
|
||||
student_words = List(
|
||||
help="Student answer.",
|
||||
help=_("Student answer."),
|
||||
scope=Scope.user_state,
|
||||
default=[]
|
||||
)
|
||||
all_words = Dict(
|
||||
help="All possible words from all students.",
|
||||
help=_("All possible words from all students."),
|
||||
scope=Scope.user_state_summary
|
||||
)
|
||||
top_words = Dict(
|
||||
help="Top num_top_words words for word cloud.",
|
||||
help=_("Top num_top_words words for word cloud."),
|
||||
scope=Scope.user_state_summary
|
||||
)
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ define([], function () {
|
||||
'></div>'
|
||||
);
|
||||
|
||||
state.baseImageEl = $('<img />');
|
||||
state.baseImageEl = $('<img />', {
|
||||
alt: gettext("Drop target image")
|
||||
});
|
||||
|
||||
state.baseImageEl.attr('src', state.config.baseImage);
|
||||
state.baseImageEl.load(function () {
|
||||
|
||||
@@ -82,6 +82,10 @@ return {
|
||||
if (this.isOriginal === true) {
|
||||
this.state.numDraggablesInSlider -= 1;
|
||||
}
|
||||
// SR: global "screen reader" object in accessibility_tools.js
|
||||
window.SR.readText(gettext('dragging out of slider'));
|
||||
} else {
|
||||
window.SR.readText(gettext('dragging'));
|
||||
}
|
||||
|
||||
this.zIndex = 1000;
|
||||
@@ -89,7 +93,8 @@ return {
|
||||
if (this.labelEl !== null) {
|
||||
this.labelEl.css('z-index', '1000');
|
||||
}
|
||||
|
||||
this.iconEl.attr('aria-grabbed', 'true').focus();
|
||||
this.toggleTargets(true);
|
||||
this.mousePressed = true;
|
||||
this.state.currentMovingDraggable = this;
|
||||
}
|
||||
@@ -98,8 +103,15 @@ return {
|
||||
'mouseUp': function () {
|
||||
if (this.mousePressed === true) {
|
||||
this.state.currentMovingDraggable = null;
|
||||
this.iconEl.attr('aria-grabbed', 'false');
|
||||
|
||||
this.checkLandingElement();
|
||||
if (this.inContainer === true) {
|
||||
window.SR.readText(gettext('dropped in slider'));
|
||||
} else {
|
||||
window.SR.readText(gettext('dropped on target'));
|
||||
}
|
||||
this.toggleTargets(false);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -236,6 +236,15 @@ return {
|
||||
return false;
|
||||
},
|
||||
|
||||
'toggleTargets': function (isEnabled) {
|
||||
var effect = (isEnabled ? 'move' : undefined);
|
||||
this.state.baseImageEl.attr('aria-dropeffect', effect);
|
||||
|
||||
$.each(this.state.targets, function (target) {
|
||||
target.targetEl.attr('aria-dropeffect', effect);
|
||||
});
|
||||
},
|
||||
|
||||
'snapToTarget': function (target) {
|
||||
var offset;
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ define(['js/capa/drag_and_drop/draggable_events', 'js/capa/drag_and_drop/draggab
|
||||
'color: black; ' +
|
||||
'font-size: 0.95em; ' +
|
||||
'" ' +
|
||||
'>' +
|
||||
'role="label">' +
|
||||
draggableObj.originalConfigObj.label +
|
||||
'</div>'
|
||||
);
|
||||
@@ -160,6 +160,7 @@ define(['js/capa/drag_and_drop/draggable_events', 'js/capa/drag_and_drop/draggab
|
||||
'correctZIndexes': draggableLogic.correctZIndexes,
|
||||
'moveBackToSlider': draggableLogic.moveBackToSlider,
|
||||
'moveDraggableTo': draggableLogic.moveDraggableTo,
|
||||
'toggleTargets': draggableLogic.toggleTargets,
|
||||
|
||||
'makeDraggableCopy': makeDraggableCopy,
|
||||
|
||||
@@ -181,8 +182,9 @@ define(['js/capa/drag_and_drop/draggable_events', 'js/capa/drag_and_drop/draggab
|
||||
'border-right: 1px solid #CCC; ' +
|
||||
'text-align: center; ' +
|
||||
'position: relative; ' +
|
||||
'cursor: move; ' +
|
||||
'" ' +
|
||||
'></div>'
|
||||
'role="listitem"></div>'
|
||||
);
|
||||
|
||||
draggableObj.containerEl.appendTo(state.sliderEl);
|
||||
@@ -237,6 +239,7 @@ define(['js/capa/drag_and_drop/draggable_events', 'js/capa/drag_and_drop/draggab
|
||||
'position: absolute; ' +
|
||||
'color: black; ' +
|
||||
'font-size: 0.95em; ' +
|
||||
'cursor: move; ' +
|
||||
'" ' +
|
||||
'>' +
|
||||
obj.label +
|
||||
@@ -276,7 +279,9 @@ define(['js/capa/drag_and_drop/draggable_events', 'js/capa/drag_and_drop/draggab
|
||||
'position: absolute; ' +
|
||||
'color: black; ' +
|
||||
'font-size: 0.95em; ' +
|
||||
'cursor: move; ' +
|
||||
'" ' +
|
||||
'tabindex="0" aria-grabbed="false" role="listitem"' +
|
||||
'>' +
|
||||
obj.label +
|
||||
'</div>'
|
||||
|
||||
@@ -86,9 +86,8 @@ define([], function () {
|
||||
'left: ' + obj.x + 'px; ' +
|
||||
borderCss +
|
||||
'" ' +
|
||||
'></div>'
|
||||
'aria-dropeffect=""></div>'
|
||||
);
|
||||
|
||||
if (fromTargetField === true) {
|
||||
targetEl.appendTo(draggableObj.iconEl);
|
||||
} else {
|
||||
@@ -115,7 +114,6 @@ define([], function () {
|
||||
'background-color: white; ' +
|
||||
'font-size: 0.95em; ' +
|
||||
'color: #009fe2; ' +
|
||||
'cursor: pointer; ' +
|
||||
'" ' +
|
||||
'>0</div>'
|
||||
);
|
||||
|
||||
@@ -27,6 +27,11 @@ class UniqueCourseTest(WebAppTest):
|
||||
"""
|
||||
Create a unique course ID.
|
||||
"""
|
||||
super(UniqueCourseTest, self).__init__(*args, **kwargs)
|
||||
|
||||
def setUp(self):
|
||||
super(UniqueCourseTest, self).setUp()
|
||||
|
||||
self.course_info = {
|
||||
'org': 'test_org',
|
||||
'number': self.unique_id,
|
||||
@@ -34,8 +39,6 @@ class UniqueCourseTest(WebAppTest):
|
||||
'display_name': 'Test Course' + self.unique_id
|
||||
}
|
||||
|
||||
super(UniqueCourseTest, self).__init__(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def course_id(self):
|
||||
return self.COURSE_ID_SEPARATOR.join([
|
||||
|
||||
@@ -75,7 +75,7 @@ class CoursePagesTest(UniqueCourseTest):
|
||||
"""
|
||||
Install a course with no content using a fixture.
|
||||
"""
|
||||
super(UniqueCourseTest, self).setUp()
|
||||
super(CoursePagesTest, self).setUp()
|
||||
|
||||
CourseFixture(
|
||||
self.course_info['org'],
|
||||
|
||||
@@ -13,6 +13,7 @@ from ..pages.lms.course_nav import CourseNavPage
|
||||
from ..pages.lms.auto_auth import AutoAuthPage
|
||||
from ..pages.lms.course_info import CourseInfoPage
|
||||
from ..fixtures.course import CourseFixture, XBlockFixtureDesc
|
||||
from box.test.flaky import flaky
|
||||
|
||||
VIDEO_SOURCE_PORT = 8777
|
||||
YOUTUBE_STUB_PORT = 9080
|
||||
@@ -36,6 +37,7 @@ class YouTubeConfigError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@flaky
|
||||
class VideoBaseTest(UniqueCourseTest):
|
||||
"""
|
||||
Base class for tests of the Video Player
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
Peer Assessments for Students
|
||||
#############################
|
||||
|
||||
This documentation has moved! Please click `Creating Peer Assessments <http://edx.readthedocs.org/projects/edx-open-response-assessments/en/latest/PeerAssessment_Students.html>`_ to see the latest peer assessment information for students.
|
||||
This documentation has moved! Please click `Peer Assessments for Students <http://edx.readthedocs.org/projects/edx-open-response-assessments/en/latest/PeerAssessment_Students.html>`_ to see the latest peer assessment information for students.
|
||||
@@ -7,10 +7,4 @@
|
||||
Creating Peer Assessments
|
||||
========================================
|
||||
|
||||
.. toctree::
|
||||
:numbered:
|
||||
:maxdepth: 2
|
||||
|
||||
PeerAssessment
|
||||
PeerAssessment_Students
|
||||
Access_PA_Info
|
||||
This documentation has moved! Please click `Creating Peer Assessments <http://edx.readthedocs.org/projects/edx-open-response-assessments/en/latest/>`_ to see the latest information about peer assessments.
|
||||
@@ -11,6 +11,12 @@ Change Log
|
||||
|
||||
* - Date
|
||||
- Change
|
||||
* - 05/22/14
|
||||
- Added descriptions of five video- and problem-related events to the
|
||||
:ref:`Tracking Logs` chapter.
|
||||
* -
|
||||
_ Added the new ``edx.forum.searched`` event to the
|
||||
:ref:`Tracking Logs` chapter.
|
||||
* - 05/06/14
|
||||
- Added enrollment event types to the :ref:`Tracking Logs` chapter.
|
||||
* - 05/05/14
|
||||
|
||||
@@ -492,10 +492,11 @@ The browser emits these events when a user works with a video.
|
||||
- EdX ID of the video being watched (for example, i4x-HarvardX-PH207x-video-Simple_Random_Sample).
|
||||
* - ``code``
|
||||
- string
|
||||
- YouTube ID of the video being watched (for example, FU3fCJNs94Y).
|
||||
- For YouTube videos, the ID of the video being loaded (for example,
|
||||
OEyXaRPEzfM). For non-YouTube videos, 'html5'.
|
||||
* - ``currentTime``
|
||||
- float
|
||||
- Time the video was played at, in seconds.
|
||||
- Time the video was played, in seconds.
|
||||
* - ``speed``
|
||||
- string
|
||||
- Video speed in use: '0.75', '1.0', '1.25', '1.50'.
|
||||
@@ -554,34 +555,69 @@ playing speed for the video.
|
||||
-
|
||||
- The speed that the user selected for the video to play.
|
||||
|
||||
.. types needed
|
||||
``load_video``
|
||||
-----------------
|
||||
|
||||
.. ``load_video``
|
||||
.. -----------------
|
||||
The browser emits ``load_video`` events when the video is fully rendered and ready to play.
|
||||
|
||||
.. %%TBD
|
||||
``event`` **Member Fields**:
|
||||
|
||||
.. The browser emits ``load_video`` events when .
|
||||
.. list-table::
|
||||
:widths: 15 15 60
|
||||
:header-rows: 1
|
||||
|
||||
.. ``event`` **Member Fields**: None
|
||||
* - Field
|
||||
- Type
|
||||
- Details
|
||||
* - ``code``
|
||||
- string
|
||||
- For YouTube videos, the ID of the video being loaded (for example,
|
||||
OEyXaRPEzfM). For non-YouTube videos, 'html5'.
|
||||
|
||||
.. ``hide_transcript``
|
||||
.. -------------------
|
||||
``hide_transcript``
|
||||
-------------------
|
||||
|
||||
.. %%TBD
|
||||
The browser emits ``hide_transcript`` events when the user clicks **CC** to
|
||||
suppress display of the video transcript.
|
||||
|
||||
.. The browser emits ``hide_transcript`` events when .
|
||||
``event`` **Member Fields**:
|
||||
|
||||
.. ``event`` **Member Fields**:
|
||||
.. list-table::
|
||||
:widths: 15 15 60
|
||||
:header-rows: 1
|
||||
|
||||
.. ``show_transcript``
|
||||
.. --------------------
|
||||
* - Field
|
||||
- Type
|
||||
- Details
|
||||
* - ``code``
|
||||
- string
|
||||
- For YouTube videos, the ID of the video being loaded (for example,
|
||||
OEyXaRPEzfM). For non-YouTube videos, 'html5'.
|
||||
* - ``currentTime``
|
||||
- float
|
||||
- The point in the video file at which the transcript was hidden, in seconds.
|
||||
|
||||
.. %%TBD
|
||||
``show_transcript``
|
||||
--------------------
|
||||
|
||||
.. The browser emits ``show_transcript`` events when .
|
||||
The browser emits ``show_transcript`` events when the user clicks **CC** to
|
||||
display the video transcript.
|
||||
|
||||
.. ``event`` **Member Fields**:
|
||||
|
||||
.. list-table::
|
||||
:widths: 15 15 60
|
||||
:header-rows: 1
|
||||
|
||||
* - Field
|
||||
- Type
|
||||
- Details
|
||||
* - ``code``
|
||||
- string
|
||||
- For YouTube videos, the ID of the video being loaded (for example,
|
||||
OEyXaRPEzfM). For non-YouTube videos, 'html5'.
|
||||
* - ``currentTime``
|
||||
- float
|
||||
- The point in the video file at which the transcript was opened, in seconds.
|
||||
|
||||
.. _pdf:
|
||||
|
||||
@@ -1270,16 +1306,27 @@ The server emits ``problem_check_fail`` events when a problem cannot be checked
|
||||
- string
|
||||
- 'closed', 'unreset'
|
||||
|
||||
.. ``problem_reset``
|
||||
.. -----------------------------
|
||||
.. no sample to check
|
||||
.. The browser emits ``problem_reset`` events when a user resets a problem.
|
||||
.. %%is this an instructor initiated event?
|
||||
``problem_reset``
|
||||
--------------------
|
||||
|
||||
The browser emits ``problem_reset`` events when a user clicks **Reset** to reset the answer to a problem.
|
||||
|
||||
.. return Logger.log('problem_reset', [_this.answers, response.contents], _this.id);
|
||||
|
||||
.. **Event Source**: Browser
|
||||
**Event Source**: Browser
|
||||
|
||||
.. ``event`` **Member Fields**: None
|
||||
``event`` **Member Fields**:
|
||||
|
||||
.. list-table::
|
||||
:widths: 15 15 60
|
||||
:header-rows: 1
|
||||
|
||||
* - Field
|
||||
- Type
|
||||
- Details
|
||||
* - ``answers``
|
||||
- string
|
||||
- The value reset by the user.
|
||||
|
||||
``problem_rescore``
|
||||
-----------------------------
|
||||
@@ -1514,16 +1561,27 @@ The server emits ``save_problem_success`` events when a problem is saved success
|
||||
- dict
|
||||
-
|
||||
|
||||
.. ``problem_graded``
|
||||
.. -------------------
|
||||
|
||||
.. %%TBD
|
||||
|
||||
.. The server emits a ``problem_graded`` event %%%
|
||||
|
||||
``problem_graded``
|
||||
-------------------
|
||||
.. return Logger.log('problem_graded', [_this.answers, response.contents], _this.id);
|
||||
The server emits a ``problem_graded`` event each time a user clicks **Check**
|
||||
for a problem and it is graded successfully.
|
||||
|
||||
.. ``event`` **Member Fields**: The ``event`` field delivers the values entered for the problem component in Studio as a string. The display name, problem text, and choices or response field labels are included.
|
||||
``event`` **Member Fields**:
|
||||
|
||||
.. list-table::
|
||||
:widths: 15 15 60
|
||||
:header-rows: 1
|
||||
|
||||
* - Field
|
||||
- Type
|
||||
- Details
|
||||
* - ``[answers, contents]``
|
||||
- array
|
||||
- ``answers`` provides the value checked by the user. ``contents`` delivers
|
||||
HTML using data entered for the problem in Studio, including the display
|
||||
name, problem text, and choices or response field labels. The array
|
||||
includes each problem in a problem component that has multiple problems.
|
||||
|
||||
.. _ora:
|
||||
|
||||
|
||||
@@ -18,10 +18,7 @@ in the process of introducing other changes.
|
||||
To check the quality of your pull request, just go to the top level of the
|
||||
edx-platform codebase and run::
|
||||
|
||||
$ paver run_quality
|
||||
|
||||
Note: The rake commands ``rake quality``, ``rake pep8``, and ``rake pylint`` are now deprecated
|
||||
to ``paver run_quality``, ``paver run_pep8``, and ``paver run_pylint`` respectively.
|
||||
$ rake quality
|
||||
|
||||
This will print a report of the quality violations that your branch has made.
|
||||
|
||||
|
||||
183
docs/en_us/install_operations/Makefile
Normal file
183
docs/en_us/install_operations/Makefile
Normal file
@@ -0,0 +1,183 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
endif
|
||||
|
||||
Q_FLAG =
|
||||
|
||||
ifeq ($(quiet), true)
|
||||
Q_FLAG = -Q
|
||||
endif
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = $(Q_FLAG) -d $(BUILDDIR)/doctrees -c source $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/getting_started.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/getting_started.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/getting_started"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/getting_started"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
0
docs/en_us/install_operations/__init__.py
Normal file
0
docs/en_us/install_operations/__init__.py
Normal file
17
docs/en_us/install_operations/source/change_log.rst
Normal file
17
docs/en_us/install_operations/source/change_log.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
############
|
||||
Change Log
|
||||
############
|
||||
|
||||
************
|
||||
May, 2014
|
||||
************
|
||||
|
||||
.. list-table::
|
||||
:widths: 10 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Date
|
||||
- Change
|
||||
* - 05/21/2014
|
||||
- The initial release of this guide, with the sections :ref:`Installing the edX Developer Stack` and :ref:`Running the edX Developer Stack`.
|
||||
|
||||
46
docs/en_us/install_operations/source/conf.py
Normal file
46
docs/en_us/install_operations/source/conf.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
import sys, os
|
||||
|
||||
# on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
|
||||
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||
import sphinx_rtd_theme
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
sys.path.append(os.path.abspath('../../../'))
|
||||
sys.path.append(os.path.abspath('../../'))
|
||||
|
||||
#from docs.shared.conf import *
|
||||
|
||||
sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
#templates_path.append('source/_templates')
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
#html_static_path.append('source/_static')
|
||||
|
||||
|
||||
# General information about the project.
|
||||
project = u'Installing, Configuring, and Running the edX Platform'
|
||||
copyright = u'2014, edX'
|
||||
|
||||
# The short X.Y version.
|
||||
version = ''
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = ''
|
||||
@@ -0,0 +1,139 @@
|
||||
.. include:: ../links.rst
|
||||
|
||||
.. _Installing the edX Developer Stack:
|
||||
|
||||
####################################
|
||||
Installing the edX Developer Stack
|
||||
####################################
|
||||
|
||||
This chapter is intended for those who are installing and running the edX Developer Stack.
|
||||
|
||||
See the following sections:
|
||||
|
||||
* `Overview`_
|
||||
* `Components`_
|
||||
* `Knowledge Prerequisites`_
|
||||
* `Software Prerequisites`_
|
||||
* `Install DevStack`_
|
||||
* `Install DevStack using the Torrent file`_
|
||||
|
||||
|
||||
**********
|
||||
Overview
|
||||
**********
|
||||
|
||||
The edX Developer Stack, known as **Devstack**, is a Vagrant instance designed
|
||||
for local development.
|
||||
|
||||
Devstack:
|
||||
|
||||
* Uses the same system requirements as production. This allows you to
|
||||
discover and fix system configuration issues early in development.
|
||||
|
||||
* Simplifies certain production settings to make development more convenient.
|
||||
For example, `nginx`_ and `gunicorn`_ are disabled in Devstack; Devstack uses
|
||||
Django's runserver instead.
|
||||
|
||||
|
||||
********************
|
||||
Components
|
||||
********************
|
||||
|
||||
Devstack includes the following edX components:
|
||||
|
||||
* The Learning Management System (LMS)
|
||||
* edX Studio
|
||||
* Discussion Forums
|
||||
* Open Response Assessor (ORA)
|
||||
|
||||
|
||||
**************************
|
||||
Knowledge Prerequisites
|
||||
**************************
|
||||
|
||||
To use Devstack, you should:
|
||||
|
||||
* Understand basic terminal usage. If you are using a Mac computer, see
|
||||
`Introduction to the Mac OS X Command Line`_. If you are using a Windows
|
||||
computer, see `Windows Command Line Reference`_.
|
||||
|
||||
* Understand Vagrant commands. See the `Vagrant Getting Started`_ guide for more
|
||||
information.
|
||||
|
||||
|
||||
**************************
|
||||
Software Prerequisites
|
||||
**************************
|
||||
|
||||
To install and run Devstack, you must first install:
|
||||
|
||||
* `VirtualBox`_ 4.3.10 or higher
|
||||
|
||||
* `Vagrant`_ 1.5.3 or higher
|
||||
|
||||
* An NFS client, if your operating system does not include one. Devstack uses
|
||||
VirtualBox Guest Editions to share folders through NFS.
|
||||
|
||||
|
||||
**************************
|
||||
Install DevStack
|
||||
**************************
|
||||
|
||||
To install Devstack directly from the command line, follow the instructions
|
||||
below. You can also install DevStack using a Torrent file, as explained in the
|
||||
next section.
|
||||
|
||||
Before beginning the installation, ensure that you have your local computer's administrator's password.
|
||||
|
||||
#. Ensure the NFSD client is running.
|
||||
|
||||
#. Create the ``devstack`` directory and navigate to it in the command prompt.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir devstack
|
||||
cd devstack
|
||||
|
||||
#. Download the Devstack Vagrant file.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -L https://raw.github.com/edx/configuration/master/vagrant/release/devstack/Vagrantfile > Vagrantfile
|
||||
|
||||
#. Install the Vagrant vbguest plugin.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vagrant plugin install vagrant-vbguest
|
||||
|
||||
#. Create the Devstack virtual machine.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vagrant up
|
||||
|
||||
The first time you create the Devstack virtual machine, Vagrant downloads the
|
||||
base box, which is about 4GB. If you destroy and recreate the virtual
|
||||
machine, Vagrant re-uses the box it downloaded.
|
||||
|
||||
#. When prompted, enter your local computer's administrator's password.
|
||||
|
||||
Your password is needed so that NFS can be set up to allow users to access
|
||||
code directories directly from your computer.
|
||||
|
||||
When you have completed these steps, see :ref:`Running the edX Developer Stack`
|
||||
to begin using Devstack.
|
||||
|
||||
|
||||
*****************************************
|
||||
Install Devstack using the Torrent file
|
||||
*****************************************
|
||||
|
||||
#. Download the Devstack `Torrent`_ file.
|
||||
|
||||
#. When you have the file on your computer, add the Virtual machine using the
|
||||
command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vagrant box add box-name path-to-box-file
|
||||
196
docs/en_us/install_operations/source/devstack/run_devstack.rst
Normal file
196
docs/en_us/install_operations/source/devstack/run_devstack.rst
Normal file
@@ -0,0 +1,196 @@
|
||||
.. include:: ../links.rst
|
||||
|
||||
.. _Running the edX Developer Stack:
|
||||
|
||||
####################################
|
||||
Running the edX Developer Stack
|
||||
####################################
|
||||
|
||||
See the following sections:
|
||||
|
||||
* `Connect to the Devstack Virtual Machine`_
|
||||
* `Set Up Ability to Preview Units (Mac/Linux Only)`_
|
||||
* `Customize the Source Code Location`_
|
||||
* `Run the LMS on Devstack`_
|
||||
* `Run Studio on Devstack`_
|
||||
* `Run Discussion Forums on Devstack`_
|
||||
* `Default Accounts on Devstack`_
|
||||
|
||||
|
||||
****************************************
|
||||
Connect to the Devstack Virtual Machine
|
||||
****************************************
|
||||
|
||||
#. To connect to the Devstack virtual machine, use the SSH command from the
|
||||
`devstack` directory.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
vagrant ssh
|
||||
|
||||
2. To use Devstack and perform any of the tasks described in this section, you
|
||||
must connect as the user **edxapp**.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo su edxapp
|
||||
|
||||
This command loads the edxapp environment from the file
|
||||
``/edx/app/edxapp/edxapp_env``. This puts ``venv python``, ``rbenv ruby`` and
|
||||
``rake`` are in your search path.
|
||||
|
||||
This command also sets the current working directory to the edx-platform
|
||||
repository (``/edx/app/edxapp/edx-platform``).
|
||||
|
||||
|
||||
****************************************************
|
||||
Set Up Ability to Preview Units (Mac/Linux Only)
|
||||
****************************************************
|
||||
|
||||
If you are installing Devstack on a Linux or Macintosh computer, in order to use
|
||||
the preview feature in edX Studio, you must add the following line to the
|
||||
``etc/hosts`` file:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
192.168.33.10 preview.localhost
|
||||
|
||||
|
||||
************************************
|
||||
Customize the Source Code Location
|
||||
************************************
|
||||
|
||||
You can customize the location of the edX source code that gets cloned when you
|
||||
provision Devstack. You may want to do this to have Devstack work with source
|
||||
code that already exists on your computer.
|
||||
|
||||
By default, the source code location is the directory in which you run ``vagrant
|
||||
up``. To change this location, set the ``VAGRANT_MOUNT_BASE`` environment
|
||||
variable to set the base directory for the edx-platform and cs_comments_service
|
||||
source code directories.
|
||||
|
||||
.. WHERE IS VARIABLE?
|
||||
|
||||
************************************
|
||||
Run the LMS on Devstack
|
||||
************************************
|
||||
|
||||
When you run the LMS on Devstack, the command updates requirements and
|
||||
compiles assets, unless you use the ``fast`` option.
|
||||
|
||||
The command uses the file ``lms/envs/devstack.py``. This file
|
||||
overrides production settings for the LMS.
|
||||
|
||||
To run the LMS on Devstack:
|
||||
|
||||
#. `Connect to the Devstack Virtual Machine`_.
|
||||
#. Run the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
paver devstack lms
|
||||
|
||||
Or, to start the LMS without updating requirements and compiling assets, use the ``fast`` option:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
paver devstack --fast lms
|
||||
|
||||
The LMS starts.
|
||||
|
||||
#. Open the LMS in your browser at ``http://localhost:8000/``.
|
||||
|
||||
Vagrant forwards port 8000 to the LMS server running in the virtual machine.
|
||||
|
||||
|
||||
************************************
|
||||
Run Studio on Devstack
|
||||
************************************
|
||||
|
||||
When you run Studio on Devstack, the command updates requirements and compiles
|
||||
assets, unless you use the ``fast`` option.
|
||||
|
||||
You run Studio on Devstack with the file ``cms/envs/devstack.py``. This file
|
||||
overrides production settings for Studio.
|
||||
|
||||
To run Studio on Devstack:
|
||||
|
||||
#. `Connect to the Devstack Virtual Machine`_.
|
||||
#. Run the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
paver devstack studio
|
||||
|
||||
Or, to start Studio without updating requirements and compiling assets, use
|
||||
the ``fast`` option:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
paver devstack --fast studio
|
||||
|
||||
Studio starts.
|
||||
|
||||
#. Open the LMS in your browser at ``http://localhost:8001/``.
|
||||
|
||||
Vagrant forwards port 8001 to the Studio server running in the virtual
|
||||
machine.
|
||||
|
||||
************************************
|
||||
Run Discussion Forums on Devstack
|
||||
************************************
|
||||
|
||||
To run discussion forums on Devstack:
|
||||
|
||||
#. `Connect to the Devstack Virtual Machine`_.
|
||||
#. Switch to the discussion forum account:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo su forum
|
||||
|
||||
#. Update Ruby requirements.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
bundle install
|
||||
|
||||
.. note::
|
||||
If you get a message for entering a password to install the bundled
|
||||
RubyGems to the system, you can safely exit by entering ``control+c`` on a
|
||||
Macintosh or ``Ctrl+C`` on Windows. The RubyGems will still be installed
|
||||
correctly for the forum user.
|
||||
|
||||
#. Start the discussion forums server.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
ruby app.rb -p 18080
|
||||
|
||||
The discussions forum server starts. You can access the discussion forums API at
|
||||
``http://localhost:18080/``.
|
||||
|
||||
************************************
|
||||
Default Accounts on Devstack
|
||||
************************************
|
||||
|
||||
When you install Devstack, the following accounts are created:
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 60
|
||||
:header-rows: 1
|
||||
|
||||
* - Account
|
||||
- Description
|
||||
* - staff@example.com
|
||||
- An LMS and Studio user with course creation and editing permissions. This
|
||||
user is a course staff member with rights to work with the demonstration
|
||||
course in Studio.
|
||||
* - verified@example.com
|
||||
- A student account that you can use to access the LMS for testing verified certificates.
|
||||
* - audit@example.com
|
||||
- A student account that you can use the access the LMS for testing course auditing.
|
||||
* - honor@example.com
|
||||
- A student account that you can use the access the LMS for testing honor code certificates.
|
||||
|
||||
The password for all of these accounts is ``edx``.
|
||||
@@ -0,0 +1,9 @@
|
||||
.. include:: ../links.rst
|
||||
|
||||
|
||||
.. _Testing on the edX Developer Stack:
|
||||
|
||||
####################################
|
||||
Testing on the edX Developer Stack
|
||||
####################################
|
||||
|
||||
19
docs/en_us/install_operations/source/index.rst
Executable file
19
docs/en_us/install_operations/source/index.rst
Executable file
@@ -0,0 +1,19 @@
|
||||
.. Getting_Started documentation master file, created by
|
||||
sphinx-quickstart on Tue Apr 16 11:19:12 2013.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
######################################################
|
||||
Installing, Configuring, and Running the edX Platform
|
||||
######################################################
|
||||
|
||||
.. toctree::
|
||||
:numbered:
|
||||
:maxdepth: 2
|
||||
|
||||
read_me
|
||||
change_log
|
||||
install_options
|
||||
devstack/install_devstack
|
||||
devstack/run_devstack
|
||||
.. devstack/test_devstack
|
||||
60
docs/en_us/install_operations/source/install_options.rst
Normal file
60
docs/en_us/install_operations/source/install_options.rst
Normal file
@@ -0,0 +1,60 @@
|
||||
.. include:: links.rst
|
||||
|
||||
########################################
|
||||
Open edX Platform Installation Options
|
||||
########################################
|
||||
|
||||
You can install the Open edX Platform in one of the following three ways.
|
||||
|
||||
**********
|
||||
Devstack
|
||||
**********
|
||||
|
||||
The edX Developer Stack, known as **Devstack**, is a Vagrant instance designed
|
||||
for local development.
|
||||
|
||||
Devstack is in the `edx configuration repository`_ on GitHub.
|
||||
|
||||
This guide includes the following sections about Devstack:
|
||||
|
||||
* :ref:`Installing the edX Developer Stack`
|
||||
|
||||
* :ref:`Running the edX Developer Stack`
|
||||
|
||||
Additional sections are planned for future versions of this guide.
|
||||
|
||||
See the `edx configuration repository wiki`_ for information from
|
||||
edX and the Open edX community about Devstack and other installation and
|
||||
configuration options. This wiki contains two pages with more information about Devstack:
|
||||
|
||||
* `Devstack wiki`_
|
||||
* `Developing on Devstack`_
|
||||
|
||||
|
||||
**********
|
||||
Fullstack
|
||||
**********
|
||||
|
||||
The edX Production Stack, known as **Fullstack**, is a Vagrant instance designed
|
||||
to set up all edX services on a single server in a production-like
|
||||
configuration.
|
||||
1
|
||||
Fullstack is in the `edx configuration repository`_ on GitHub.
|
||||
|
||||
Fullstack information is planned for future versions of this guide.
|
||||
|
||||
You can see the `edx configuration repository wiki`_ for information from edX
|
||||
and the Open edX community on Fullstack and other installation and configuration options.
|
||||
|
||||
|
||||
==================
|
||||
Ubuntu 12.04 64
|
||||
==================
|
||||
|
||||
You can install the edX Production Stack on a single Ubuntu 12.04 64-bit server.
|
||||
|
||||
Ubuntu information is planned for future versions of this guide.
|
||||
|
||||
You can see the `edx configuration repository wiki`_ for information from edX
|
||||
and the Open edX community about Ubuntu and other installation and configuration
|
||||
options.
|
||||
12
docs/en_us/install_operations/source/links.rst
Normal file
12
docs/en_us/install_operations/source/links.rst
Normal file
@@ -0,0 +1,12 @@
|
||||
.. _nginx: http://nginx.com
|
||||
.. _gunicorn: http://gunicorn.org
|
||||
.. _Introduction to the Mac OS X Command Line: http://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line
|
||||
.. _Windows Command Line Reference: http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/ntcmds.mspx?mfr=true
|
||||
.. _Vagrant Getting Started: http://docs.vagrantup.com/v2/getting-started/index.html
|
||||
.. _VirtualBox: https://www.virtualbox.org/wiki/Downloads
|
||||
.. _Vagrant: http://www.vagrantup.com/downloads.html
|
||||
.. _Torrent: https://s3.amazonaws.com/edx-static/vagrant-images/20140418-injera-devstack.box?torrent
|
||||
.. _edx configuration repository: https://github.com/edx/configuration
|
||||
.. _edx configuration repository wiki: https://github.com/edx/configuration/wiki
|
||||
.. _Devstack wiki: https://github.com/edx/configuration/wiki/edX-Developer-Stack
|
||||
.. _Developing on Devstack: https://github.com/edx/edx-platform/wiki/Developing-on-the-edX-Developer-Stack
|
||||
16
docs/en_us/install_operations/source/read_me.rst
Normal file
16
docs/en_us/install_operations/source/read_me.rst
Normal file
@@ -0,0 +1,16 @@
|
||||
#########
|
||||
Read Me
|
||||
#########
|
||||
|
||||
The edX *Installing and Configuring the edX Platform* documentation is created
|
||||
using RST_ files and Sphinx_. You, the user community, can help update and revise this documentation project on GitHub:
|
||||
|
||||
https://github.com/edx/edx-platform/tree/master/docs/install_operations/source
|
||||
|
||||
To suggest a revision, fork the project, make changes in your fork, and submit
|
||||
a pull request back to the original project: this is known as the `GitHub Flow`_.
|
||||
|
||||
.. _Sphinx: http://sphinx-doc.org/
|
||||
.. _LaTeX: http://www.latex-project.org/
|
||||
.. _`GitHub Flow`: https://github.com/blog/1557-github-flow-in-the-browser
|
||||
.. _RST: http://docutils.sourceforge.net/rst.html
|
||||
@@ -42,9 +42,10 @@ edX Studio
|
||||
:alt: Other help resources at the bottom of the Studio UI
|
||||
|
||||
|
||||
* A problem that caused the video transcript download button to not work for
|
||||
students is fixed. If this was a problem in your course, you must re-import
|
||||
the course in Studio. (BLD-1014)
|
||||
* A problem in a small number of XML-authored courses that caused the video
|
||||
transcript download button to not work for students is fixed. If your course
|
||||
was one of the courses affected, you must re-import the course in Studio to
|
||||
enable transcript downloads. (BLD-1014)
|
||||
|
||||
* A problem that prevented users with only course staff privileges from
|
||||
importing courses is resolved. (STUD-1595)
|
||||
|
||||
@@ -3,17 +3,28 @@
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
The edX *Release Notes for edX Course Staff* provides a cumulative
|
||||
list of changes that affect course authoring in edX Studio and the edX Learning
|
||||
Management System.
|
||||
####################################
|
||||
edX Course Staff Release Notes
|
||||
####################################
|
||||
|
||||
There is a page in this document for each update to the edX system on `edx.org`_ and `edX Edge`_. Each page contains information on new or changed documentation, and new features and changes in edX Studio, the edX Learning Management System, Discussions, Analytics, accessibility, and more.
|
||||
The *edX Course Staff Release Notes* provide a cumulative list of changes
|
||||
that affect course authoring in edX Studio and the edX Learning Management
|
||||
System.
|
||||
|
||||
This document contains a page for each update to the edX system, which includes
|
||||
both `edx.org`_ and `edX Edge`_. Each page contains information on new or
|
||||
changed documentation, as well as new features and changes in edX Studio, the
|
||||
edX Learning Management System, Discussion Forums, Analytics, accessibility, and
|
||||
more.
|
||||
|
||||
You can sign up for the new `edX Release Announcements mailing list`_ to get an
|
||||
email message when we have updated the edX Course Staff Release Notes.
|
||||
|
||||
|
||||
|
||||
#########
|
||||
*************
|
||||
2014
|
||||
#########
|
||||
*************
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
@@ -39,9 +50,9 @@ There is a page in this document for each update to the edX system on `edx.org`_
|
||||
01-16-2014
|
||||
01-07-2014
|
||||
|
||||
#########
|
||||
*************
|
||||
2013
|
||||
#########
|
||||
*************
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
paver commands
|
||||
"""
|
||||
__all__ = ["assets", "servers", "docs", "prereqs", "quality"]
|
||||
from . import assets, servers, docs, prereqs, quality
|
||||
__all__ = ["assets", "servers", "docs", "prereqs"]
|
||||
from . import assets, servers, docs, prereqs
|
||||
|
||||
@@ -104,7 +104,6 @@ def install_node_prereqs():
|
||||
sh('npm install')
|
||||
|
||||
|
||||
@task
|
||||
def install_python_prereqs():
|
||||
"""
|
||||
Installs Python prerequisites
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
"""
|
||||
Check code quality using pep8, pylint, and diff_quality.
|
||||
"""
|
||||
from paver.easy import sh, task, cmdopts, needs
|
||||
import os
|
||||
import errno
|
||||
from .utils.envs import Env
|
||||
|
||||
|
||||
def get_or_make_dir(directory_path):
|
||||
"""
|
||||
Ensure that a directory exists, and return its path
|
||||
"""
|
||||
try:
|
||||
os.makedirs(directory_path)
|
||||
except OSError as err:
|
||||
if err.errno != errno.EEXIST:
|
||||
# If we get an error other than one that says
|
||||
# that the file already exists
|
||||
raise
|
||||
|
||||
return directory_path
|
||||
|
||||
|
||||
@task
|
||||
@needs('pavelib.prereqs.install_python_prereqs')
|
||||
@cmdopts([
|
||||
("system=", "s", "System to act on"),
|
||||
("errors", "e", "Check for errors only"),
|
||||
])
|
||||
def run_pylint(options):
|
||||
"""
|
||||
Run pylint on system code
|
||||
"""
|
||||
system = getattr(options, 'system', 'lms')
|
||||
errors = getattr(options, 'errors', False)
|
||||
|
||||
# Directory to put the pylint report in.
|
||||
# This makes the folder if it doesn't already exist.
|
||||
report_dir = get_or_make_dir(os.path.join(Env.REPORT_DIR, system))
|
||||
|
||||
flags = '-E' if errors else ''
|
||||
|
||||
apps = [system]
|
||||
|
||||
for directory in ['djangoapps', 'lib']:
|
||||
dirs = os.listdir(os.path.join(system, directory))
|
||||
apps.extend([d for d in dirs if os.path.isdir(os.path.join(system, directory, d))])
|
||||
|
||||
apps_list = ' '.join(apps)
|
||||
|
||||
pythonpath_prefix = (
|
||||
"PYTHONPATH={system}:{system}/djangoapps:{system}/"
|
||||
"lib:common/djangoapps:common/lib".format(
|
||||
system=system
|
||||
)
|
||||
)
|
||||
|
||||
sh(
|
||||
"{pythonpath_prefix} pylint {flags} -f parseable {apps} | "
|
||||
"tee {report_dir}/pylint.report".format(
|
||||
pythonpath_prefix=pythonpath_prefix,
|
||||
flags=flags,
|
||||
apps=apps_list,
|
||||
report_dir=report_dir
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@task
|
||||
@needs('pavelib.prereqs.install_python_prereqs')
|
||||
@cmdopts([
|
||||
("system=", "s", "System to act on"),
|
||||
])
|
||||
def run_pep8(options):
|
||||
"""
|
||||
Run pep8 on system code
|
||||
"""
|
||||
system = getattr(options, 'system', 'lms')
|
||||
|
||||
# Directory to put the pep8 report in.
|
||||
# This makes the folder if it doesn't already exist.
|
||||
report_dir = get_or_make_dir(os.path.join(Env.REPORT_DIR, system))
|
||||
|
||||
sh('pep8 {system} | tee {report_dir}/pep8.report'.format(system=system, report_dir=report_dir))
|
||||
|
||||
|
||||
@task
|
||||
@needs('pavelib.prereqs.install_python_prereqs')
|
||||
def run_quality():
|
||||
"""
|
||||
Build the html diff quality reports, and print the reports to the console.
|
||||
"""
|
||||
|
||||
# Directory to put the diff reports in.
|
||||
# This makes the folder if it doesn't already exist.
|
||||
dquality_dir = get_or_make_dir(os.path.join(Env.REPORT_DIR, "diff_quality"))
|
||||
|
||||
# Generage diff-quality html report for pep8, and print to console
|
||||
# If pep8 reports exist, use those
|
||||
# Otherwise, `diff-quality` will call pep8 itself
|
||||
|
||||
pep8_files = []
|
||||
for subdir, _dirs, files in os.walk(os.path.join(Env.REPORT_DIR)):
|
||||
for f in files:
|
||||
if f == "pep8.report":
|
||||
pep8_files.append(os.path.join(subdir, f))
|
||||
|
||||
pep8_reports = u' '.join(pep8_files)
|
||||
|
||||
sh(
|
||||
"diff-quality --violations=pep8 --html-report {dquality_dir}/"
|
||||
"diff_quality_pep8.html {pep8_reports}".format(
|
||||
dquality_dir=dquality_dir, pep8_reports=pep8_reports)
|
||||
)
|
||||
|
||||
sh(
|
||||
"diff-quality --violations=pep8 {pep8_reports}".format(
|
||||
pep8_reports=pep8_reports)
|
||||
)
|
||||
|
||||
# Generage diff-quality html report for pylint, and print to console
|
||||
# If pylint reports exist, use those
|
||||
# Otherwise, `diff-quality` will call pylint itself
|
||||
|
||||
pylint_files = []
|
||||
for subdir, _dirs, files in os.walk(os.path.join(Env.REPORT_DIR)):
|
||||
for f in files:
|
||||
if f == "pylint.report":
|
||||
pylint_files.append(os.path.join(subdir, f))
|
||||
|
||||
pylint_reports = u' '.join(pylint_files)
|
||||
|
||||
pythonpath_prefix = (
|
||||
"PYTHONPATH=$PYTHONPATH:lms:lms/djangoapps:lms/lib:cms:cms/djangoapps:cms/lib:"
|
||||
"common:common/djangoapps:common/lib"
|
||||
)
|
||||
|
||||
sh(
|
||||
"{pythonpath_prefix} diff-quality --violations=pylint --html-report "
|
||||
"{dquality_dir}/diff_quality_pylint.html {pylint_reports}".format(
|
||||
pythonpath_prefix=pythonpath_prefix,
|
||||
dquality_dir=dquality_dir,
|
||||
pylint_reports=pylint_reports
|
||||
)
|
||||
)
|
||||
|
||||
sh(
|
||||
"{pythonpath_prefix} diff-quality --violations=pylint {pylint_reports}".format(
|
||||
pythonpath_prefix=pythonpath_prefix,
|
||||
pylint_reports=pylint_reports
|
||||
)
|
||||
)
|
||||
@@ -17,9 +17,6 @@ class Env(object):
|
||||
# Root of the git repository (edx-platform)
|
||||
REPO_ROOT = path(__file__).abspath().parent.parent.parent
|
||||
|
||||
# Reports Directory
|
||||
REPORT_DIR = REPO_ROOT / 'reports'
|
||||
|
||||
# Service variant (lms, cms, etc.) configured with an environment variable
|
||||
# We use this to determine which envs.json file to load.
|
||||
SERVICE_VARIANT = os.environ.get('SERVICE_VARIANT', None)
|
||||
|
||||
@@ -147,7 +147,7 @@ def run_bok_choy(test_spec)
|
||||
# Construct the nosetests command, specifying where to save screenshots and XUnit XML reports
|
||||
cmd = [
|
||||
"SCREENSHOT_DIR='#{BOK_CHOY_LOG_DIR}'", "nosetests", test_spec,
|
||||
"--with-xunit", "--xunit-file=#{BOK_CHOY_XUNIT_REPORT}", "--verbosity=2"
|
||||
"--with-xunit", "--with-flaky", "--xunit-file=#{BOK_CHOY_XUNIT_REPORT}", "--verbosity=2"
|
||||
]
|
||||
|
||||
# Configure parallel test execution, if specified
|
||||
|
||||
72
rakelib/quality.rake
Normal file
72
rakelib/quality.rake
Normal file
@@ -0,0 +1,72 @@
|
||||
def run_pylint(system, report_dir, flags='')
|
||||
apps = Dir["#{system}", "#{system}/djangoapps/*"]
|
||||
if system != 'lms'
|
||||
apps += Dir["#{system}/lib/*"]
|
||||
end
|
||||
|
||||
apps = apps.map do |app|
|
||||
File.basename(app)
|
||||
end.select do |app|
|
||||
app !=~ /.pyc$/
|
||||
end.map do |app|
|
||||
if app =~ /.py$/
|
||||
app.gsub('.py', '')
|
||||
else
|
||||
app
|
||||
end
|
||||
end
|
||||
|
||||
pythonpath_prefix = "PYTHONPATH=#{system}:#{system}/djangoapps:#{system}/lib:common/djangoapps:common/lib"
|
||||
sh("#{pythonpath_prefix} pylint #{flags} -f parseable #{apps.join(' ')} | tee #{report_dir}/pylint.report")
|
||||
end
|
||||
|
||||
|
||||
[:lms, :cms, :common].each do |system|
|
||||
report_dir = report_dir_path(system)
|
||||
directory report_dir
|
||||
|
||||
namespace :pylint do
|
||||
namespace system do
|
||||
desc "Run pylint checking for #{system} checking for errors only, and aborting if there are any"
|
||||
task :errors do
|
||||
run_pylint(system, report_dir, '-E')
|
||||
end
|
||||
end
|
||||
|
||||
desc "Run pylint on all #{system} code"
|
||||
task system => [report_dir, :install_python_prereqs] do
|
||||
run_pylint(system, report_dir)
|
||||
end
|
||||
end
|
||||
task :pylint => :"pylint:#{system}"
|
||||
|
||||
namespace :pep8 do
|
||||
desc "Run pep8 on all #{system} code"
|
||||
task system => [report_dir, :install_python_prereqs] do
|
||||
sh("pep8 #{system} | tee #{report_dir}/pep8.report")
|
||||
end
|
||||
end
|
||||
task :pep8 => :"pep8:#{system}"
|
||||
end
|
||||
|
||||
dquality_dir = File.join(REPORT_DIR, "diff_quality")
|
||||
directory dquality_dir
|
||||
|
||||
desc "Build the html diff quality reports, and print the reports to the console."
|
||||
task :quality => [dquality_dir, :install_python_prereqs] do
|
||||
|
||||
# Generage diff-quality html report for pep8, and print to console
|
||||
# If pep8 reports exist, use those
|
||||
# Otherwise, `diff-quality` will call pep8 itself
|
||||
pep8_reports = FileList[File.join(REPORT_DIR, '**/pep8.report')].join(' ')
|
||||
sh("diff-quality --violations=pep8 --html-report #{dquality_dir}/diff_quality_pep8.html #{pep8_reports}")
|
||||
sh("diff-quality --violations=pep8 #{pep8_reports}")
|
||||
|
||||
# Generage diff-quality html report for pylint, and print to console
|
||||
# If pylint reports exist, use those
|
||||
# Otherwise, `diff-quality` will call pylint itself
|
||||
pylint_reports = FileList[File.join(REPORT_DIR, '**/pylint.report')].join(' ')
|
||||
pythonpath_prefix = "PYTHONPATH=$PYTHONPATH:lms:lms/djangoapps:lms/lib:cms:cms/djangoapps:cms/lib:common:common/djangoapps:common/lib"
|
||||
sh("#{pythonpath_prefix} diff-quality --violations=pylint --html-report #{dquality_dir}/diff_quality_pylint.html #{pylint_reports}")
|
||||
sh("#{pythonpath_prefix} diff-quality --violations=pylint #{pylint_reports}")
|
||||
end
|
||||
@@ -1,39 +0,0 @@
|
||||
# quality tasks deprecated to paver
|
||||
|
||||
require 'colorize'
|
||||
|
||||
def deprecated(deprecated, deprecated_by)
|
||||
|
||||
task deprecated do
|
||||
|
||||
# Need to install paver dependencies for the commands to work!
|
||||
sh("pip install -r requirements/edx/paver.txt")
|
||||
|
||||
if deprecated.include? "extract" and ARGV.last.downcase == 'extract'
|
||||
new_cmd = deprecated_by + " --extract"
|
||||
else
|
||||
new_cmd = deprecated_by
|
||||
end
|
||||
|
||||
puts("Task #{deprecated} has been deprecated. Use #{deprecated_by} instead.".red)
|
||||
sh(new_cmd)
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
||||
deprecated("pep8:cms", "paver run_pep8 --system=cms")
|
||||
deprecated("pep8:lms", "paver run_pep8 --system=lms")
|
||||
deprecated("pep8:common", "paver run_pep8 --system=common")
|
||||
deprecated("pep8", "paver run_pep8")
|
||||
|
||||
deprecated("pylint:cms", "paver run_pylint --system=cms")
|
||||
deprecated("pylint:lms", "paver run_pylint --system=lms")
|
||||
deprecated("pylint:common", "paver run_pylint --system=common")
|
||||
deprecated("pylint", "paver run_pylint")
|
||||
|
||||
deprecated("pylint:cms:errors", "paver run_pylint --system=cms --errors")
|
||||
deprecated("pylint:lms:errors", "paver run_pylint --system=lms --errors")
|
||||
deprecated("pylint:common:errors", "paver run_pylint --system=common --errors")
|
||||
deprecated("pylint:errors", "paver run_pylint --errors")
|
||||
|
||||
deprecated("quality", "paver run_quality")
|
||||
@@ -125,6 +125,7 @@ rednose==0.3
|
||||
selenium==2.39.0
|
||||
splinter==0.5.4
|
||||
testtools==0.9.34
|
||||
flaky==0.2.0
|
||||
|
||||
git+https://github.com/mfogel/django-settings-context-processor.git
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Requirements for edx.org that aren't necessarily needed for Open edX.
|
||||
|
||||
-e git+ssh://git@github.com/jazkarta/edX-jsdraw.git@9fcd333aaa2ac3df65dd247b601ce0b56bb10cad#egg=edx-jsdraw
|
||||
-e git+https://github.com/gsehub/xblock-mentoring.git@43aa3202456b4c8674673474b81c51d8895d403d#egg=xblock-mentoring
|
||||
-e git+https://github.com/gsehub/xblock-mentoring.git@b779446e9f51b9a1576d514cc14166907316a106#egg=xblock-mentoring
|
||||
|
||||
Reference in New Issue
Block a user