Skip to content

Commit

Permalink
feat(AU-2283): Create transcript bulk delete endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Rodra committed Nov 18, 2024
1 parent 9215f04 commit c399085
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 1 deletion.
34 changes: 34 additions & 0 deletions edxval/tests/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,13 @@
status="test",
)

VIDEO_DICT_SIMPSONS = dict(
client_video_id="TheSimpsons",
duration=100.00,
edx_video_id="simpson-id",
status="test",
)

TRANSCRIPT_DATA = {
"overwatch": """
1
Expand Down Expand Up @@ -452,3 +459,30 @@
preferred_languages=['ar', 'en'],
video_source_language='en',
)

VIDEO_TRANSCRIPT_SIMPSON_ES = dict(
video_id='simpson-id',
language_code='es',
transcript='edxval/tests/data/The_Flash.srt',
provider=TranscriptProviderType.CIELO24,
file_format=TranscriptFormat.SRT,
file_data=TRANSCRIPT_DATA['flash']
)

VIDEO_TRANSCRIPT_SIMPSON_KO = dict(
video_id='simpson-id',
language_code='ko',
transcript='edxval/tests/data/The_Flash.srt',
provider=TranscriptProviderType.CIELO24,
file_format=TranscriptFormat.SRT,
file_data=TRANSCRIPT_DATA['flash']
)

VIDEO_TRANSCRIPT_SIMPSON_RU = dict(
video_id='simpson-id',
language_code='ru',
transcript='edxval/tests/data/The_Flash.srt',
provider=TranscriptProviderType.CIELO24,
file_format=TranscriptFormat.SRT,
file_data=TRANSCRIPT_DATA['flash']
)
120 changes: 120 additions & 0 deletions edxval/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1152,3 +1152,123 @@ def test_successful_response(self):
mock_video_ids.assert_called_once_with(course_id)

self.assertEqual(response.status_code, status.HTTP_200_OK)


@ddt
class VideoTranscriptBulkDeleteTest(APIAuthTestCase):
"""
Tests for transcript bulk deletion handler.
"""
def setUp(self):
"""
Tests setup.
"""
self.url = reverse('bulk-delete-video-transcript')
self.video_1 = Video.objects.create(**constants.VIDEO_DICT_SIMPSONS)
self.transcript_data_es = constants.VIDEO_TRANSCRIPT_SIMPSON_ES
self.transcript_data_ko = constants.VIDEO_TRANSCRIPT_SIMPSON_KO
self.transcript_data_ru = constants.VIDEO_TRANSCRIPT_SIMPSON_RU
super().setUp()

@data(
(
{
'simpson-id': ['es', 'ko', 'ru']
},
('Language "ko" is not available for video "simpson-id".\n'
'Language "ru" is not available for video "simpson-id".')
),
)
@unpack
def test_transcript_bulk_delete_handler_wrong_payload_missing_transcript_for_video(
self,
request_payload,
expected_error_message
):
"""
Tests the transcript upload handler when the required attributes are missing.
"""
VideoTranscript.objects.create(
video=self.video_1,
language_code=self.transcript_data_es['language_code'],
file_format=self.transcript_data_es['file_format'],
provider=self.transcript_data_es['provider'],
)
response = self.client.post(self.url, data=json.dumps(request_payload), content_type='application/json')
self.assertEqual(response.status_code, 400)
self.assertEqual(json.loads(response.content.decode('utf-8'))['message'], expected_error_message)

@data(
(
{
'simpson-id': ['ko'],
'flintstone-id': ['ru'],
},
('Language "ko" is not available for video "simpson-id".\n'
'Language "ru" is not available for video "flintstone-id".')
),
)
@unpack
def test_transcript_bulk_delete_handler_wrong_payload_missing_transcript_for_videos(
self,
request_payload,
expected_error_message,
):
"""
Tests the transcript upload handler when the required attributes are missing.
"""
response = self.client.post(self.url, data=json.dumps(request_payload), content_type='application/json')
self.assertEqual(response.status_code, 400)
self.assertEqual(json.loads(response.content.decode('utf-8'))['message'], expected_error_message)

@data(
(
{
'simpson-id': 'ru'
},
'Value for video "simpson-id" needs to be a list of language codes.'
),
)
@unpack
def test_transcript_bulk_delete_handler_wrong_payload_not_a_list(self, request_payload, expected_error_message):
"""
Tests the transcript upload handler when the required attributes are missing.
"""
response = self.client.post(self.url, data=json.dumps(request_payload), content_type='application/json')
self.assertEqual(response.status_code, 400)
self.assertEqual(json.loads(response.content.decode('utf-8'))['message'], expected_error_message)

@data(
(
{
'simpson-id': ['es', 'ko', 'ru'],
},
'3 transcripts were successfully deleted.'
),
)
@unpack
def test_transcript_bulk_delete_handler_success(self, request_payload, expected_message):
"""
Tests the transcript upload handler when payload is accurate and deletion works.
"""
VideoTranscript.objects.create(
video=self.video_1,
language_code=self.transcript_data_es['language_code'],
file_format=self.transcript_data_es['file_format'],
provider=self.transcript_data_es['provider'],
)
VideoTranscript.objects.create(
video=self.video_1,
language_code=self.transcript_data_ko['language_code'],
file_format=self.transcript_data_ko['file_format'],
provider=self.transcript_data_ko['provider'],
)
VideoTranscript.objects.create(
video=self.video_1,
language_code=self.transcript_data_ru['language_code'],
file_format=self.transcript_data_ru['file_format'],
provider=self.transcript_data_ru['provider'],
)
response = self.client.post(self.url, data=json.dumps(request_payload), content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertEqual(json.loads(response.content.decode('utf-8'))['message'], expected_message)
3 changes: 3 additions & 0 deletions edxval/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
path('videos/courses/<str:course_id>/video-ids', views.CourseVideoIDsView.as_view(),
name='course-video-ids'
),
path('videos/video-transcripts/bulk-delete/', views.VideoTranscriptBulkDelete.as_view(),
name='bulk-delete-video-transcript'
),
]

if getattr(settings, 'PROVIDER_STATES_SETUP_VIEW_URL', None):
Expand Down
69 changes: 68 additions & 1 deletion edxval/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from edxval.api import create_or_update_video_transcript, get_transcript_details_for_course, get_video_ids_for_course
from edxval.api import (
create_or_update_video_transcript,
delete_video_transcript,
get_available_transcript_languages,
get_transcript_details_for_course,
get_video_ids_for_course,
)
from edxval.models import (
LIST_MAX_ITEMS,
CourseVideo,
Expand Down Expand Up @@ -410,3 +416,64 @@ def put(self, request):
EncodedVideo.objects.create(video=video, profile=profile, **encode_data)

return Response(status=status.HTTP_200_OK)


class VideoTranscriptBulkDelete(APIView):
"""
View to bulk delete video transcripts
"""
authentication_classes = (JwtAuthentication, SessionAuthentication)

# noinspection PyMethodMayBeStatic
def post(self, request):
"""
View to delete a set of transcript files.
Arguments:
request: A WSGI request object
The request body should be a JSON object.
The JSON object should be a dictionary:
* Each key in the dictionary should be a video_id (a string representing the ID of the video).
* Each value in the dictionary should be a list of language_codes (a string representing the
language code of the transcript to be deleted).
Example:
{
"video_id_1": ["language_code_1", "language_code_2"],
"video_id_2": ["language_code_3", "language_code_4"]
}
Returns
- A 400 if any of the validation fails
- A 200 if all transcripts delete jobs are triggered successfully
"""
data = request.data
missing_transcripts = []
for video_id, language_codes in data.items():
if not isinstance(language_codes, list):
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={'message': f'Value for video "{video_id}" needs to be a list of language codes.'}
)
for language_code in language_codes:
if language_code not in get_available_transcript_languages(video_id=video_id):
missing_transcripts.append(f'Language "{language_code}" is not available for video "{video_id}".')

if missing_transcripts:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={'message': '\n'.join(missing_transcripts)}
)

deleted = 0

for video_id, language_codes in data.items():
for language_code in language_codes:
delete_video_transcript(video_id=video_id, language_code=language_code)
deleted += 1

return Response(
status=status.HTTP_200_OK,
data={'message': f'{deleted} transcripts were successfully deleted.'}
)

0 comments on commit c399085

Please sign in to comment.