Added view to begin and end speach.

This commit is contained in:
Norman Jäckel 2015-05-27 15:42:32 +02:00
parent 95be18b78e
commit 0853701cdd
3 changed files with 154 additions and 5 deletions

View File

@ -262,7 +262,7 @@ class ItemViewSet(ModelViewSet):
Speaker.objects.add(user, item) Speaker.objects.add(user, item)
except OpenSlidesError as e: except OpenSlidesError as e:
raise ValidationError({'detail': e}) raise ValidationError({'detail': e})
message = _('User was successfully added to the list of speakers.') message = _('User %s was successfully added to the list of speakers.') % user
else: else:
# request.method == 'DELETE' # request.method == 'DELETE'
@ -286,13 +286,63 @@ class ItemViewSet(ModelViewSet):
if not self.request.user.has_perm('agenda.can_manage'): if not self.request.user.has_perm('agenda.can_manage'):
self.permission_denied(request) self.permission_denied(request)
try: try:
speaker = Speaker.objects.get(pk=speaker_id) speaker = Speaker.objects.get(pk=int(speaker_id))
except Speaker.DoesNotExist: except (ValueError, Speaker.DoesNotExist):
raise ValidationError({'detail': _('Speaker does not exist.')}) raise ValidationError({'detail': _('Speaker does not exist.')})
# Delete the speaker. # Delete the speaker.
speaker.delete() speaker.delete()
message = _('Speaker was successfully removed from the list of speakers.') message = _('Speaker %s was successfully removed from the list of speakers.') % speaker
# Initiate response.
return Response({'detail': message})
@detail_route(methods=['PUT', 'DELETE'])
def speak(self, request, pk=None):
"""
Special view endpoint to begin and end speach of speakers. Send PUT
{'speaker': <speaker_id>} to begin speach. Omit data to begin speach of
the next speaker. Send DELETE to end speach of current speaker.
Checks also whether the requesting user can do this. He needs at
least the permissions 'agenda.can_see' (see
self.check_permission()). Also the permission 'agenda.can_manage'
is required.
"""
# Check permission.
if not self.request.user.has_perm('agenda.can_manage'):
self.permission_denied(request)
# Retrieve item.
item = self.get_object()
if request.method == 'PUT':
# Retrieve speaker_id
speaker_id = request.data.get('speaker')
if speaker_id is None:
speaker = item.get_next_speaker()
if speaker is None:
raise ValidationError({'detail': _('The list of speakers is empty.')})
else:
try:
speaker = Speaker.objects.get(pk=int(speaker_id))
except (ValueError, Speaker.DoesNotExist):
raise ValidationError({'detail': _('Speaker does not exist.')})
speaker.begin_speach()
message = _('User is now speaking.')
else:
# request.method == 'DELETE'
try:
# We assume that there aren't multiple entries because this
# is forbidden by the Model's begin_speach method. We assume that
# there is only one speaker instance or none.
current_speaker = Speaker.objects.filter(item=item, end_time=None).exclude(begin_time=None).get()
except Speaker.DoesNotExist:
raise ValidationError(
{'detail': _('There is no one speaking at the moment according to %(item)s.') % {'item': item}})
current_speaker.end_speach()
message = _('The speach is finished now.')
# Initiate response. # Initiate response.
return Response({'detail': message}) return Response({'detail': message})

View File

@ -104,6 +104,12 @@ class ManageSpeaker(TestCase):
{'speaker': '1'}) {'speaker': '1'})
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
def test_remove_someone_else_invalid_data(self):
response = self.client.delete(
reverse('item-manage-speaker', args=[self.item.pk]),
{'speaker': 'invalid'})
self.assertEqual(response.status_code, 400)
def test_remove_someone_else_non_admin(self): def test_remove_someone_else_non_admin(self):
admin = get_user_model().objects.get(username='admin') admin = get_user_model().objects.get(username='admin')
group_staff = admin.groups.get(name='Staff') group_staff = admin.groups.get(name='Staff')
@ -115,3 +121,59 @@ class ManageSpeaker(TestCase):
reverse('item-manage-speaker', args=[self.item.pk]), reverse('item-manage-speaker', args=[self.item.pk]),
{'speaker': speaker.pk}) {'speaker': speaker.pk})
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
class Speak(TestCase):
"""
Tests view to begin or end speach.
"""
def setUp(self):
self.client = APIClient()
self.client.login(username='admin', password='admin')
self.item = Item.objects.create(title='test_title_KooDueco3zaiGhiraiho')
self.user = get_user_model().objects.create_user(
username='test_user_Aigh4vohb3seecha4aa4',
password='test_password_eneupeeVo5deilixoo8j')
def test_begin_speach(self):
Speaker.objects.add(self.user, self.item)
speaker = Speaker.objects.add(get_user_model().objects.get(username='admin'), self.item)
self.assertTrue(Speaker.objects.get(pk=speaker.pk).begin_time is None)
response = self.client.put(
reverse('item-speak', args=[self.item.pk]),
{'speaker': speaker.pk})
self.assertEqual(response.status_code, 200)
self.assertFalse(Speaker.objects.get(pk=speaker.pk).begin_time is None)
def test_begin_speach_next_speaker(self):
speaker = Speaker.objects.add(self.user, self.item)
Speaker.objects.add(get_user_model().objects.get(username='admin'), self.item)
self.assertTrue(Speaker.objects.get(pk=speaker.pk).begin_time is None)
response = self.client.put(reverse('item-speak', args=[self.item.pk]))
self.assertEqual(response.status_code, 200)
self.assertFalse(Speaker.objects.get(pk=speaker.pk).begin_time is None)
def test_begin_speach_invalid_speaker_id(self):
response = self.client.put(
reverse('item-speak', args=[self.item.pk]),
{'speaker': '1'})
self.assertEqual(response.status_code, 400)
def test_begin_speach_invalid_data(self):
response = self.client.put(
reverse('item-speak', args=[self.item.pk]),
{'speaker': 'invalid'})
self.assertEqual(response.status_code, 400)
def test_end_speach(self):
speaker = Speaker.objects.add(get_user_model().objects.get(username='admin'), self.item)
speaker.begin_speach()
self.assertFalse(Speaker.objects.get(pk=speaker.pk).begin_time is None)
self.assertTrue(Speaker.objects.get(pk=speaker.pk).end_time is None)
response = self.client.delete(reverse('item-speak', args=[self.item.pk]))
self.assertEqual(response.status_code, 200)
self.assertFalse(Speaker.objects.get(pk=speaker.pk).end_time is None)
def test_end_speach_no_current_speaker(self):
response = self.client.delete(reverse('item-speak', args=[self.item.pk]))
self.assertEqual(response.status_code, 400)

View File

@ -50,5 +50,42 @@ class ItemViewSetManageSpeaker(TestCase):
self.request.user.has_perm.return_value = True self.request.user.has_perm.return_value = True
self.request.data = {'speaker': '1'} self.request.data = {'speaker': '1'}
self.view_instance.manage_speaker(self.request) self.view_instance.manage_speaker(self.request)
mock_speaker.objects.get.assert_called_with(pk='1') mock_speaker.objects.get.assert_called_with(pk=1)
mock_speaker.objects.get.return_value.delete.assert_called_with() mock_speaker.objects.get.return_value.delete.assert_called_with()
class ItemViewSetSpeak(TestCase):
"""
Tests views of ItemViewSet to begin and end speach.
"""
def setUp(self):
self.request = MagicMock()
self.view_instance = ItemViewSet()
self.view_instance.request = self.request
self.view_instance.get_object = get_object_mock = MagicMock()
get_object_mock.return_value = self.mock_item = MagicMock()
def test_begin_speach(self):
self.request.method = 'PUT'
self.request.user.has_perm.return_value = True
self.request.data = {}
self.mock_item.get_next_speaker.return_value = mock_next_speaker = MagicMock()
self.view_instance.speak(self.request)
mock_next_speaker.begin_speach.assert_called_with()
@patch('openslides.agenda.views.Speaker')
def test_begin_speach_specific_speaker(self, mock_speaker):
self.request.method = 'PUT'
self.request.user.has_perm.return_value = True
self.request.data = {'speaker': '1'}
mock_speaker.objects.get.return_value = mock_next_speaker = MagicMock()
self.view_instance.speak(self.request)
mock_next_speaker.begin_speach.assert_called_with()
@patch('openslides.agenda.views.Speaker')
def test_end_speach(self, mock_speaker):
self.request.method = 'DELETE'
self.request.user.has_perm.return_value = True
mock_speaker.objects.filter.return_value.exclude.return_value.get.return_value = mock_speaker = MagicMock()
self.view_instance.speak(self.request)
mock_speaker.end_speach.assert_called_with()