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: add patch endpoint to update transcript providers #562

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion edxval/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
init
"""

__version__ = '2.9.0'
__version__ = '2.10.0'
23 changes: 23 additions & 0 deletions edxval/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,29 @@ def create_or_update_video_transcript(video_id, language_code, metadata, file_da
return video_transcript.url()


def update_transcript_provider(video_id, language_code, provider):
"""
Update transcript provider for an existing transcript.

Arguments:
video_id: id identifying the video to which the transcript is associated.
language_code: language code of a video transcript.
provider: transcript provider
"""
video_transcript = VideoTranscript.get_or_none(video_id, language_code)

if provider and provider not in list(dict(TranscriptProviderType.TRANSCRIPT_MODEL_CHOICES).keys()):
raise InvalidTranscriptProvider(f'{provider} transcript provider is not supported')

if video_transcript:
video_transcript.provider = provider
video_transcript.save()
return video_transcript
else:
logger.info('Transcript does not exist for video id "%s" and language code "%s"', video_id, language_code)
return video_transcript


def delete_video_transcript(video_id, language_code):
"""
Delete transcript for an existing video.
Expand Down
42 changes: 42 additions & 0 deletions edxval/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3047,6 +3047,48 @@ def test_get_available_transcript_languages(self):
transcript_languages = api.get_available_transcript_languages(video_id='super-soaker')
self.assertEqual(transcript_languages, ['en', 'fr'])

def test_update_transcript_provider(self):
"""
Verify that `update_transcript_provider` works as expected.
"""
video_id = 'super-soaker'
language_code = 'en'
query_filter = {
'video__edx_video_id': video_id,
'language_code': language_code
}

api.update_transcript_provider(
video_id=video_id,
language_code=language_code,
provider=TranscriptProviderType.EDX_AI_TRANSLATIONS,
)

transcript = VideoTranscript.objects.get(**query_filter)
self.assertEqual(transcript.provider, TranscriptProviderType.EDX_AI_TRANSLATIONS)

def test_update_transcript_provider_exception(self):
video_id = 'super-soaker'
language_code = 'en'

with self.assertRaises(InvalidTranscriptProvider):
api.update_transcript_provider(
video_id=video_id,
language_code=language_code,
provider="Wrong Provider",
)

def test_update_transcript_provider_exception_no_transcript(self):
video_id = 'not-a-video-in-there'
language_code = 'en'

attempt = api.update_transcript_provider(
video_id=video_id,
language_code=language_code,
provider=TranscriptProviderType.EDX_AI_TRANSLATIONS,
)
assert attempt is None

@patch('edxval.api.logger')
def test_delete_video_transcript(self, mock_logger):
"""
Expand Down
75 changes: 75 additions & 0 deletions edxval/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,81 @@ def test_error_responses(self, post_data, message):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data['message'], message)

@data(
{'video_id': 'super-soaker', 'language_code': 'en', 'provider': TranscriptProviderType.EDX_AI_TRANSLATIONS},
{'video_id': 'super-soaker', 'language_code': 'fr', 'provider': TranscriptProviderType.EDX_AI_TRANSLATIONS},
)
@unpack
def test_patch_transcript_success(self, video_id, language_code, provider):
"""
Test successful PATCH request to update the provider.
"""
VideoTranscript.objects.create(
video=self.video,
language_code=language_code,
file_format=TranscriptFormat.SRT,
provider=TranscriptProviderType.CUSTOM
)

# Action: Send the PATCH request
url = reverse('video-transcripts')
patch_data = {'video_id': video_id, 'language_code': language_code, 'provider': provider}
response = self.client.patch(url, patch_data, format='json')

# Assertions
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Verify the transcript was updated in the database
updated_transcript = VideoTranscript.objects.get(video=self.video, language_code=language_code)
self.assertEqual(updated_transcript.provider, provider)

@data(
{'video_id': '', 'language_code': 'en', 'provider': ''},
{'video_id': 'super-soaker', 'language_code': '', 'provider': 'provider_a'},
)
@unpack
def test_patch_transcript_missing_params(self, video_id, language_code, provider):
"""
Test PATCH request with missing parameters.
"""
url = reverse('video-transcripts')
patch_data = {'video_id': video_id, 'language_code': language_code, 'provider': provider}
response = self.client.patch(url, patch_data, format='json')

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data['message'],
"The params video_id, language_code, and provider are required."
)

def test_patch_transcript_invalid_provider(self):
"""
Test PATCH request with an invalid provider.
"""
VideoTranscript.objects.create(
video=self.video,
language_code="ar",
file_format=TranscriptFormat.SRT,
provider=TranscriptProviderType.CUSTOM,
)

url = reverse('video-transcripts')
patch_data = {'video_id': 'super-soaker', 'language_code': 'ar', 'provider': 'invalid_provider'}
response = self.client.patch(url, patch_data, format='json')

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data['message'], 'Invalid transcript provider.')

def test_patch_transcript_not_found(self):
"""
Test PATCH request when the transcript doesn't exist. Should return 204.
"""

url = reverse('video-transcripts')
patch_data = {'video_id': 'nonexistent_video', 'language_code': 'en', 'provider': TranscriptProviderType.CUSTOM}
response = self.client.patch(url, patch_data, format='json')

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


@ddt
class VideoStatusViewTest(APIAuthTestCase):
Expand Down
39 changes: 39 additions & 0 deletions edxval/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
delete_video_transcript,
get_transcript_details_for_course,
get_video_ids_for_course,
update_transcript_provider,
)
from edxval.exceptions import InvalidTranscriptProvider
from edxval.models import (
LIST_MAX_ITEMS,
CourseVideo,
Expand Down Expand Up @@ -217,6 +219,43 @@
status=status.HTTP_204_NO_CONTENT,
)

def patch(self, request):
"""
Partially update a video transcript, only supporting updating the `provider` field.
"""
video_id = request.data.get('video_id')
language_code = request.data.get('language_code')
provider = request.data.get('provider')

if not video_id or not language_code or not provider:
return Response(
{"message": "The params video_id, language_code, and provider are required."},
status=status.HTTP_400_BAD_REQUEST
)

try:
updated_transcript = update_transcript_provider(
video_id=video_id,
language_code=language_code,
provider=provider,
)
if updated_transcript:
return Response(status=status.HTTP_200_OK)

except InvalidTranscriptProvider:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={'message': 'Invalid transcript provider.'}
)

except Exception as e: # pylint: disable=broad-exception-caught
return Response(

Check warning on line 252 in edxval/views.py

View check run for this annotation

Codecov / codecov/patch

edxval/views.py#L251-L252

Added lines #L251 - L252 were not covered by tests
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
data={'message': str(e)}
)

return Response(status=status.HTTP_204_NO_CONTENT)


class CourseTranscriptsDetailView(APIView):
"""
Expand Down
Loading