Cache team_size on CourseTeam model and remove is_active field.
TNL-3112 TNL-3154
This commit is contained in:
@@ -14,3 +14,8 @@ class NotEnrolledInCourseForTeam(TeamAPIRequestError):
|
||||
class AlreadyOnTeamInCourse(TeamAPIRequestError):
|
||||
"""User is already a member of another team in the same course."""
|
||||
pass
|
||||
|
||||
|
||||
class ImmutableMembershipFieldException(Exception):
|
||||
"""An attempt was made to change an immutable field on a CourseTeamMembership model"""
|
||||
pass
|
||||
|
||||
98
lms/djangoapps/teams/migrations/0006_add_team_size.py
Normal file
98
lms/djangoapps/teams/migrations/0006_add_team_size.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
from teams.models import CourseTeamMembership
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding field 'CourseTeam.team_size'
|
||||
db.add_column('teams_courseteam', 'team_size',
|
||||
self.gf('django.db.models.fields.IntegerField')(default=0, db_index=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding index on 'CourseTeam', fields ['last_activity_at']
|
||||
db.create_index('teams_courseteam', ['last_activity_at'])
|
||||
|
||||
if not db.dry_run:
|
||||
for team in orm.CourseTeam.objects.all():
|
||||
team.team_size = CourseTeamMembership.objects.filter(team=team).count()
|
||||
team.save()
|
||||
|
||||
def backwards(self, orm):
|
||||
# Removing index on 'CourseTeam', fields ['last_activity_at']
|
||||
db.delete_index('teams_courseteam', ['last_activity_at'])
|
||||
|
||||
# Deleting field 'CourseTeam.team_size'
|
||||
db.delete_column('teams_courseteam', 'team_size')
|
||||
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'teams.courseteam': {
|
||||
'Meta': {'object_name': 'CourseTeam'},
|
||||
'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
|
||||
'discussion_topic_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'language': ('student.models.LanguageField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'team_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
|
||||
'team_size': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
|
||||
'topic_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'db_index': 'True', 'related_name': "'teams'", 'symmetrical': 'False', 'through': "orm['teams.CourseTeamMembership']", 'to': "orm['auth.User']"})
|
||||
},
|
||||
'teams.courseteammembership': {
|
||||
'Meta': {'unique_together': "(('user', 'team'),)", 'object_name': 'CourseTeamMembership'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_activity_at': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'membership'", 'to': "orm['teams.CourseTeam']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['teams']
|
||||
@@ -0,0 +1,85 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Deleting field 'CourseTeam.is_active'
|
||||
db.delete_column('teams_courseteam', 'is_active')
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Adding field 'CourseTeam.is_active'
|
||||
db.add_column('teams_courseteam', 'is_active',
|
||||
self.gf('django.db.models.fields.BooleanField')(default=True),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'teams.courseteam': {
|
||||
'Meta': {'object_name': 'CourseTeam'},
|
||||
'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
|
||||
'discussion_topic_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'language': ('student.models.LanguageField', [], {'max_length': '16', 'blank': 'True'}),
|
||||
'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'team_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
|
||||
'team_size': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
|
||||
'topic_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'db_index': 'True', 'related_name': "'teams'", 'symmetrical': 'False', 'through': "orm['teams.CourseTeamMembership']", 'to': "orm['auth.User']"})
|
||||
},
|
||||
'teams.courseteammembership': {
|
||||
'Meta': {'unique_together': "(('user', 'team'),)", 'object_name': 'CourseTeamMembership'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_activity_at': ('django.db.models.fields.DateTimeField', [], {}),
|
||||
'team': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'membership'", 'to': "orm['teams.CourseTeam']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['teams']
|
||||
@@ -26,7 +26,7 @@ from django_comment_common.signals import (
|
||||
from xmodule_django.models import CourseKeyField
|
||||
from util.model_utils import slugify
|
||||
from student.models import LanguageField, CourseEnrollment
|
||||
from .errors import AlreadyOnTeamInCourse, NotEnrolledInCourseForTeam
|
||||
from .errors import AlreadyOnTeamInCourse, NotEnrolledInCourseForTeam, ImmutableMembershipFieldException
|
||||
from teams import TEAM_DISCUSSION_CONTEXT
|
||||
|
||||
|
||||
@@ -76,7 +76,6 @@ class CourseTeam(models.Model):
|
||||
team_id = models.CharField(max_length=255, unique=True)
|
||||
discussion_topic_id = models.CharField(max_length=255, unique=True)
|
||||
name = models.CharField(max_length=255, db_index=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
course_id = CourseKeyField(max_length=255, db_index=True)
|
||||
topic_id = models.CharField(max_length=255, db_index=True, blank=True)
|
||||
date_created = models.DateTimeField(auto_now_add=True)
|
||||
@@ -86,8 +85,9 @@ class CourseTeam(models.Model):
|
||||
blank=True,
|
||||
help_text=ugettext_lazy("Optional language the team uses as ISO 639-1 code."),
|
||||
)
|
||||
last_activity_at = models.DateTimeField()
|
||||
last_activity_at = models.DateTimeField(db_index=True) # indexed for ordering
|
||||
users = models.ManyToManyField(User, db_index=True, related_name='teams', through='CourseTeamMembership')
|
||||
team_size = models.IntegerField(default=0, db_index=True) # indexed for ordering
|
||||
|
||||
@classmethod
|
||||
def create(cls, name, course_id, description, topic_id=None, country=None, language=None):
|
||||
@@ -135,6 +135,11 @@ class CourseTeam(models.Model):
|
||||
team=self
|
||||
)
|
||||
|
||||
def reset_team_size(self):
|
||||
"""Reset team_size to reflect the current membership count."""
|
||||
self.team_size = CourseTeamMembership.objects.filter(team=self).count()
|
||||
self.save()
|
||||
|
||||
|
||||
class CourseTeamMembership(models.Model):
|
||||
"""This model represents the membership of a single user in a single team."""
|
||||
@@ -148,12 +153,40 @@ class CourseTeamMembership(models.Model):
|
||||
date_joined = models.DateTimeField(auto_now_add=True)
|
||||
last_activity_at = models.DateTimeField()
|
||||
|
||||
immutable_fields = ('user', 'team', 'date_joined')
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""Memberships are immutable, with the exception of last activity
|
||||
date.
|
||||
"""
|
||||
if name in self.immutable_fields:
|
||||
# Check the current value -- if it is None, then this
|
||||
# model is being created from the database and it's fine
|
||||
# to set the value. Otherwise, we're trying to overwrite
|
||||
# an immutable field.
|
||||
current_value = getattr(self, name, None)
|
||||
if current_value is not None:
|
||||
raise ImmutableMembershipFieldException
|
||||
super(CourseTeamMembership, self).__setattr__(name, value)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
""" Customize save method to set the last_activity_at if it does not currently exist. """
|
||||
"""Customize save method to set the last_activity_at if it does not
|
||||
currently exist. Also resets the team's size if this model is
|
||||
being created.
|
||||
"""
|
||||
should_reset_team_size = False
|
||||
if self.pk is None:
|
||||
should_reset_team_size = True
|
||||
if not self.last_activity_at:
|
||||
self.last_activity_at = datetime.utcnow().replace(tzinfo=pytz.utc)
|
||||
|
||||
super(CourseTeamMembership, self).save(*args, **kwargs)
|
||||
if should_reset_team_size:
|
||||
self.team.reset_team_size() # pylint: disable=no-member
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
"""Recompute the related team's team_size after deleting a membership"""
|
||||
super(CourseTeamMembership, self).delete(*args, **kwargs)
|
||||
self.team.reset_team_size() # pylint: disable=no-member
|
||||
|
||||
@classmethod
|
||||
def get_memberships(cls, username=None, course_ids=None, team_id=None):
|
||||
|
||||
@@ -51,7 +51,6 @@ class CourseTeamSerializer(serializers.ModelSerializer):
|
||||
"id",
|
||||
"discussion_topic_id",
|
||||
"name",
|
||||
"is_active",
|
||||
"course_id",
|
||||
"topic_id",
|
||||
"date_created",
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
defaults: {
|
||||
id: null,
|
||||
name: '',
|
||||
is_active: null,
|
||||
course_id: '',
|
||||
topic_id: '',
|
||||
date_created: '',
|
||||
|
||||
@@ -14,7 +14,6 @@ define([
|
||||
createTeamData = {
|
||||
id: null,
|
||||
name: "TeamName",
|
||||
is_active: null,
|
||||
course_id: "a/b/c",
|
||||
topic_id: "awesomeness",
|
||||
date_created: "",
|
||||
|
||||
@@ -32,7 +32,6 @@ define([
|
||||
id: "id " + i,
|
||||
language: testLanguages[i%4][0],
|
||||
country: testCountries[i%4][0],
|
||||
is_active: true,
|
||||
membership: [],
|
||||
last_activity_at: ''
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# pylint: disable=no-member
|
||||
"""Tests for the teams API at the HTTP request level."""
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime
|
||||
@@ -20,7 +21,7 @@ from django_comment_common.signals import (
|
||||
)
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from student.tests.factories import UserFactory
|
||||
from student.tests.factories import CourseEnrollmentFactory, UserFactory
|
||||
|
||||
from .factories import CourseTeamFactory, CourseTeamMembershipFactory
|
||||
from ..models import CourseTeam, CourseTeamMembership
|
||||
@@ -42,16 +43,18 @@ class TeamMembershipTest(SharedModuleStoreTestCase):
|
||||
|
||||
self.user1 = UserFactory.create(username='user1')
|
||||
self.user2 = UserFactory.create(username='user2')
|
||||
self.user3 = UserFactory.create(username='user3')
|
||||
|
||||
for user in (self.user1, self.user2, self.user3):
|
||||
CourseEnrollmentFactory.create(user=user, course_id=COURSE_KEY1)
|
||||
CourseEnrollmentFactory.create(user=self.user1, course_id=COURSE_KEY2)
|
||||
|
||||
self.team1 = CourseTeamFactory(course_id=COURSE_KEY1, team_id='team1')
|
||||
self.team2 = CourseTeamFactory(course_id=COURSE_KEY2, team_id='team2')
|
||||
|
||||
self.team_membership11 = CourseTeamMembership(user=self.user1, team=self.team1)
|
||||
self.team_membership11.save()
|
||||
self.team_membership12 = CourseTeamMembership(user=self.user2, team=self.team1)
|
||||
self.team_membership12.save()
|
||||
self.team_membership21 = CourseTeamMembership(user=self.user1, team=self.team2)
|
||||
self.team_membership21.save()
|
||||
self.team_membership11 = self.team1.add_user(self.user1)
|
||||
self.team_membership12 = self.team1.add_user(self.user2)
|
||||
self.team_membership21 = self.team2.add_user(self.user1)
|
||||
|
||||
def test_membership_last_activity_set(self):
|
||||
current_last_activity = self.team_membership11.last_activity_at
|
||||
@@ -64,6 +67,24 @@ class TeamMembershipTest(SharedModuleStoreTestCase):
|
||||
# already exist.
|
||||
self.assertEqual(self.team_membership11.last_activity_at, current_last_activity)
|
||||
|
||||
def test_team_size_delete_membership(self):
|
||||
"""Test that the team size field is correctly updated when deleting a
|
||||
team membership.
|
||||
"""
|
||||
self.assertEqual(self.team1.team_size, 2)
|
||||
self.team_membership11.delete()
|
||||
team = CourseTeam.objects.get(id=self.team1.id)
|
||||
self.assertEqual(team.team_size, 1)
|
||||
|
||||
def test_team_size_create_membership(self):
|
||||
"""Test that the team size field is correctly updated when creating a
|
||||
team membership.
|
||||
"""
|
||||
self.assertEqual(self.team1.team_size, 2)
|
||||
self.team1.add_user(self.user3)
|
||||
team = CourseTeam.objects.get(id=self.team1.id)
|
||||
self.assertEqual(team.team_size, 3)
|
||||
|
||||
@ddt.data(
|
||||
(None, None, None, 3),
|
||||
('user1', None, None, 2),
|
||||
|
||||
@@ -208,13 +208,8 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
|
||||
)
|
||||
self.test_team_2 = CourseTeamFactory.create(name='Wind Team', course_id=self.test_course_1.id)
|
||||
self.test_team_3 = CourseTeamFactory.create(name='Nuclear Team', course_id=self.test_course_1.id)
|
||||
self.test_team_4 = CourseTeamFactory.create(
|
||||
name='Coal Team',
|
||||
course_id=self.test_course_1.id,
|
||||
is_active=False
|
||||
)
|
||||
self.test_team_5 = CourseTeamFactory.create(name='Another Team', course_id=self.test_course_2.id)
|
||||
self.test_team_6 = CourseTeamFactory.create(
|
||||
self.test_team_4 = CourseTeamFactory.create(name='Another Team', course_id=self.test_course_2.id)
|
||||
self.test_team_5 = CourseTeamFactory.create(
|
||||
name='Public Profile Team',
|
||||
course_id=self.test_course_2.id,
|
||||
topic_id='topic_6'
|
||||
@@ -234,7 +229,6 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
|
||||
self.test_team_3,
|
||||
self.test_team_4,
|
||||
self.test_team_5,
|
||||
self.test_team_6,
|
||||
self.test_team_7,
|
||||
)}
|
||||
|
||||
@@ -245,8 +239,8 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
|
||||
|
||||
self.test_team_1.add_user(self.users['student_enrolled'])
|
||||
self.test_team_3.add_user(self.users['student_enrolled_both_courses_other_team'])
|
||||
self.test_team_5.add_user(self.users['student_enrolled_both_courses_other_team'])
|
||||
self.test_team_6.add_user(self.users['student_enrolled_public_profile'])
|
||||
self.test_team_4.add_user(self.users['student_enrolled_both_courses_other_team'])
|
||||
self.test_team_5.add_user(self.users['student_enrolled_public_profile'])
|
||||
|
||||
def build_membership_data_raw(self, username, team):
|
||||
"""Assembles a membership creation payload based on the raw values provided."""
|
||||
@@ -400,7 +394,7 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
|
||||
|
||||
def verify_expanded_team(self, team):
|
||||
"""Verifies that fields exist on the returned team json indicating that it is expanded."""
|
||||
for field in ['id', 'name', 'is_active', 'course_id', 'topic_id', 'date_created', 'description']:
|
||||
for field in ['id', 'name', 'course_id', 'topic_id', 'date_created', 'description']:
|
||||
self.assertIn(field, team)
|
||||
|
||||
|
||||
@@ -446,9 +440,6 @@ class TestListTeamsAPI(TeamAPITestCase):
|
||||
def test_filter_topic_id(self):
|
||||
self.verify_names({'course_id': self.test_course_1.id, 'topic_id': 'topic_0'}, 200, [u'Sólar team'])
|
||||
|
||||
def test_filter_include_inactive(self):
|
||||
self.verify_names({'include_inactive': True}, 200, ['Coal Team', 'Nuclear Team', u'Sólar team', 'Wind Team'])
|
||||
|
||||
@ddt.data(
|
||||
(None, 200, ['Nuclear Team', u'Sólar team', 'Wind Team']),
|
||||
('name', 200, ['Nuclear Team', u'Sólar team', 'Wind Team']),
|
||||
@@ -673,7 +664,6 @@ class TestCreateTeamAPI(TeamAPITestCase):
|
||||
'name': 'Fully specified team',
|
||||
'language': 'fr',
|
||||
'country': 'CA',
|
||||
'is_active': True,
|
||||
'topic_id': 'great-topic',
|
||||
'course_id': str(self.test_course_1.id),
|
||||
'description': 'Another fantastic team'
|
||||
@@ -723,7 +713,7 @@ class TestDetailTeamAPI(TeamAPITestCase):
|
||||
|
||||
def test_expand_public_user(self):
|
||||
result = self.get_team_detail(
|
||||
self.test_team_6.team_id,
|
||||
self.test_team_5.team_id,
|
||||
200,
|
||||
{'expand': 'user'},
|
||||
user='student_enrolled_public_profile'
|
||||
@@ -1025,7 +1015,7 @@ class TestListMembershipAPI(TeamAPITestCase):
|
||||
def test_expand_public_user(self):
|
||||
result = self.get_membership_list(
|
||||
200,
|
||||
{'team_id': self.test_team_6.team_id, 'expand': 'user'},
|
||||
{'team_id': self.test_team_5.team_id, 'expand': 'user'},
|
||||
user='student_enrolled_public_profile'
|
||||
)
|
||||
self.verify_expanded_public_user(result['results'][0]['user'])
|
||||
@@ -1113,7 +1103,7 @@ class TestCreateMembershipAPI(TeamAPITestCase):
|
||||
def test_over_max_team_size_in_course_2(self):
|
||||
response = self.post_create_membership(
|
||||
400,
|
||||
self.build_membership_data('student_enrolled_other_course_not_on_team', self.test_team_5),
|
||||
self.build_membership_data('student_enrolled_other_course_not_on_team', self.test_team_4),
|
||||
user='student_enrolled_other_course_not_on_team'
|
||||
)
|
||||
self.assertIn('full', json.loads(response.content)['developer_message'])
|
||||
@@ -1167,7 +1157,7 @@ class TestDetailMembershipAPI(TeamAPITestCase):
|
||||
|
||||
def test_expand_public_user(self):
|
||||
result = self.get_membership_detail(
|
||||
self.test_team_6.team_id,
|
||||
self.test_team_5.team_id,
|
||||
self.users['student_enrolled_public_profile'].username,
|
||||
200,
|
||||
{'expand': 'user'},
|
||||
|
||||
@@ -191,9 +191,6 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
|
||||
* page: Page number to retrieve.
|
||||
|
||||
* include_inactive: If true, inactive teams will be returned. The
|
||||
default is to not include inactive teams.
|
||||
|
||||
* expand: Comma separated list of types for which to return
|
||||
expanded representations. Supports "user" and "team".
|
||||
|
||||
@@ -220,10 +217,6 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
|
||||
* name: The name of the team.
|
||||
|
||||
* is_active: True if the team is currently active. If false, the
|
||||
team is considered "soft deleted" and will not be included by
|
||||
default in results.
|
||||
|
||||
* course_id: The identifier for the course this team belongs to.
|
||||
|
||||
* topic_id: Optionally specifies which topic the team is associated
|
||||
@@ -266,8 +259,8 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
|
||||
Any logged in user who has verified their email address can create
|
||||
a team. The format mirrors that of a GET for an individual team,
|
||||
but does not include the id, is_active, date_created, or membership
|
||||
fields. id is automatically computed based on name.
|
||||
but does not include the id, date_created, or membership fields.
|
||||
id is automatically computed based on name.
|
||||
|
||||
If the user is not logged in, a 401 error is returned.
|
||||
|
||||
@@ -292,9 +285,7 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
|
||||
def get(self, request):
|
||||
"""GET /api/team/v0/teams/"""
|
||||
result_filter = {
|
||||
'is_active': True
|
||||
}
|
||||
result_filter = {}
|
||||
|
||||
if 'course_id' in request.QUERY_PARAMS:
|
||||
course_id_string = request.QUERY_PARAMS['course_id']
|
||||
@@ -335,8 +326,6 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
)
|
||||
return Response(error, status=status.HTTP_400_BAD_REQUEST)
|
||||
result_filter.update({'topic_id': request.QUERY_PARAMS['topic_id']})
|
||||
if 'include_inactive' in request.QUERY_PARAMS and request.QUERY_PARAMS['include_inactive'].lower() == 'true':
|
||||
del result_filter['is_active']
|
||||
|
||||
if 'text_search' in request.QUERY_PARAMS and CourseTeamIndexer.search_is_enabled():
|
||||
search_engine = CourseTeamIndexer.engine()
|
||||
@@ -355,7 +344,6 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
self.get_paginate_by(),
|
||||
self.get_page()
|
||||
)
|
||||
|
||||
serializer = self.get_pagination_serializer(paginated_results)
|
||||
else:
|
||||
queryset = CourseTeam.objects.filter(**result_filter)
|
||||
@@ -364,10 +352,8 @@ class TeamsListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
# MySQL does case-insensitive order_by.
|
||||
queryset = queryset.order_by('name')
|
||||
elif order_by_input == 'open_slots':
|
||||
queryset = queryset.annotate(team_size=Count('users'))
|
||||
queryset = queryset.order_by('team_size', '-last_activity_at')
|
||||
elif order_by_input == 'last_activity_at':
|
||||
queryset = queryset.annotate(team_size=Count('users'))
|
||||
queryset = queryset.order_by('-last_activity_at', 'team_size')
|
||||
else:
|
||||
return Response({
|
||||
@@ -496,10 +482,6 @@ class TeamsDetailView(ExpandableFieldViewMixin, RetrievePatchAPIView):
|
||||
|
||||
* name: The name of the team.
|
||||
|
||||
* is_active: True if the team is currently active. If false, the team
|
||||
is considered "soft deleted" and will not be included by default in
|
||||
results.
|
||||
|
||||
* course_id: The identifier for the course this team belongs to.
|
||||
|
||||
* topic_id: Optionally specifies which topic the team is
|
||||
|
||||
Reference in New Issue
Block a user