Team Assignments Dashboard (#24019)
* Add team assignments to frontend * Limit team assignments to the given teamset * Remove deprecated django render_to_response * Move team assignments panel behind feature flag
This commit is contained in:
@@ -365,3 +365,20 @@ def anonymous_user_ids_for_team(user, team):
|
||||
anonymous_id_for_user(user=team_member, course_id=team.course_id, save=False)
|
||||
for team_member in team.users.all()
|
||||
])
|
||||
|
||||
|
||||
def get_assignments_for_team(user, team):
|
||||
""" Get openassessment XBlocks configured for the current teamset """
|
||||
# Confirm access
|
||||
if not has_specific_team_access(user, team):
|
||||
raise Exception("User {user} is not permitted to access team info for {team}".format(
|
||||
user=user.username,
|
||||
team=team.team_id
|
||||
))
|
||||
|
||||
# Limit to team-enabled ORAs for the matching teamset in the course
|
||||
return modulestore().get_items(
|
||||
team.course_id,
|
||||
qualifiers={'category': 'openassessment'},
|
||||
settings={'teams_enabled': True, 'selected_teamset_id': team.topic_id}
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ from .views import (
|
||||
MembershipBulkManagementView,
|
||||
MembershipDetailView,
|
||||
MembershipListView,
|
||||
TeamsAssignmentsView,
|
||||
TeamsDetailView,
|
||||
TeamsListView,
|
||||
TopicDetailView,
|
||||
@@ -32,6 +33,13 @@ urlpatterns = [
|
||||
TeamsDetailView.as_view(),
|
||||
name="teams_detail"
|
||||
),
|
||||
url(
|
||||
r'^v0/teams/{team_id_pattern}/assignments$'.format(
|
||||
team_id_pattern=TEAM_ID_PATTERN,
|
||||
),
|
||||
TeamsAssignmentsView.as_view(),
|
||||
name="teams_assignments_list"
|
||||
),
|
||||
url(
|
||||
r'^v0/topics/$',
|
||||
TopicListView.as_view(),
|
||||
|
||||
@@ -48,7 +48,7 @@ define([
|
||||
el: $('.profile-view'),
|
||||
teamEvents: TeamSpecHelpers.teamEvents,
|
||||
courseID: TeamSpecHelpers.testCourseID,
|
||||
context: TeamSpecHelpers.testContext,
|
||||
context: options.context || TeamSpecHelpers.testContext,
|
||||
model: teamModel,
|
||||
topic: isInstructorManagedTopic ?
|
||||
TeamSpecHelpers.createMockInstructorManagedTopic() :
|
||||
@@ -72,6 +72,23 @@ define([
|
||||
)
|
||||
);
|
||||
AjaxHelpers.respondWithJson(requests, TeamSpecHelpers.createMockDiscussionResponse());
|
||||
|
||||
// Assignments are feature-flagged
|
||||
if (profileView.context.teamsAssignmentsUrl) {
|
||||
AjaxHelpers.expectRequest(
|
||||
requests,
|
||||
'GET',
|
||||
interpolate( // eslint-disable-line no-undef
|
||||
'/api/team/v0/teams/%(teamId)s/assignments',
|
||||
{
|
||||
teamId: teamModel.id
|
||||
},
|
||||
true
|
||||
)
|
||||
);
|
||||
AjaxHelpers.respondWithJson(requests, TeamSpecHelpers.createMockTeamAssignments(options.assignments));
|
||||
}
|
||||
|
||||
return profileView;
|
||||
};
|
||||
|
||||
@@ -101,6 +118,76 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
describe('TeamAssignmentsView', function() {
|
||||
it('can render itself', function() {
|
||||
// Given a member of a team with team assignments
|
||||
var mockAssignments = TeamSpecHelpers.createMockTeamAssignments(),
|
||||
options = {
|
||||
membership: DEFAULT_MEMBERSHIP
|
||||
},
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
// When they go to the team profile view
|
||||
var view = createTeamProfileView(requests, options);
|
||||
|
||||
// The Assignments section renders with their assignments
|
||||
expect(view.$('.team-assignment').length).toEqual(mockAssignments.length);
|
||||
});
|
||||
|
||||
it('displays a message when no assignments are found', function() {
|
||||
// Given a member viewing a team with no assignments
|
||||
var mockAssignments = [],
|
||||
options = {
|
||||
assignments: mockAssignments,
|
||||
membership: DEFAULT_MEMBERSHIP
|
||||
},
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
// When they view the team
|
||||
var view = createTeamProfileView(requests, options);
|
||||
|
||||
// There should be filler text that says there are no assignments
|
||||
expect(view.$('#assignments').text()).toEqual('No assignments for team');
|
||||
expect(view.$('.team-assignment').length).toEqual(0);
|
||||
});
|
||||
|
||||
it('does not show at all for someone who is not on the team or staff', function() {
|
||||
// Given a user who is not on a team viewing a team with assignments
|
||||
var mockAssignments = TeamSpecHelpers.createMockTeamAssignments(),
|
||||
options = {
|
||||
assignments: mockAssignments
|
||||
},
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
// When the user goes to the team detail page
|
||||
var view = createTeamProfileView(requests, options);
|
||||
|
||||
// Then then assignments view does not appear on the page
|
||||
expect(view.$('.team-assignments').length).toBe(0);
|
||||
});
|
||||
|
||||
it('does not show at all when the feature flag is turned off', function() {
|
||||
// Given the team submissions feature is turned off
|
||||
// (teamAsssignmentsUrl isn't surfaced to user)
|
||||
var mockAssignments = TeamSpecHelpers.createMockTeamAssignments(),
|
||||
options = {
|
||||
assignments: mockAssignments,
|
||||
membership: DEFAULT_MEMBERSHIP,
|
||||
context: Object.assign({}, TeamSpecHelpers.testContext)
|
||||
},
|
||||
requests = AjaxHelpers.requests(this),
|
||||
view;
|
||||
|
||||
delete options.context.teamsAssignmentsUrl;
|
||||
|
||||
// When the user goes to the team detail page
|
||||
view = createTeamProfileView(requests, options);
|
||||
|
||||
// Then then assignments view does not appear on the page
|
||||
expect(view.$('.team-assignments').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DiscussionsView', function() {
|
||||
it('can render itself', function() {
|
||||
var requests = AjaxHelpers.requests(this),
|
||||
|
||||
@@ -73,6 +73,18 @@ define([
|
||||
);
|
||||
};
|
||||
|
||||
var createMockTeamAssignments = function(assignments, options) {
|
||||
if (_.isUndefined(assignments)) {
|
||||
assignments = [ // eslint-disable-line no-param-reassign
|
||||
{
|
||||
display_name: 'Send me',
|
||||
location: 'your location'
|
||||
}
|
||||
];
|
||||
}
|
||||
return _.extend(assignments, options);
|
||||
};
|
||||
|
||||
var createMockTeamMembershipsData = function(startIndex, stopIndex) {
|
||||
var teams = createMockTeamData(startIndex, stopIndex);
|
||||
return _.map(_.range(startIndex, stopIndex + 1), function(i) {
|
||||
@@ -282,6 +294,7 @@ define([
|
||||
countries: testCountries,
|
||||
topicUrl: '/api/team/v0/topics/topic_id,' + testCourseID,
|
||||
teamsUrl: '/api/team/v0/teams/',
|
||||
teamsAssignmentsUrl: '/api/team/v0/teams/team_id/assignments',
|
||||
teamsDetailUrl: '/api/team/v0/teams/team_id',
|
||||
teamMembershipsUrl: '/api/team/v0/team_memberships/',
|
||||
teamMembershipDetailUrl: '/api/team/v0/team_membership/team_id,' + testUser,
|
||||
@@ -327,6 +340,7 @@ define([
|
||||
createMockTeamData: createMockTeamData,
|
||||
createMockTeamsResponse: createMockTeamsResponse,
|
||||
createMockTeams: createMockTeams,
|
||||
createMockTeamAssignments: createMockTeamAssignments,
|
||||
createMockUserInfo: createMockUserInfo,
|
||||
createMockContext: createMockContext,
|
||||
createMockTopic: createMockTopic,
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
'common/js/components/utils/view_utils',
|
||||
'teams/js/views/team_utils',
|
||||
'text!teams/templates/team-profile.underscore',
|
||||
'text!teams/templates/team-member.underscore'
|
||||
'text!teams/templates/team-member.underscore',
|
||||
'text!teams/templates/team-assignment.underscore'
|
||||
],
|
||||
function(Backbone, _, gettext, HtmlUtils, TeamDiscussionView, ViewUtils, TeamUtils,
|
||||
teamTemplate, teamMemberTemplate) {
|
||||
teamTemplate, teamMemberTemplate, teamAssignmentTemplate) {
|
||||
var TeamProfileView = Backbone.View.extend({
|
||||
|
||||
errorMessage: gettext('An error occurred. Try again.'),
|
||||
@@ -44,6 +45,10 @@
|
||||
isInstructorManagedTopic = TeamUtils.isInstructorManagedTopic(this.topic.attributes.type),
|
||||
maxTeamSize = this.topic.getMaxTeamSize(this.context.courseMaxTeamSize);
|
||||
|
||||
// Assignments URL isn't provided if team assignments shouldn't be shown
|
||||
// so we can treat it like a toggle
|
||||
var showAssignments = !!this.context.teamsAssignmentsUrl;
|
||||
|
||||
var showLeaveLink = isMember && (isAdminOrStaff || !isInstructorManagedTopic);
|
||||
|
||||
HtmlUtils.setHtml(
|
||||
@@ -56,7 +61,9 @@
|
||||
language: this.languages[this.model.get('language')],
|
||||
membershipText: TeamUtils.teamCapacityText(memberships.length, maxTeamSize),
|
||||
isMember: isMember,
|
||||
isAdminOrStaff: isAdminOrStaff,
|
||||
showLeaveLink: showLeaveLink,
|
||||
showAssignments: showAssignments,
|
||||
hasCapacity: maxTeamSize && (memberships.length < maxTeamSize),
|
||||
hasMembers: memberships.length >= 1
|
||||
})
|
||||
@@ -67,12 +74,48 @@
|
||||
});
|
||||
this.discussionView.render();
|
||||
|
||||
if (showAssignments) {
|
||||
this.getTeamAssignments();
|
||||
}
|
||||
|
||||
this.renderTeamMembers();
|
||||
|
||||
this.setFocusToHeaderFunc();
|
||||
return this;
|
||||
},
|
||||
|
||||
getTeamAssignments: function() {
|
||||
var view = this;
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: view.context.teamsAssignmentsUrl.replace('team_id', view.model.get('id'))
|
||||
}).done(function(data) {
|
||||
view.renderTeamAssignments(data);
|
||||
}).fail(function(data) {
|
||||
TeamUtils.parseAndShowMessage(data, view.errorMessage);
|
||||
});
|
||||
},
|
||||
|
||||
renderTeamAssignments: function(assignments) {
|
||||
var view = this;
|
||||
|
||||
if (!assignments || !assignments.length) {
|
||||
view.$('#assignments').text(gettext('No assignments for team'));
|
||||
return;
|
||||
}
|
||||
|
||||
_.each(assignments, function(assignment) {
|
||||
HtmlUtils.append(
|
||||
view.$('#assignments'),
|
||||
HtmlUtils.template(teamAssignmentTemplate)({
|
||||
displayName: assignment.display_name,
|
||||
linkLocation: assignment.location
|
||||
})
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
renderTeamMembers: function() {
|
||||
var view = this;
|
||||
_.each(this.model.get('membership'), function(membership) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<li class="team-assignment">
|
||||
<a href="<%- linkLocation %>"><%- displayName %></a>
|
||||
</li>
|
||||
@@ -1,5 +1,12 @@
|
||||
<div class="team-profile">
|
||||
<div class="page-content-main">
|
||||
<% if (showAssignments && (isMember || isAdminOrStaff)) { %>
|
||||
<div class="team-assignments">
|
||||
<h3><%- gettext("Team Assignments") %></h3>
|
||||
<ul id="assignments"></ul>
|
||||
</div>
|
||||
<hr></hr>
|
||||
<% } %>
|
||||
<div class="discussion-module" data-course-id="<%- courseID %>" data-discussion-id="<%- discussionTopicID %>"
|
||||
data-read-only="<%- readOnly %>"
|
||||
data-user-create-comment="<%- !readOnly %>"
|
||||
|
||||
@@ -50,6 +50,9 @@ from openedx.core.djangolib.js_utils import (
|
||||
topicUrl: '${topic_url | n, js_escaped_string}',
|
||||
topicsUrl: '${topics_url | n, js_escaped_string}',
|
||||
teamsUrl: '${teams_url | n, js_escaped_string}',
|
||||
% if teams_assignments_url:
|
||||
teamsAssignmentsUrl: '${teams_assignments_url | n, js_escaped_string}',
|
||||
% endif
|
||||
teamsDetailUrl: '${teams_detail_url | n, js_escaped_string}',
|
||||
teamMembershipsUrl: '${team_memberships_url | n, js_escaped_string}',
|
||||
teamMembershipDetailUrl: '${team_membership_detail_url | n, js_escaped_string}',
|
||||
|
||||
@@ -34,7 +34,7 @@ from lms.djangoapps.program_enrollments.tests.factories import ProgramEnrollment
|
||||
from student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
|
||||
from util.testing import EventTestMixin
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
from ..models import CourseTeamMembership
|
||||
from ..search_indexes import CourseTeam, CourseTeamIndexer, course_team_post_save_callback
|
||||
@@ -648,6 +648,14 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def get_team_assignments(self, team_id, expected_status=200, **kwargs):
|
||||
""" Get the open response assessments assigned to a team """
|
||||
return self.make_call(
|
||||
reverse('teams_assignments_list', args=[team_id]),
|
||||
expected_status,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def get_topics_list(self, expected_status=200, data=None, **kwargs):
|
||||
"""Gets the list of topics, passing data as query params. Verifies expected_status."""
|
||||
return self.make_call(reverse('topics_list'), expected_status, 'get', data, **kwargs)
|
||||
@@ -1570,6 +1578,100 @@ class TestUpdateTeamAPI(EventTestMixin, TeamAPITestCase):
|
||||
self.assertEqual(team['name'], 'foo')
|
||||
|
||||
|
||||
@patch.dict(settings.FEATURES, {'ENABLE_ORA_TEAM_SUBMISSIONS': True})
|
||||
@ddt.ddt
|
||||
class TestTeamAssignmentsView(TeamAPITestCase):
|
||||
""" Tests for the TeamAssignmentsView """
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
""" Create an openassessment block for testing """
|
||||
super().setUpClass()
|
||||
|
||||
course = cls.test_course_1
|
||||
teamset_id = cls.solar_team.topic_id
|
||||
other_teamset_id = cls.wind_team.topic_id
|
||||
|
||||
section = ItemFactory.create(
|
||||
parent=course,
|
||||
category='chapter',
|
||||
display_name='Test Section'
|
||||
)
|
||||
subsection = ItemFactory.create(
|
||||
parent=section,
|
||||
category="sequential"
|
||||
)
|
||||
unit_1 = ItemFactory.create(
|
||||
parent=subsection,
|
||||
category="vertical"
|
||||
)
|
||||
open_assessment = ItemFactory.create(
|
||||
parent=unit_1,
|
||||
category="openassessment",
|
||||
teams_enabled=True,
|
||||
selected_teamset_id=teamset_id
|
||||
)
|
||||
unit_2 = ItemFactory.create(
|
||||
parent=subsection,
|
||||
category="vertical"
|
||||
)
|
||||
off_team_open_assessment = ItemFactory.create( # pylint: disable=unused-variable
|
||||
parent=unit_2,
|
||||
category="openassessment",
|
||||
teams_enabled=True,
|
||||
selected_teamset_id=other_teamset_id
|
||||
)
|
||||
|
||||
cls.team_assignments = [open_assessment]
|
||||
|
||||
@ddt.unpack
|
||||
@ddt.data(
|
||||
(None, 401),
|
||||
('student_inactive', 401),
|
||||
('student_unenrolled', 403),
|
||||
('student_on_team_2_private_set_1', 404),
|
||||
('student_enrolled', 200),
|
||||
('staff', 200),
|
||||
('course_staff', 200),
|
||||
('community_ta', 200),
|
||||
)
|
||||
def test_get_assignments(self, user, expected_status):
|
||||
# Given a course with team-enabled open responses
|
||||
team_id = self.solar_team.team_id
|
||||
|
||||
# When I get the assignments for a team
|
||||
assignments = self.get_team_assignments(team_id, expected_status, user=user)
|
||||
|
||||
if expected_status == 200:
|
||||
# I successful, I get back the assignments for a team
|
||||
self.assertEqual(len(assignments), len(self.team_assignments))
|
||||
|
||||
# ... with the right data structure
|
||||
for assignment in assignments:
|
||||
self.assertIn('display_name', assignment.keys())
|
||||
self.assertIn('location', assignment.keys())
|
||||
|
||||
def test_get_assignments_bad_team(self):
|
||||
# Given a bad team is supplied
|
||||
user = 'student_enrolled'
|
||||
team_id = 'bogus-team'
|
||||
|
||||
# When I run the query, I get back a 404 error
|
||||
expected_status = 404
|
||||
self.get_team_assignments(team_id, expected_status, user=user)
|
||||
|
||||
@patch.dict(settings.FEATURES, {'ENABLE_ORA_TEAM_SUBMISSIONS': False})
|
||||
def test_get_assignments_feature_not_enabled(self):
|
||||
# Given the team submissions feature is not enabled
|
||||
user = 'student_enrolled'
|
||||
team_id = self.solar_team.team_id
|
||||
|
||||
# When I try to get assignments
|
||||
# Then I get back a 503 error
|
||||
expected_status = 503
|
||||
self.get_team_assignments(team_id, expected_status, user=user)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestListTopicsAPI(TeamAPITestCase):
|
||||
"""Test cases for the topic listing endpoint."""
|
||||
|
||||
@@ -13,7 +13,7 @@ from django.core.exceptions import PermissionDenied
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from django.http import Http404, HttpResponse, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, render_to_response
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_noop
|
||||
@@ -51,6 +51,7 @@ from .api import (
|
||||
add_team_count,
|
||||
can_user_modify_team,
|
||||
can_user_create_team_in_topic,
|
||||
get_assignments_for_team,
|
||||
has_course_staff_privileges,
|
||||
has_specific_team_access,
|
||||
has_specific_teamset_access,
|
||||
@@ -68,6 +69,7 @@ from .serializers import (
|
||||
TopicSerializer
|
||||
)
|
||||
from .utils import emit_team_event
|
||||
from .waffle import are_team_submissions_enabled
|
||||
|
||||
TEAM_MEMBERSHIPS_PER_PAGE = 5
|
||||
TOPICS_PER_PAGE = 12
|
||||
@@ -210,7 +212,12 @@ class TeamsDashboardView(GenericAPIView):
|
||||
"disable_courseware_js": True,
|
||||
"teams_base_url": reverse('teams_dashboard', request=request, kwargs={'course_id': course_id}),
|
||||
}
|
||||
return render_to_response("teams/teams.html", context)
|
||||
|
||||
# Assignments are feature-flagged
|
||||
if are_team_submissions_enabled(course_key):
|
||||
context["teams_assignments_url"] = reverse('teams_assignments_list', args=['team_id'])
|
||||
|
||||
return render(request, "teams/teams.html", context)
|
||||
|
||||
def _serialize_and_paginate(self, pagination_cls, queryset, request, serializer_cls, serializer_ctx):
|
||||
"""
|
||||
@@ -823,6 +830,87 @@ class TeamsDetailView(ExpandableFieldViewMixin, RetrievePatchAPIView):
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class TeamsAssignmentsView(GenericAPIView):
|
||||
"""
|
||||
**Use Cases**
|
||||
|
||||
Get a team's assignments
|
||||
|
||||
**Example Requests**:
|
||||
|
||||
GET /api/team/v0/teams/{team_id}/assignments
|
||||
|
||||
**Response Values for GET**
|
||||
|
||||
If the user is logged in, the response is an array of the following data strcuture:
|
||||
|
||||
* display_name: The name of the assignment to display (currently the Unit title)
|
||||
|
||||
* location: The jump link to a specific assignments
|
||||
|
||||
For all text fields, clients rendering the values should take care
|
||||
to HTML escape them to avoid script injections, as the data is
|
||||
stored exactly as specified. The intention is that plain text is
|
||||
supported, not HTML.
|
||||
|
||||
If team assignments are not enabled for course, a 503 is returned.
|
||||
|
||||
If the user is not logged in, a 401 error is returned.
|
||||
|
||||
If the user is unenrolled or does not have API access, a 403 error is returned.
|
||||
|
||||
If the supplied course/team is bad or the user is not permitted to
|
||||
search in a protected team, a 404 error is returned as if the team does not exist.
|
||||
|
||||
"""
|
||||
authentication_classes = (BearerAuthentication, SessionAuthentication)
|
||||
permission_classes = (
|
||||
permissions.IsAuthenticated,
|
||||
IsEnrolledOrIsStaff,
|
||||
HasSpecificTeamAccess,
|
||||
IsStaffOrPrivilegedOrReadOnly,
|
||||
)
|
||||
|
||||
def get(self, request, team_id):
|
||||
"""GET v0/teams/{team_id_pattern}/assignments"""
|
||||
course_team = get_object_or_404(CourseTeam, team_id=team_id)
|
||||
user = request.user
|
||||
course_id = course_team.course_id
|
||||
|
||||
if not are_team_submissions_enabled(course_id):
|
||||
return Response(status=status.HTTP_503_SERVICE_UNAVAILABLE)
|
||||
|
||||
if not has_team_api_access(request.user, course_id):
|
||||
return Response(status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
if not has_specific_team_access(user, course_team):
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
teamset_ora_blocks = get_assignments_for_team(user, course_team)
|
||||
|
||||
# Serialize info for display
|
||||
assignments = [{
|
||||
'display_name': self._display_name_for_ora_block(block),
|
||||
'location': self._jump_location_for_block(course_id, block.location)
|
||||
} for block in teamset_ora_blocks]
|
||||
|
||||
return Response(assignments)
|
||||
|
||||
def _display_name_for_ora_block(self, block):
|
||||
""" Get the unit name where the ORA is located for better display naming """
|
||||
unit = modulestore().get_item(block.parent)
|
||||
section = modulestore().get_item(unit.parent)
|
||||
|
||||
return "{section}: {unit}".format(
|
||||
section=section.display_name,
|
||||
unit=unit.display_name
|
||||
)
|
||||
|
||||
def _jump_location_for_block(self, course_id, location):
|
||||
""" Get the URL for jumping to a designated XBlock in a course """
|
||||
return reverse('jump_to', kwargs={'course_id': str(course_id), 'location': str(location)})
|
||||
|
||||
|
||||
class TopicListView(GenericAPIView):
|
||||
"""
|
||||
**Use Cases**
|
||||
|
||||
26
lms/djangoapps/teams/waffle.py
Normal file
26
lms/djangoapps/teams/waffle.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Togglable settings for Teams behavior
|
||||
"""
|
||||
from django.conf import settings
|
||||
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
|
||||
|
||||
|
||||
# Course Waffle inherited from edx/edx-ora2
|
||||
WAFFLE_NAMESPACE = 'openresponseassessment'
|
||||
TEAM_SUBMISSIONS_FLAG = 'team_submissions'
|
||||
|
||||
# edx/edx-platform feature
|
||||
TEAM_SUBMISISONS_FEATURE = 'ENABLE_ORA_TEAM_SUBMISSIONS'
|
||||
|
||||
|
||||
def are_team_submissions_enabled(course_key):
|
||||
"""
|
||||
Checks to see if the CourseWaffleFlag or Django setting for team submissions is enabled
|
||||
"""
|
||||
if CourseWaffleFlag(WAFFLE_NAMESPACE, TEAM_SUBMISSIONS_FLAG).is_enabled(course_key):
|
||||
return True
|
||||
|
||||
if settings.FEATURES.get(TEAM_SUBMISISONS_FEATURE, False):
|
||||
return True
|
||||
|
||||
return False
|
||||
Reference in New Issue
Block a user