Commit 6534503c by Diana Huang Committed by cahrens

Add teams tab

TNL-1939
parent 099be9e7
# -*- coding: utf-8 -*-
"""
Teams page.
"""
from .course_page import CoursePage
class TeamsPage(CoursePage):
"""
Teams page/tab.
"""
url_path = "teams"
def is_browser_on_page(self):
""" Checks if teams page is being viewed """
return self.q(css='body.view-teams').present
def get_body_text(self):
""" Returns the current dummy text. This will be changed once there is more content on the page. """
return self.q(css='.teams-text').text[0]
"""
Acceptance tests for the teams feature.
"""
from ..helpers import UniqueCourseTest
from ...pages.lms.teams import TeamsPage
from nose.plugins.attrib import attr
from ...fixtures.course import CourseFixture
from ...pages.lms.tab_nav import TabNavPage
from ...pages.lms.auto_auth import AutoAuthPage
from ...pages.lms.course_info import CourseInfoPage
@attr('shard_5')
class TeamsTabTest(UniqueCourseTest):
"""
Tests verifying when the Teams tab is present.
"""
def setUp(self):
super(TeamsTabTest, self).setUp()
self.tab_nav = TabNavPage(self.browser)
self.course_info_page = CourseInfoPage(self.browser, self.course_id)
self.teams_page = TeamsPage(self.browser, self.course_id)
self.test_topic = {u"name": u"a topic", u"description": u"test topic", u"id": 0}
def set_team_configuration(self, configuration, enroll_in_course=True, global_staff=False):
"""
Sets team configuration on the course and calls auto-auth on the user.
"""
#pylint: disable=attribute-defined-outside-init
self.course_fixture = CourseFixture(**self.course_info)
if configuration:
self.course_fixture.add_advanced_settings(
{u"teams_configuration": {u"value": configuration}}
)
self.course_fixture.install()
enroll_course_id = self.course_id if enroll_in_course else None
AutoAuthPage(self.browser, course_id=enroll_course_id, staff=global_staff).visit()
self.course_info_page.visit()
def verify_teams_present(self, present):
"""
Verifies whether or not the teams tab is present. If it should be present, also
checks the text on the page (to ensure view is working).
"""
if present:
self.assertIn("Teams", self.tab_nav.tab_names)
self.teams_page.visit()
self.assertEqual("This is the new Teams tab.", self.teams_page.get_body_text())
else:
self.assertNotIn("Teams", self.tab_nav.tab_names)
def test_teams_not_enabled(self):
"""
Scenario: teams tab should not be present if no team configuration is set
Given I am enrolled in a course without team configuration
When I view the course info page
Then I should not see the Teams tab
"""
self.set_team_configuration(None)
self.verify_teams_present(False)
def test_teams_not_enabled_no_topics(self):
"""
Scenario: teams tab should not be present if team configuration does not specify topics
Given I am enrolled in a course with no topics in the team configuration
When I view the course info page
Then I should not see the Teams tab
"""
self.set_team_configuration({u"max_team_size": 10, u"topics": []})
self.verify_teams_present(False)
def test_teams_not_enabled_not_enrolled(self):
"""
Scenario: teams tab should not be present if student is not enrolled in the course
Given there is a course with team configuration and topics
And I am not enrolled in that course, and am not global staff
When I view the course info page
Then I should not see the Teams tab
"""
self.set_team_configuration({u"max_team_size": 10, u"topics": [self.test_topic]}, enroll_in_course=False)
self.verify_teams_present(False)
def test_teams_enabled(self):
"""
Scenario: teams tab should be present if user is enrolled in the course and it has team configuration
Given I am enrolled in a course with team configuration and topics
When I view the course info page
Then I should see the Teams tab
And the correct content should be on the page
"""
self.set_team_configuration({u"max_team_size": 10, u"topics": [self.test_topic]})
self.verify_teams_present(True)
def test_teams_enabled_global_staff(self):
"""
Scenario: teams tab should be present if user is not enrolled in the course, but is global staff
Given there is a course with team configuration
And I am not enrolled in that course, but am global staff
When I view the course info page
Then I should see the Teams tab
And the correct content should be on the page
"""
self.set_team_configuration(
{u"max_team_size": 10, u"topics": [self.test_topic]}, enroll_in_course=False, global_staff=True
)
self.verify_teams_present(True)
"""
Definition of the course team feature.
"""
from django.utils.translation import ugettext as _
from courseware.tabs import EnrolledCourseViewType
from .views import is_feature_enabled
class TeamsCourseViewType(EnrolledCourseViewType):
"""
The representation of the course teams view type.
"""
name = "teams"
title = _("Teams")
view_name = "teams_dashboard"
@classmethod
def is_enabled(cls, course, user=None):
"""Returns true if the teams feature is enabled in the course.
Args:
course (CourseDescriptor): the course using the feature
user (User): the user interacting with the course
"""
if not super(TeamsCourseViewType, cls).is_enabled(course, user=user):
return False
return is_feature_enabled(course)
define(["jquery", "teams/js/teams_tab_factory"],
function($, TeamsTabFactory) {
'use strict';
describe("teams django app", function() {
var teamsTab;
beforeEach(function() {
setFixtures("<div class='team-tab-content'></div>");
teamsTab = new TeamsTabFactory();
});
it("can load templates", function() {
expect($("body").text()).toContain("This is the new Teams tab");
});
});
}
);
;(function (define) {
'use strict';
define(['jquery', 'teams/js/views/teams_tab'],
function ($, TeamsTabView) {
return function () {
var view = new TeamsTabView({
el: $('.team-tab-content')
});
view.render();
};
});
}).call(this, define || RequireJS.define);
;(function (define) {
'use strict';
define(['backbone', 'underscore', 'text!teams/templates/teams-tab.underscore'],
function (Backbone, _, teamsTabTemplate) {
var TeamTabView = Backbone.View.extend({
render: function() {
this.$el.html(_.template(teamsTabTemplate, {}));
}
});
return TeamTabView;
});
}).call(this, define || RequireJS.define);
<p class="teams-text">This is the new Teams tab.</p>
## mako
<%! from django.utils.translation import ugettext as _ %>
<%namespace name='static' file='/static_content.html'/>
<%inherit file="/main.html" />
<%block name="bodyclass">view-teams is-in-course course</%block>
<%block name="pagetitle">${_("Teams")}</%block>
<%block name="headextra">
<%static:css group='style-course'/>
</%block>
<%include file="/courseware/course_navigation.html" args="active_page='teams'" />
<div class="team-tab-content"></div>
<%block name="js_extra">
<script type="text/javascript">
(function (require) {
require(['teams/js/teams_tab_factory'], function (TeamsTabFactory) {
var pageView = new TeamsTabFactory({
});
});
}).call(this, require || RequireJS.require);
</script>
</%block>
"""
Tests for views.py
"""
from nose.plugins.attrib import attr
from student.tests.factories import (
CourseEnrollmentFactory,
UserFactory,
)
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from django.http import Http404
from django.core.urlresolvers import reverse
from rest_framework.test import APIClient
@attr('shard_1')
class TestDashboard(ModuleStoreTestCase):
test_password = "test"
def setUp(self):
"""
Set up tests
"""
super(TestDashboard, self).setUp()
self.course = CourseFactory.create(
teams_configuration={"max_team_size": 10, "topics": [{"name": "foo", "id": 0, "description": "test topic"}]}
)
# will be assigned to self.client by default
self.user = UserFactory.create(password=self.test_password)
self.teams_url = reverse('teams_dashboard', args=[self.course.id])
def test_anonymous(self):
""" Verifies that an anonymous client cannot access the team dashboard. """
anonymous_client = APIClient()
response = anonymous_client.get(self.teams_url)
self.assertEqual(404, response.status_code)
def test_not_enrolled_not_staff(self):
""" Verifies that a student who is not enrolled cannot access the team dashboard. """
response = self.client.get(self.teams_url)
self.assertEqual(404, response.status_code)
def test_not_enrolled_staff(self):
"""
Verifies that a user with global access who is not enrolled in the course can access the team dashboard.
"""
staff_user = UserFactory(is_staff=True, password=self.test_password)
staff_client = APIClient()
staff_client.login(username=staff_user.username, password=self.test_password)
response = staff_client.get(self.teams_url)
self.assertContains(response, "TeamsTabFactory", status_code=200)
def test_enrolled_not_staff(self):
"""
Verifies that a user without global access who is enrolled in the course can access the team dashboard.
"""
CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id)
self.client.login(username=self.user.username, password=self.test_password)
response = self.client.get(self.teams_url)
self.assertContains(response, "TeamsTabFactory", status_code=200)
def test_enrolled_teams_not_enabled(self):
"""
Verifies that a user without global access who is enrolled in the course cannot access the team dashboard
if the teams feature is not enabled.
"""
course = CourseFactory.create()
teams_url = reverse('teams_dashboard', args=[course.id])
CourseEnrollmentFactory.create(user=self.user, course_id=course.id)
self.client.login(username=self.user.username, password=self.test_password)
response = self.client.get(teams_url)
self.assertEqual(404, response.status_code)
def test_bad_course_id(self):
"""
Verifies expected behavior when course_id does not reference an existing course or is invalid.
"""
bad_org = "badorgxxx"
bad_team_url = self.teams_url.replace(self.course.id.org, bad_org)
response = self.client.get(bad_team_url)
self.assertEqual(404, response.status_code)
bad_team_url = bad_team_url.replace(bad_org, "invalid/course/id")
response = self.client.get(bad_team_url)
self.assertEqual(404, response.status_code)
"""
URLs for teams.
"""
from django.conf.urls import patterns, url
from teams.views import TeamsDashboardView
urlpatterns = patterns(
"teams.views",
url(r"^/$", TeamsDashboardView.as_view(), name="teams_dashboard"),
)
"""
View methods for the course team feature.
"""
from django.shortcuts import render_to_response
from opaque_keys.edx.keys import CourseKey
from courseware.courses import get_course_with_access, has_access
from django.http import Http404
from django.conf import settings
from django.views.generic.base import View
from student.models import CourseEnrollment
class TeamsDashboardView(View):
"""
View methods related to the teams dashboard.
"""
def get(self, request, course_id):
"""
Renders the teams dashboard, which is shown on the "Teams" tab.
Raises a 404 if the course specified by course_id does not exist, the
user is not registered for the course, or the teams feature is not enabled.
"""
course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, "load", course_key)
if not is_feature_enabled(course):
raise Http404
if not CourseEnrollment.is_enrolled(request.user, course.id) and \
not has_access(request.user, 'staff', course, course.id):
raise Http404
context = {"course": course}
return render_to_response("teams/teams.html", context)
def is_feature_enabled(course):
"""
Returns True if the teams feature is enabled.
"""
return settings.FEATURES.get('ENABLE_TEAMS', False) and course.teams_enabled
......@@ -1854,6 +1854,9 @@ INSTALLED_APPS = (
# Credit courses
'openedx.core.djangoapps.credit',
# Course teams
'teams',
)
######################### CSRF #########################################
......
......@@ -25,6 +25,7 @@
'jquery.url': 'xmodule_js/common_static/js/vendor/url.min',
'datepair': 'xmodule_js/common_static/js/vendor/timepicker/datepair',
'date': 'xmodule_js/common_static/js/vendor/date',
'text': 'xmodule_js/common_static/js/vendor/requirejs/text',
'underscore': 'xmodule_js/common_static/js/vendor/underscore-min',
'underscore.string': 'xmodule_js/common_static/js/vendor/underscore.string.min',
'backbone': 'xmodule_js/common_static/js/vendor/backbone-min',
......@@ -576,6 +577,7 @@
// TODO: why do these need 'lms/include' at the front but the CMS equivalent logic doesn't?
define([
// Run the LMS tests
'lms/include/teams/js/spec/teams_factory_spec.js',
'lms/include/js/spec/photocapture_spec.js',
'lms/include/js/spec/staff_debug_actions_spec.js',
'lms/include/js/spec/views/notification_spec.js',
......
......@@ -35,6 +35,7 @@ lib_paths:
- xmodule_js/common_static/js/vendor/jasmine-imagediff.js
- xmodule_js/common_static/js/vendor/require.js
- js/RequireJS-namespace-undefine.js
- xmodule_js/common_static/js/vendor/requirejs/text.js
- xmodule_js/common_static/js/vendor/jquery.min.js
- xmodule_js/common_static/js/vendor/jquery-ui.min.js
- xmodule_js/common_static/js/vendor/jquery.cookie.js
......@@ -64,10 +65,12 @@ lib_paths:
src_paths:
- js
- js/common_helpers
- teams/js
# Paths to spec (test) JavaScript files
spec_paths:
- js/spec
- teams/js/spec
# Paths to fixture files (optional)
# The fixture path will be set automatically when using jasmine-jquery.
......@@ -91,6 +94,7 @@ fixture_paths:
- js/fixtures/edxnotes
- js/fixtures/search
- templates/search
- teams/templates
- templates/discovery
requirejs:
......
......@@ -46,6 +46,7 @@
paths: {
"annotator_1.2.9": "js/vendor/edxnotes/annotator-full.min",
"date": "js/vendor/date",
"text": 'js/vendor/requirejs/text',
"backbone": "js/vendor/backbone-min",
"backbone-super": "js/vendor/backbone-super",
"underscore.string": "js/vendor/underscore.string.min",
......@@ -67,7 +68,7 @@
"osda": 'js/vendor/ova/OpenSeaDragonAnnotation',
"ova": 'js/vendor/ova/ova',
"catch": 'js/vendor/ova/catch/js/catch',
"handlebars": 'js/vendor/ova/catch/js/handlebars-1.1.2',
"handlebars": 'js/vendor/ova/catch/js/handlebars-1.1.2'
// end of files needed by OVA
},
shim: {
......@@ -89,7 +90,7 @@
exports: "Backbone"
},
"backbone-super": {
deps: ["backbone"],
deps: ["backbone"]
},
"logger": {
exports: "Logger"
......@@ -147,7 +148,7 @@
"grouping-annotator", "diacritic-annotator", "openseadragon", "jquery-Watch", "catch", "handlebars",
"URI"
]
},
}
// End of needed by OVA
}
};
......
../djangoapps/teams/static/teams
\ No newline at end of file
......@@ -428,6 +428,12 @@ if settings.COURSEWARE_ENABLED:
url(r'^api/branding/v1/', include('branding.api_urls')),
)
if settings.FEATURES["ENABLE_TEAMS"]:
# Teams endpoints
urlpatterns += (
url(r'^courses/{}/teams'.format(settings.COURSE_ID_PATTERN), include('teams.urls'), name="teams_endpoints"),
)
# allow course staff to change to student view of courseware
if settings.FEATURES.get('ENABLE_MASQUERADE'):
urlpatterns += (
......
......@@ -33,6 +33,7 @@ setup(
"progress = lms.djangoapps.courseware.tabs:ProgressCourseViewType",
"static_tab = lms.djangoapps.courseware.tabs:StaticCourseViewType",
"syllabus = lms.djangoapps.courseware.tabs:SyllabusCourseViewType",
"teams = lms.djangoapps.teams.plugins:TeamsCourseViewType",
"textbooks = lms.djangoapps.courseware.tabs:TextbookCourseViews",
"wiki = lms.djangoapps.course_wiki.tab:WikiCourseViewType",
......
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