Commit d54ac1eb by Matt Drayer

Merge pull request #10948 from edx/ziafazal/SOL-1375

ziafazal/SOL-1375: Create course to org link at the time of course creation
parents 094f7709 2cb2233d
......@@ -2,8 +2,10 @@
Test view handler for rerun (and eventually create)
"""
import ddt
from mock import patch
from django.test.client import RequestFactory
from django.core.urlresolvers import reverse
from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
......@@ -15,6 +17,10 @@ from student.tests.factories import UserFactory
from contentstore.tests.utils import AjaxEnabledTestClient, parse_json
from datetime import datetime
from xmodule.course_module import CourseFields
from util.organizations_helpers import (
add_organization,
get_course_organizations,
)
@ddt.ddt
......@@ -33,7 +39,7 @@ class TestCourseListing(ModuleStoreTestCase):
self.factory = RequestFactory()
self.client = AjaxEnabledTestClient()
self.client.login(username=self.user.username, password='test')
self.course_create_rerun_url = reverse('course_handler')
source_course = CourseFactory.create(
org='origin',
number='the_beginning',
......@@ -57,7 +63,7 @@ class TestCourseListing(ModuleStoreTestCase):
"""
Just testing the functionality the view handler adds over the tasks tested in test_clone_course
"""
response = self.client.ajax_post('/course/', {
response = self.client.ajax_post(self.course_create_rerun_url, {
'source_course_key': unicode(self.source_course_key),
'org': self.source_course_key.org, 'course': self.source_course_key.course, 'run': 'copy',
'display_name': 'not the same old name',
......@@ -76,7 +82,7 @@ class TestCourseListing(ModuleStoreTestCase):
Tests newly created course has web certs enabled by default.
"""
with modulestore().default_store(store):
response = self.client.ajax_post('/course/', {
response = self.client.ajax_post(self.course_create_rerun_url, {
'org': 'orgX',
'number': 'CS101',
'display_name': 'Course with web certs enabled',
......@@ -87,3 +93,66 @@ class TestCourseListing(ModuleStoreTestCase):
new_course_key = CourseKey.from_string(data['course_key'])
course = self.store.get_course(new_course_key)
self.assertTrue(course.cert_html_view_enabled)
@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': False})
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_course_creation_without_org_app_enabled(self, store):
"""
Tests course creation workflow should not create course to org
link if organizations_app is not enabled.
"""
with modulestore().default_store(store):
response = self.client.ajax_post(self.course_create_rerun_url, {
'org': 'orgX',
'number': 'CS101',
'display_name': 'Course with web certs enabled',
'run': '2015_T2'
})
self.assertEqual(response.status_code, 200)
data = parse_json(response)
new_course_key = CourseKey.from_string(data['course_key'])
course_orgs = get_course_organizations(new_course_key)
self.assertEqual(course_orgs, [])
@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': True})
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_course_creation_with_org_not_in_system(self, store):
"""
Tests course creation workflow when course organization does not exist
in system.
"""
with modulestore().default_store(store):
response = self.client.ajax_post(self.course_create_rerun_url, {
'org': 'orgX',
'number': 'CS101',
'display_name': 'Course with web certs enabled',
'run': '2015_T2'
})
self.assertEqual(response.status_code, 400)
data = parse_json(response)
self.assertIn(u'Organization you selected does not exist in the system', data['error'])
@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': True})
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_course_creation_with_org_in_system(self, store):
"""
Tests course creation workflow when course organization exist in system.
"""
add_organization({
'name': 'Test Organization',
'short_name': 'orgX',
'description': 'Testing Organization Description',
})
with modulestore().default_store(store):
response = self.client.ajax_post(self.course_create_rerun_url, {
'org': 'orgX',
'number': 'CS101',
'display_name': 'Course with web certs enabled',
'run': '2015_T2'
})
self.assertEqual(response.status_code, 200)
data = parse_json(response)
new_course_key = CourseKey.from_string(data['course_key'])
course_orgs = get_course_organizations(new_course_key)
self.assertEqual(len(course_orgs), 1)
self.assertEqual(course_orgs[0]['short_name'], 'orgX')
......@@ -84,6 +84,11 @@ from util.milestones_helpers import (
is_valid_course_key,
set_prerequisite_courses,
)
from util.organizations_helpers import (
add_organization_course,
get_organization_by_short_name,
organizations_enabled,
)
from util.string_utils import _has_non_ascii_characters
from xmodule.contentstore.content import StaticContent
from xmodule.course_module import CourseFields
......@@ -738,8 +743,17 @@ def _create_new_course(request, org, number, run, fields):
Returns the URL for the course overview page.
Raises DuplicateCourseError if the course already exists
"""
org_data = get_organization_by_short_name(org)
if not org_data and organizations_enabled():
return JsonResponse(
{'error': _('You must link this course to an organization in order to continue. '
'Organization you selected does not exist in the system, '
'you will need to add it to the system')},
status=400
)
store_for_new_course = modulestore().default_modulestore.get_modulestore_type()
new_course = create_new_course_in_store(store_for_new_course, request.user, org, number, run, fields)
add_organization_course(org_data, new_course.id)
return JsonResponse({
'url': reverse_course_url('course_handler', new_course.id),
'course_key': unicode(new_course.id),
......
"""Organizations views for use with Studio."""
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import View
from django.http import HttpResponse
from openedx.core.lib.js_utils import escape_json_dumps
from util.organizations_helpers import get_organizations
class OrganizationListView(View):
"""View rendering organization list as json.
This view renders organization list json which is used in org
autocomplete while creating new course.
"""
@method_decorator(login_required)
def get(self, request, *args, **kwargs):
"""Returns organization list as json."""
organizations = get_organizations()
org_names_list = [(org["short_name"]) for org in organizations]
return HttpResponse(escape_json_dumps(org_names_list), content_type='application/json; charset=utf-8')
"""Tests covering the Organizations listing on the Studio home."""
import json
from mock import patch
from django.core.urlresolvers import reverse
from django.test import TestCase
from util.organizations_helpers import add_organization
from student.tests.factories import UserFactory
@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': True})
class TestOrganizationListing(TestCase):
"""Verify Organization listing behavior."""
@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': True})
def setUp(self):
super(TestOrganizationListing, self).setUp()
self.staff = UserFactory(is_staff=True)
self.client.login(username=self.staff.username, password='test')
self.org_names_listing_url = reverse('organizations')
self.org_short_names = ["alphaX", "betaX", "orgX"]
for index, short_name in enumerate(self.org_short_names):
add_organization(organization_data={
'name': 'Test Organization %s' % index,
'short_name': short_name,
'description': 'Testing Organization %s Description' % index,
})
def test_organization_list(self):
"""Verify that the organization names list api returns list of organization short names."""
response = self.client.get(self.org_names_listing_url, HTTP_ACCEPT='application/json')
self.assertEqual(response.status_code, 200)
org_names = json.loads(response.content)
self.assertEqual(org_names, self.org_short_names)
......@@ -109,6 +109,8 @@ YOUTUBE['TEXT_API']['url'] = "127.0.0.1:{0}/test_transcripts_youtube/".format(YO
FEATURES['ENABLE_COURSEWARE_INDEX'] = True
FEATURES['ENABLE_LIBRARY_INDEX'] = True
FEATURES['ORGANIZATIONS_APP'] = True
SEARCH_ENGINE = "search.tests.mock_search_engine.MockSearchEngine"
# Path at which to store the mock index
MOCK_SEARCH_BACKING_FILE = (
......
......@@ -180,6 +180,8 @@ FEATURES = {
# Special Exams, aka Timed and Proctored Exams
'ENABLE_SPECIAL_EXAMS': False,
'ORGANIZATIONS_APP': False,
}
ENABLE_JASMINE = False
......@@ -924,6 +926,9 @@ OPTIONAL_APPS = (
# milestones
'milestones',
# Organizations App (http://github.com/edx/edx-organizations)
'organizations',
)
......
......@@ -91,7 +91,7 @@ define(["domReady", "jquery", "underscore", "js/utils/cancel_on_escape", "js/vie
$('.new-course-save').on('click', saveNewCourse);
$cancelButton.bind('click', makeCancelHandler('course'));
CancelOnEscape($cancelButton);
CreateCourseUtils.setupOrgAutocomplete();
CreateCourseUtils.configureHandlers();
};
......
......@@ -41,6 +41,8 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers
var requests = AjaxHelpers.requests(this);
var redirectSpy = spyOn(ViewUtils, 'redirect');
$('.new-course-button').click()
AjaxHelpers.expectJsonRequest(requests, 'GET', '/organizations');
AjaxHelpers.respondWithJson(requests, ['DemoX', 'DemoX2', 'DemoX3']);
fillInFields('DemoX', 'DM101', '2014', 'Demo course');
$('.new-course-save').click();
AjaxHelpers.expectJsonRequest(requests, 'POST', '/course/', {
......@@ -53,11 +55,14 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers
url: 'dummy_test_url'
});
expect(redirectSpy).toHaveBeenCalledWith('dummy_test_url');
$(".new-course-org").autocomplete("destroy");
});
it("displays an error when saving fails", function () {
var requests = AjaxHelpers.requests(this);
$('.new-course-button').click();
AjaxHelpers.expectJsonRequest(requests, 'GET', '/organizations');
AjaxHelpers.respondWithJson(requests, ['DemoX', 'DemoX2', 'DemoX3']);
fillInFields('DemoX', 'DM101', '2014', 'Demo course');
$('.new-course-save').click();
AjaxHelpers.respondWithJson(requests, {
......@@ -67,6 +72,7 @@ define(["jquery", "common/js/spec_helpers/ajax_helpers", "common/js/spec_helpers
expect($('#course_creation_error')).toContainText('error message');
expect($('.new-course-save')).toHaveClass('is-disabled');
expect($('.new-course-save')).toHaveAttr('aria-disabled', 'true');
$(".new-course-org").autocomplete("destroy");
});
it("saves new libraries", function () {
......
......@@ -11,6 +11,14 @@ define(["jquery", "gettext", "common/js/components/utils/view_utils", "js/views/
CreateUtilsFactory.call(this, selectors, classes, keyLengthViolationMessage, keyFieldSelectors, nonEmptyCheckFieldSelectors);
this.setupOrgAutocomplete = function(){
$.getJSON('/organizations', function (data) {
$(selectors.org).autocomplete({
source: data
});
});
};
this.create = function (courseInfo, errorHandler) {
$.postJSON(
'/course/',
......
......@@ -157,3 +157,35 @@
}
}
}
.ui-autocomplete {
position: absolute;
top: 0;
left: 0;
margin: 0;
padding: 0;
cursor: default;
@include linear-gradient($gray-l5, $white);
border-right: 1px solid $gray-l2;
border-bottom: 1px solid $gray-l2;
border-left: 1px solid $gray-l2;
background-color: $gray-l5;
box-shadow: inset 0 1px 2px $shadow-l1;
color: $color-copy-emphasized;
li.ui-menu-item{
padding: 0;
margin: 0;
a {
color: $color-copy-emphasized;
}
a.ui-state-focus{
border: none;
background-color: $blue;
background: $blue;
color: $white;
}
}
}
......@@ -4,7 +4,7 @@ from django.conf.urls import patterns, include, url
from ratelimitbackend import admin
from cms.djangoapps.contentstore.views.program import ProgramAuthoringView, ProgramsIdTokenView
from cms.djangoapps.contentstore.views.organization import OrganizationListView
admin.autodiscover()
......@@ -41,6 +41,7 @@ urlpatterns = patterns(
url(r'^not_found$', 'contentstore.views.not_found', name='not_found'),
url(r'^server_error$', 'contentstore.views.server_error', name='server_error'),
url(r'^organizations$', OrganizationListView.as_view(), name='organizations'),
# temporary landing page for edge
url(r'^edge$', 'contentstore.views.edge', name='edge'),
......
......@@ -10,7 +10,7 @@ def add_organization(organization_data):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('ORGANIZATIONS_APP', False):
if not organizations_enabled():
return None
from organizations import api as organizations_api
return organizations_api.add_organization(organization_data=organization_data)
......@@ -20,7 +20,7 @@ def add_organization_course(organization_data, course_id):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('ORGANIZATIONS_APP', False):
if not organizations_enabled():
return None
from organizations import api as organizations_api
return organizations_api.add_organization_course(organization_data=organization_data, course_key=course_id)
......@@ -30,17 +30,31 @@ def get_organization(organization_id):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('ORGANIZATIONS_APP', False):
if not organizations_enabled():
return []
from organizations import api as organizations_api
return organizations_api.get_organization(organization_id)
def get_organization_by_short_name(organization_short_name):
"""
Client API operation adapter/wrapper
"""
if not organizations_enabled():
return None
from organizations import api as organizations_api
from organizations.exceptions import InvalidOrganizationException
try:
return organizations_api.get_organization_by_short_name(organization_short_name)
except InvalidOrganizationException:
return None
def get_organizations():
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('ORGANIZATIONS_APP', False):
if not organizations_enabled():
return []
from organizations import api as organizations_api
# Due to the way unit tests run for edx-platform, models are not yet available at the time
......@@ -58,7 +72,7 @@ def get_organization_courses(organization_id):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('ORGANIZATIONS_APP', False):
if not organizations_enabled():
return []
from organizations import api as organizations_api
return organizations_api.get_organization_courses(organization_id)
......@@ -68,7 +82,14 @@ def get_course_organizations(course_id):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('ORGANIZATIONS_APP', False):
if not organizations_enabled():
return []
from organizations import api as organizations_api
return organizations_api.get_course_organizations(course_id)
def organizations_enabled():
"""
Returns boolean indication if organizations app is enabled on not.
"""
return settings.FEATURES.get('ORGANIZATIONS_APP', False)
......@@ -23,6 +23,7 @@ class OrganizationsHelpersTestCase(ModuleStoreTestCase):
self.organization = {
'name': 'Test Organization',
'short_name': 'Orgx',
'description': 'Testing Organization Helpers Library',
}
......@@ -49,3 +50,25 @@ class OrganizationsHelpersTestCase(ModuleStoreTestCase):
def test_add_organization_course_returns_none_when_app_disabled(self):
response = organizations_helpers.add_organization_course(self.organization, self.course.id)
self.assertIsNone(response)
def test_get_organization_by_short_name_when_app_disabled(self):
"""
Tests get_organization_by_short_name api when app is disabled.
"""
response = organizations_helpers.get_organization_by_short_name(self.organization['short_name'])
self.assertIsNone(response)
@patch.dict('django.conf.settings.FEATURES', {'ORGANIZATIONS_APP': True})
def test_get_organization_by_short_name_when_app_enabled(self):
"""
Tests get_organization_by_short_name api when app is enabled.
"""
response = organizations_helpers.add_organization(organization_data=self.organization)
self.assertIsNotNone(response['id'])
response = organizations_helpers.get_organization_by_short_name(self.organization['short_name'])
self.assertIsNotNone(response['id'])
# fetch non existing org
response = organizations_helpers.get_organization_by_short_name('non_existing')
self.assertIsNone(response)
......@@ -85,6 +85,85 @@ class DashboardPage(PageObject):
"""
self.q(css='.wrapper-create-library .new-library-save').click()
@property
def new_course_button(self):
"""
Returns "New Course" button.
"""
return self.q(css='.new-course-button')
def is_new_course_form_visible(self):
"""
Is the new course form visible?
"""
return self.q(css='.wrapper-create-course').visible
def click_new_course_button(self):
"""
Click "New Course" button
"""
self.q(css='.new-course-button').first.click()
self.wait_for_ajax()
def fill_new_course_form(self, display_name, org, number, run):
"""
Fill out the form to create a new course.
"""
field = lambda fn: self.q(css='.wrapper-create-course #new-course-{}'.format(fn))
field('name').fill(display_name)
field('org').fill(org)
field('number').fill(number)
field('run').fill(run)
def is_new_course_form_valid(self):
"""
Returns `True` if new course form is valid otherwise `False`.
"""
return (
self.q(css='.wrapper-create-course .new-course-save:not(.is-disabled)').present and
not self.q(css='.wrapper-create-course .wrap-error.is-shown').present
)
def submit_new_course_form(self):
"""
Submit the new course form.
"""
self.q(css='.wrapper-create-course .new-course-save').first.click()
self.wait_for_ajax()
@property
def error_notification(self):
"""
Returns error notification element.
"""
return self.q(css='.wrapper-notification-error.is-shown')
@property
def error_notification_message(self):
"""
Returns text of error message.
"""
self.wait_for_element_visibility(
".wrapper-notification-error.is-shown .message", "Error message is visible"
)
return self.error_notification.results[0].find_element_by_css_selector('.message').text
@property
def course_org_field(self):
"""
Returns course organization input.
"""
return self.q(css='.wrapper-create-course #new-course-org')
def select_item_in_autocomplete_widget(self, item_text):
"""
Selects item in autocomplete where text of item matches item_text.
"""
self.wait_for_element_visibility(
".ui-autocomplete .ui-menu-item", "Autocomplete widget is visible"
)
self.q(css='.ui-autocomplete .ui-menu-item a').filter(lambda el: el.text == item_text)[0].click()
def list_courses(self):
"""
List all the courses found on the page's list of libraries.
......@@ -102,6 +181,15 @@ class DashboardPage(PageObject):
}
return self.q(css='.courses li.course-item').map(div2info).results
def has_course(self, org, number, run):
"""
Returns `True` if course for given org, number and run exists on the page otherwise `False`
"""
for course in self.list_courses():
if course['org'] == org and course['number'] == number and course['run'] == run:
return True
return False
def list_libraries(self):
"""
Click the tab to display the available libraries, and return detail of them.
......
......@@ -3,6 +3,7 @@ Test course discovery.
"""
import datetime
import json
import uuid
from bok_choy.web_app_test import WebAppTest
from ..helpers import remove_file
......@@ -34,29 +35,14 @@ class CourseDiscoveryTest(WebAppTest):
super(CourseDiscoveryTest, self).setUp()
self.page = CourseDiscoveryPage(self.browser)
for i in range(10):
org = self.unique_id
number = unicode(i)
for i in range(12):
org = 'test_org'
number = "{}{}".format(str(i), str(uuid.uuid4().get_hex().upper()[0:6]))
run = "test_run"
name = "test course"
name = "test course" if i < 10 else "grass is always greener"
settings = {'enrollment_start': datetime.datetime(1970, 1, 1).isoformat()}
CourseFixture(org, number, run, name, settings=settings).install()
for i in range(2):
org = self.unique_id
number = unicode(i)
run = "test_run"
name = "grass is always greener"
CourseFixture(
org,
number,
run,
name,
settings={
'enrollment_start': datetime.datetime(1970, 1, 1).isoformat()
}
).install()
def _auto_auth(self, username, email, staff):
"""
Logout and login with given credentials.
......
"""
Acceptance tests for course creation.
"""
import uuid
from bok_choy.web_app_test import WebAppTest
from ...pages.studio.auto_auth import AutoAuthPage
from ...pages.studio.index import DashboardPage
from ...pages.studio.overview import CourseOutlinePage
class CreateCourseTest(WebAppTest):
"""
Test that we can create a new course the studio home page.
"""
def setUp(self):
"""
Load the helper for the home page (dashboard page)
"""
super(CreateCourseTest, self).setUp()
self.auth_page = AutoAuthPage(self.browser, staff=True)
self.dashboard_page = DashboardPage(self.browser)
self.course_name = "New Course Name"
self.course_org = "orgX"
self.course_number = str(uuid.uuid4().get_hex().upper()[0:6])
self.course_run = "2015_T2"
def test_create_course_with_non_existing_org(self):
"""
Scenario: Ensure that the course creation with non existing org display proper error message.
Given I have filled course creation form with a non existing and all required fields
When I click 'Create' button
Form validation should pass
Then I see the error message explaining reason for failure to create course
"""
self.auth_page.visit()
self.dashboard_page.visit()
self.assertFalse(self.dashboard_page.has_course(
org='testOrg', number=self.course_number, run=self.course_run
))
self.assertTrue(self.dashboard_page.new_course_button.present)
self.dashboard_page.click_new_course_button()
self.assertTrue(self.dashboard_page.is_new_course_form_visible())
self.dashboard_page.fill_new_course_form(
self.course_name, 'testOrg', self.course_number, self.course_run
)
self.assertTrue(self.dashboard_page.is_new_course_form_valid())
self.dashboard_page.submit_new_course_form()
self.assertTrue(self.dashboard_page.error_notification.present)
self.assertIn(
u'Organization you selected does not exist in the system', self.dashboard_page.error_notification_message
)
def test_create_course_with_existing_org(self):
"""
Scenario: Ensure that the course creation with an existing org should be successful.
Given I have filled course creation form with an existing org and all required fields
When I click 'Create' button
Form validation should pass
Then I see the course listing page with newly created course
"""
self.auth_page.visit()
self.dashboard_page.visit()
self.assertFalse(self.dashboard_page.has_course(
org=self.course_org, number=self.course_number, run=self.course_run
))
self.assertTrue(self.dashboard_page.new_course_button.present)
self.dashboard_page.click_new_course_button()
self.assertTrue(self.dashboard_page.is_new_course_form_visible())
self.dashboard_page.fill_new_course_form(
self.course_name, self.course_org, self.course_number, self.course_run
)
self.assertTrue(self.dashboard_page.is_new_course_form_valid())
self.dashboard_page.submit_new_course_form()
# Successful creation of course takes user to course outline page
course_outline_page = CourseOutlinePage(
self.browser,
self.course_org,
self.course_number,
self.course_run
)
course_outline_page.visit()
course_outline_page.wait_for_page()
# Go back to dashboard and verify newly created course exists there
self.dashboard_page.visit()
self.assertTrue(self.dashboard_page.has_course(
org=self.course_org, number=self.course_number, run=self.course_run
))
def test_create_course_with_existing_org_via_autocomplete(self):
"""
Scenario: Ensure that the course creation with an existing org should be successful.
Given I have filled course creation form with an existing org and all required fields
And I selected `Course Organization` input via autocomplete
When I click 'Create' button
Form validation should pass
Then I see the course listing page with newly created course
"""
self.auth_page.visit()
self.dashboard_page.visit()
new_org = 'orgX2'
self.assertFalse(self.dashboard_page.has_course(
org=new_org, number=self.course_number, run=self.course_run
))
self.assertTrue(self.dashboard_page.new_course_button.present)
self.dashboard_page.click_new_course_button()
self.assertTrue(self.dashboard_page.is_new_course_form_visible())
self.dashboard_page.fill_new_course_form(
self.course_name, '', self.course_number, self.course_run
)
self.dashboard_page.course_org_field.fill('org')
self.dashboard_page.select_item_in_autocomplete_widget(new_org)
self.assertTrue(self.dashboard_page.is_new_course_form_valid())
self.dashboard_page.submit_new_course_form()
# Successful creation of course takes user to course outline page
course_outline_page = CourseOutlinePage(
self.browser,
new_org,
self.course_number,
self.course_run
)
course_outline_page.visit()
course_outline_page.wait_for_page()
# Go back to dashboard and verify newly created course exists there
self.dashboard_page.visit()
self.assertTrue(self.dashboard_page.has_course(
org=new_org, number=self.course_number, run=self.course_run
))
[
{
"pk": 99,
"model": "organizations.organization",
"fields": {
"name": "Demo org 1",
"short_name": "orgX",
"description": "Description of organization 1",
"logo": "org1_logo.png",
"active": 1
}
},
{
"pk": 100,
"model": "organizations.organization",
"fields": {
"name": "Demo org 2",
"short_name": "orgX2",
"description": "Description of organization 2",
"logo": "org2_logo.png",
"active": 1
}
},
{
"pk": 101,
"model": "organizations.organization",
"fields": {
"name": "Demo org 3",
"short_name": "orgX3",
"description": "Description of organization 3",
"logo": "org3_logo.png",
"active": 1
}
},
{
"pk": 102,
"model": "organizations.organization",
"fields": {
"name": "Demo org 4",
"short_name": "test_org",
"description": "Description of organization 4",
"logo": "org4_logo.png",
"active": 1
}
}
]
......@@ -93,7 +93,7 @@ git+https://github.com/edx/xblock-utils.git@v1.0.0#egg=xblock-utils==v1.0.0
-e git+https://github.com/edx-solutions/xblock-google-drive.git@138e6fa0bf3a2013e904a085b9fed77dab7f3f21#egg=xblock-google-drive
-e git+https://github.com/edx/edx-reverification-block.git@0.0.5#egg=edx-reverification-block==0.0.5
-e git+https://github.com/edx/edx-user-state-client.git@30c0ad4b9f57f8d48d6943eb585ec8a9205f4469#egg=edx-user-state-client
git+https://github.com/edx/edx-organizations.git@release-2015-11-25#egg=edx-organizations==0.1.9
git+https://github.com/edx/edx-organizations.git@release-2015-12-08#egg=edx-organizations==0.2.0
git+https://github.com/edx/edx-proctoring.git@0.11.6#egg=edx-proctoring==0.11.6
git+https://github.com/edx/xblock-lti-consumer.git@v1.0.0#egg=xblock-lti-consumer==v1.0.0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment