Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(AU-2283): Create transcript delete endpoint #541

Merged
merged 9 commits into from
Dec 2, 2024
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
Rodra marked this conversation as resolved.
Show resolved Hide resolved
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):
Rodra marked this conversation as resolved.
Show resolved Hide resolved
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):
jansenk marked this conversation as resolved.
Show resolved Hide resolved
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.'}
)
Loading