|
1 | 1 | """ API Views for course advanced settings """ |
2 | 2 |
|
| 3 | +import logging |
3 | 4 | from django import forms |
4 | 5 | import edx_api_doc_tools as apidocs |
5 | 6 | from opaque_keys.edx.keys import CourseKey |
|
18 | 19 | from ..serializers import CourseAdvancedSettingsSerializer |
19 | 20 | from ....views.course import update_course_advanced_settings |
20 | 21 |
|
| 22 | +log = logging.getLogger(__name__) |
| 23 | + |
21 | 24 |
|
22 | 25 | @view_auth_classes(is_authenticated=True) |
23 | 26 | class AdvancedCourseSettingsView(DeveloperErrorViewMixin, APIView): |
@@ -190,26 +193,114 @@ def patch(self, request: Request, course_id: str): |
190 | 193 | if not has_studio_write_access(request.user, course_key): |
191 | 194 | self.permission_denied(request) |
192 | 195 |
|
193 | | - course_app_settings_map = CourseAppsPluginManager.get_course_app_settings_mapping() |
194 | | - for setting in course_app_settings_map: |
195 | | - if setting_to_update := request.data.get(setting): |
196 | | - course_app_enabled = setting_to_update.get("value", None) |
197 | | - if course_app_enabled is not None: |
198 | | - try: |
199 | | - set_course_app_status( |
200 | | - course_key=course_key, |
201 | | - app_id=course_app_settings_map[setting], |
202 | | - enabled=course_app_enabled, |
203 | | - request=request, |
204 | | - ) |
205 | | - # enabling/disabling the course app setting also updates |
206 | | - # the advanced settings, so we remove it from the request data |
207 | | - request.data.pop(setting) |
208 | | - except ValidationError: |
209 | | - # Failed to update the course app status, |
210 | | - # we ignore and let the normal flow handle updates |
211 | | - pass |
| 196 | + # Process course app settings that have corresponding advanced settings |
| 197 | + self._process_course_app_settings(request, course_key) |
212 | 198 |
|
213 | 199 | course_block = modulestore().get_course(course_key) |
214 | 200 | updated_data = update_course_advanced_settings(course_block, request.data, request.user) |
215 | 201 | return Response(updated_data) |
| 202 | + |
| 203 | + def _process_course_app_settings(self, request: Request, course_key: CourseKey) -> None: |
| 204 | + """ |
| 205 | + Process course app settings that have corresponding advanced settings. |
| 206 | +
|
| 207 | + Updates course app status for settings that are managed by course apps, |
| 208 | + and removes them from the request data to avoid duplicate processing. |
| 209 | +
|
| 210 | + Args: |
| 211 | + request: The HTTP request containing settings to update |
| 212 | + course_key: The course key for the course being updated |
| 213 | + """ |
| 214 | + course_app_settings_map = CourseAppsPluginManager.get_course_app_settings_mapping(course_key) |
| 215 | + |
| 216 | + if not course_app_settings_map: |
| 217 | + log.debug("No course app settings mapping found for course: %s", course_key) |
| 218 | + return |
| 219 | + |
| 220 | + log.debug( |
| 221 | + "Processing course app settings: course_key=%s, user=%s, " |
| 222 | + "available_app_settings=%s, request_settings=%s", |
| 223 | + course_key, request.user.username, |
| 224 | + list(course_app_settings_map.keys()), list(request.data.keys()) |
| 225 | + ) |
| 226 | + |
| 227 | + settings_processed = 0 |
| 228 | + settings_failed = 0 |
| 229 | + |
| 230 | + for setting_name in course_app_settings_map: |
| 231 | + setting_data = request.data.get(setting_name) |
| 232 | + if not setting_data: |
| 233 | + continue |
| 234 | + |
| 235 | + new_value = setting_data.get("value") |
| 236 | + if new_value is None: |
| 237 | + log.debug("Skipping setting %s - no value provided", setting_name) |
| 238 | + continue |
| 239 | + |
| 240 | + app_id = course_app_settings_map[setting_name] |
| 241 | + |
| 242 | + if self._update_course_app_setting( |
| 243 | + request=request, |
| 244 | + course_key=course_key, |
| 245 | + setting_name=setting_name, |
| 246 | + app_id=app_id, |
| 247 | + enabled=new_value |
| 248 | + ): |
| 249 | + settings_processed += 1 |
| 250 | + # Remove from request data since it's been handled by course app |
| 251 | + request.data.pop(setting_name) |
| 252 | + else: |
| 253 | + settings_failed += 1 |
| 254 | + |
| 255 | + log.info( |
| 256 | + "Course app settings processing complete: course_key=%s, " |
| 257 | + "processed=%d, failed=%d (falling back to advanced settings)", |
| 258 | + course_key, settings_processed, settings_failed |
| 259 | + ) |
| 260 | + |
| 261 | + def _update_course_app_setting( |
| 262 | + self, request: Request, course_key: CourseKey, |
| 263 | + setting_name: str, app_id: str, enabled: bool |
| 264 | + ) -> bool: |
| 265 | + """ |
| 266 | + Update a single course app setting. |
| 267 | +
|
| 268 | + Args: |
| 269 | + request: The HTTP request |
| 270 | + course_key: The course key |
| 271 | + setting_name: Name of the advanced setting |
| 272 | + app_id: ID of the course app |
| 273 | + enabled: Whether to enable or disable the app |
| 274 | +
|
| 275 | + Returns: |
| 276 | + bool: True if update was successful, False if it failed |
| 277 | + """ |
| 278 | + try: |
| 279 | + log.debug( |
| 280 | + "Attempting course app update: course_key=%s, app_id=%s, " |
| 281 | + "setting=%s, enabled=%s, user=%s", |
| 282 | + course_key, app_id, setting_name, enabled, request.user.username |
| 283 | + ) |
| 284 | + |
| 285 | + set_course_app_status( |
| 286 | + course_key=course_key, |
| 287 | + app_id=app_id, |
| 288 | + enabled=enabled, |
| 289 | + user=request.user, |
| 290 | + ) |
| 291 | + |
| 292 | + log.info( |
| 293 | + "Successfully updated course app via advanced settings: " |
| 294 | + "course_key=%s, app_id=%s, setting=%s, enabled=%s, user=%s", |
| 295 | + course_key, app_id, setting_name, enabled, request.user.username |
| 296 | + ) |
| 297 | + return True |
| 298 | + |
| 299 | + except ValidationError as e: |
| 300 | + log.warning( |
| 301 | + "Course app update failed with validation error, " |
| 302 | + "will fallback to advanced settings flow: " |
| 303 | + "course_key=%s, app_id=%s, setting=%s, enabled=%s, user=%s, error=%s", |
| 304 | + course_key, app_id, setting_name, enabled, request.user.username, str(e) |
| 305 | + ) |
| 306 | + return False |
0 commit comments