Skip to content

Commit 564ac66

Browse files
feat: Add transcript editor toggle and update related serializers and tests (#267)
1 parent 521952b commit 564ac66

4 files changed

Lines changed: 90 additions & 1 deletion

File tree

cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class CourseWaffleFlagsSerializer(serializers.Serializer):
3535
enable_unit_expanded_view = serializers.SerializerMethodField()
3636
enable_outline_component_creation = serializers.SerializerMethodField()
3737
enable_audio_description = serializers.SerializerMethodField()
38+
enable_transcript_editor = serializers.SerializerMethodField()
3839

3940
def get_course_key(self):
4041
"""
@@ -200,3 +201,10 @@ def get_enable_audio_description(self, obj):
200201
"""
201202
course_key = self.get_course_key()
202203
return toggles.audio_description_enabled(course_key)
204+
205+
def get_enable_transcript_editor(self, obj):
206+
"""
207+
Method to get the contentstore.enable_transcript_editor waffle flag.
208+
"""
209+
course_key = self.get_course_key()
210+
return toggles.transcript_editor_enabled(course_key)

cms/djangoapps/contentstore/rest_api/v1/views/tests/test_course_waffle_flags.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class CourseWaffleFlagsViewTest(CourseTestCase):
4242
"enable_unit_expanded_view": False,
4343
"enable_outline_component_creation": False,
4444
"enable_audio_description": False,
45+
"enable_transcript_editor": False,
4546
}
4647

4748
def setUp(self):
@@ -84,3 +85,55 @@ def test_audio_description_upload_flag_enabled(self):
8485
url = reverse("cms.djangoapps.contentstore:v1:course_waffle_flags")
8586
response = self.client.get(url)
8687
assert response.data["enable_audio_description"] is True
88+
89+
def test_enable_transcript_editor_flag_default_is_false(self):
90+
"""
91+
The contentstore.enable_transcript_editor flag should default to False when not
92+
overridden, both globally and for a specific course.
93+
"""
94+
global_url = reverse("cms.djangoapps.contentstore:v1:course_waffle_flags")
95+
course_url = reverse(
96+
"cms.djangoapps.contentstore:v1:course_waffle_flags",
97+
kwargs={"course_id": self.course.id},
98+
)
99+
for url in (global_url, course_url):
100+
response = self.client.get(url)
101+
assert response.data["enable_transcript_editor"] is False
102+
103+
@override_waffle_flag(toggles.ENABLE_TRANSCRIPT_EDITOR, active=True)
104+
def test_enable_transcript_editor_flag_enabled_globally(self):
105+
"""
106+
When the contentstore.enable_transcript_editor flag is active globally, the
107+
serializer should return True for both the global endpoint and any
108+
course-specific endpoint.
109+
"""
110+
global_url = reverse("cms.djangoapps.contentstore:v1:course_waffle_flags")
111+
course_url = reverse(
112+
"cms.djangoapps.contentstore:v1:course_waffle_flags",
113+
kwargs={"course_id": self.course.id},
114+
)
115+
for url in (global_url, course_url):
116+
response = self.client.get(url)
117+
assert response.data["enable_transcript_editor"] is True
118+
119+
def test_enable_transcript_editor_flag_enabled_per_course(self):
120+
"""
121+
When the contentstore.enable_transcript_editor flag is enabled via a
122+
WaffleFlagCourseOverrideModel entry for a specific course, the
123+
course-scoped endpoint should return True while the global endpoint
124+
should remain False.
125+
"""
126+
WaffleFlagCourseOverrideModel.objects.create(
127+
waffle_flag=toggles.ENABLE_TRANSCRIPT_EDITOR.name,
128+
course_id=self.course.id,
129+
enabled=True,
130+
)
131+
global_url = reverse("cms.djangoapps.contentstore:v1:course_waffle_flags")
132+
course_url = reverse(
133+
"cms.djangoapps.contentstore:v1:course_waffle_flags",
134+
kwargs={"course_id": self.course.id},
135+
)
136+
global_response = self.client.get(global_url)
137+
course_response = self.client.get(course_url)
138+
assert global_response.data["enable_transcript_editor"] is False
139+
assert course_response.data["enable_transcript_editor"] is True

cms/djangoapps/contentstore/toggles.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,31 @@ def audio_description_enabled(course_key):
9090
return ENABLE_AUDIO_DESCRIPTION.is_enabled(course_key)
9191

9292

93+
# .. toggle_name: contentstore.enable_transcript_editor
94+
# .. toggle_implementation: CourseWaffleFlag
95+
# .. toggle_default: False
96+
# .. toggle_description: Enables the in-platform transcript editor in Studio's
97+
# videos page. When enabled, course staff can open an editor modal from the
98+
# transcript row action menu and edit transcript cue text directly in the
99+
# browser; edits are saved via PATCH to a new v1 transcript endpoint. When
100+
# disabled, the "Edit transcript" menu item is hidden and the API endpoint
101+
# returns 404. Existing upload, download, and delete flows are unaffected.
102+
# .. toggle_use_cases: open_edx
103+
# .. toggle_creation_date: 2026-05-05
104+
# .. toggle_tickets: TNL2-608
105+
ENABLE_TRANSCRIPT_EDITOR = CourseWaffleFlag(
106+
'contentstore.enable_transcript_editor',
107+
__name__,
108+
)
109+
110+
111+
def transcript_editor_enabled(course_key):
112+
"""
113+
Return True if the in-platform transcript editor is enabled for the course.
114+
"""
115+
return ENABLE_TRANSCRIPT_EDITOR.is_enabled(course_key)
116+
117+
93118
# .. toggle_name: legacy_studio.exam_settings
94119
# .. toggle_implementation: WaffleFlag
95120
# .. toggle_default: False

cms/djangoapps/contentstore/transcript_storage_handlers.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ def upload_transcript(request):
181181
new_language_code = request.POST['new_language_code']
182182
transcript_file = request.FILES['file']
183183
try:
184+
# Determine whether this upload replaces an existing transcript
185+
# (return 200) or creates a new one (return 201).
186+
is_replace = new_language_code in get_available_transcript_languages(video_id=edx_video_id)
184187
# Convert SRT transcript into an SJSON format
185188
# and upload it to S3.
186189
sjson_subs = Transcript.convert(
@@ -198,7 +201,7 @@ def upload_transcript(request):
198201
},
199202
file_data=ContentFile(sjson_subs),
200203
)
201-
response = JsonResponse(status=201)
204+
response = JsonResponse(status=200 if is_replace else 201)
202205
except (TranscriptsGenerationException, UnicodeDecodeError):
203206
LOGGER.error("Unable to update transcript on edX video %s for language %s", edx_video_id, new_language_code)
204207
response = JsonResponse(

0 commit comments

Comments
 (0)