diff --git a/.github/workflows/pylint-checks.yml b/.github/workflows/pylint-checks.yml index e643efd61e..f9f86a096b 100644 --- a/.github/workflows/pylint-checks.yml +++ b/.github/workflows/pylint-checks.yml @@ -17,7 +17,7 @@ jobs: - module-name: lms-1 path: "lms/djangoapps/badges/ lms/djangoapps/branding/ lms/djangoapps/bulk_email/ lms/djangoapps/bulk_enroll/ lms/djangoapps/bulk_user_retirement/ lms/djangoapps/ccx/ lms/djangoapps/certificates/ lms/djangoapps/commerce/ lms/djangoapps/course_api/ lms/djangoapps/course_blocks/ lms/djangoapps/course_home_api/ lms/djangoapps/course_wiki/ lms/djangoapps/coursewarehistoryextended/ lms/djangoapps/debug/ lms/djangoapps/courseware/ lms/djangoapps/course_goals/ lms/djangoapps/rss_proxy/ lms/djangoapps/save_for_later/" - module-name: lms-2 - path: "lms/djangoapps/gating/ lms/djangoapps/grades/ lms/djangoapps/instructor/ lms/djangoapps/instructor_analytics/ lms/djangoapps/discussion/ lms/djangoapps/edxnotes/ lms/djangoapps/email_marketing/ lms/djangoapps/experiments/ lms/djangoapps/instructor_task/ lms/djangoapps/learner_dashboard/ lms/djangoapps/lms_initialization/ lms/djangoapps/lms_xblock/ lms/djangoapps/lti_provider/ lms/djangoapps/mailing/ lms/djangoapps/mobile_api/ lms/djangoapps/monitoring/ lms/djangoapps/program_enrollments/ lms/djangoapps/rss_proxy lms/djangoapps/static_template_view/ lms/djangoapps/staticbook/ lms/djangoapps/support/ lms/djangoapps/survey/ lms/djangoapps/teams/ lms/djangoapps/tests/ lms/djangoapps/user_tours/ lms/djangoapps/verify_student/ lms/envs/ lms/lib/ lms/tests.py" + path: "lms/djangoapps/gating/ lms/djangoapps/grades/ lms/djangoapps/instructor/ lms/djangoapps/instructor_analytics/ lms/djangoapps/discussion/ lms/djangoapps/edxnotes/ lms/djangoapps/email_marketing/ lms/djangoapps/experiments/ lms/djangoapps/instructor_task/ lms/djangoapps/learner_dashboard/ lms/djangoapps/lms_initialization/ lms/djangoapps/lms_xblock/ lms/djangoapps/lti_provider/ lms/djangoapps/mailing/ lms/djangoapps/mobile_api/ lms/djangoapps/monitoring/ lms/djangoapps/ora_staff_grader/ lms/djangoapps/program_enrollments/ lms/djangoapps/rss_proxy lms/djangoapps/static_template_view/ lms/djangoapps/staticbook/ lms/djangoapps/support/ lms/djangoapps/survey/ lms/djangoapps/teams/ lms/djangoapps/tests/ lms/djangoapps/user_tours/ lms/djangoapps/verify_student/ lms/envs/ lms/lib/ lms/tests.py" - module-name: openedx-1 path: "openedx/core/types/ openedx/core/djangoapps/ace_common/ openedx/core/djangoapps/agreements/ openedx/core/djangoapps/api_admin/ openedx/core/djangoapps/auth_exchange/ openedx/core/djangoapps/bookmarks/ openedx/core/djangoapps/cache_toolbox/ openedx/core/djangoapps/catalog/ openedx/core/djangoapps/ccxcon/ openedx/core/djangoapps/commerce/ openedx/core/djangoapps/common_initialization/ openedx/core/djangoapps/common_views/ openedx/core/djangoapps/config_model_utils/ openedx/core/djangoapps/content/ openedx/core/djangoapps/content_libraries/ openedx/core/djangoapps/contentserver/ openedx/core/djangoapps/cookie_metadata/ openedx/core/djangoapps/cors_csrf/ openedx/core/djangoapps/course_apps/ openedx/core/djangoapps/course_date_signals/ openedx/core/djangoapps/course_groups/ openedx/core/djangoapps/coursegraph/ openedx/core/djangoapps/courseware_api/ openedx/core/djangoapps/crawlers/ openedx/core/djangoapps/credentials/ openedx/core/djangoapps/credit/ openedx/core/djangoapps/dark_lang/ openedx/core/djangoapps/debug/ openedx/core/djangoapps/demographics/ openedx/core/djangoapps/discussions/ openedx/core/djangoapps/django_comment_common/ openedx/core/djangoapps/embargo/ openedx/core/djangoapps/enrollments/ openedx/core/djangoapps/external_user_ids/ openedx/core/djangoapps/zendesk_proxy/ openedx/core/djangolib/ openedx/core/lib/ openedx/core/tests/" - module-name: openedx-2 diff --git a/.github/workflows/unit-test-shards.json b/.github/workflows/unit-test-shards.json index 58e4b8fddc..a30423815e 100644 --- a/.github/workflows/unit-test-shards.json +++ b/.github/workflows/unit-test-shards.json @@ -59,6 +59,7 @@ "lms/djangoapps/mailing/", "lms/djangoapps/mobile_api/", "lms/djangoapps/monitoring/", + "lms/djangoapps/ora_staff_grader/", "lms/djangoapps/program_enrollments/", "lms/djangoapps/rss_proxy/", "lms/djangoapps/save_for_later/", diff --git a/cms/envs/common.py b/cms/envs/common.py index ccf6adc514..4cf5cc8567 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -1816,6 +1816,7 @@ OPTIONAL_APPS = ( ('openassessment', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), ('openassessment.assessment', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), ('openassessment.fileupload', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + ('openassessment.staffgrader', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), ('openassessment.workflow', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), ('openassessment.xblock', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), diff --git a/common/djangoapps/student/management/commands/_create_users.py b/common/djangoapps/student/management/commands/_create_users.py index 3b0aeb0d90..f3e739ac48 100644 --- a/common/djangoapps/student/management/commands/_create_users.py +++ b/common/djangoapps/student/management/commands/_create_users.py @@ -1,18 +1,25 @@ """ Shared behavior between create_test_users and create_random_users """ +from django.contrib.auth import get_user_model +from django.core.validators import ValidationError from xmodule.modulestore.django import modulestore + from lms.djangoapps.instructor.access import allow_access from openedx.core.djangoapps.user_authn.views.registration_form import AccountCreationForm -from common.djangoapps.student.helpers import do_create_account +from common.djangoapps.student.helpers import do_create_account, AccountValidationError from common.djangoapps.student.models import CourseEnrollment +User = get_user_model() + + def create_users( course_key, user_data, enrollment_mode=None, course_staff=False, - activate=False + activate=False, + ignore_user_already_exists=False, ): """Create users, enrolling them in course_key if it's not None""" for single_user_data in user_data: @@ -21,7 +28,26 @@ def create_users( tos_required=False ) - (user, _, _) = do_create_account(account_creation_form) + user_already_exists = False + try: + (user, _, _) = do_create_account(account_creation_form) + except (ValidationError, AccountValidationError) as account_creation_error: + # It would be convenient if we just had the AccountValidationError raised, because we include a + # helpful error code in there, but we also do form validation on account_creation_form and those + # are pretty opaque. + try: + # Check to see if there's a user with our username. If the username and email match our input, + # we're good, it's the same user, probably. If the email doesn't match, just to be safe we will + # continue to fail. + user = User.objects.get(username=single_user_data['username']) + if user.email == single_user_data['email'] and ignore_user_already_exists: + user_already_exists = True + print(f'Test user {user.username} already exists. Continuing to attempt to enroll.') + else: + raise account_creation_error + except User.DoesNotExist: + # If a user with the username doesn't exist the error was probably something else, so reraise + raise account_creation_error # pylint: disable=raise-missing-from if activate: user.is_active = True @@ -33,7 +59,7 @@ def create_users( course = modulestore().get_course(course_key, depth=1) allow_access(course, user, 'staff', send_email=False) - if course_key and course_staff: + if course_key and course_staff and not user_already_exists: print(f'Created user {user.username} as course staff') - else: + elif not user_already_exists: print(f'Created user {user.username}') diff --git a/common/djangoapps/student/management/commands/create_test_users.py b/common/djangoapps/student/management/commands/create_test_users.py index 8c44f5ebdd..9881c37e7f 100644 --- a/common/djangoapps/student/management/commands/create_test_users.py +++ b/common/djangoapps/student/management/commands/create_test_users.py @@ -64,6 +64,11 @@ class Command(BaseCommand): ), action='store_true' ) + parser.add_argument( + '--ignore_user_already_exists', + help="Don't fail if a user already exists. Log the error and attempt to enroll them in the course.", + action='store_true' + ) def handle(self, *args, **options): course_key = options['course'] @@ -78,5 +83,6 @@ class Command(BaseCommand): ), enrollment_mode=enrollment_mode, course_staff=course_staff, - activate=True + activate=True, + ignore_user_already_exists=options['ignore_user_already_exists'] ) diff --git a/common/djangoapps/student/management/tests/test_create_test_users.py b/common/djangoapps/student/management/tests/test_create_test_users.py index c1c0f1c427..9258b15f4f 100644 --- a/common/djangoapps/student/management/tests/test_create_test_users.py +++ b/common/djangoapps/student/management/tests/test_create_test_users.py @@ -29,10 +29,18 @@ class CreateTestUsersTestCase(SharedModuleStoreTestCase): self.user_model = get_user_model() self.num_users_start = len(self.user_model.objects.all()) - def call_command(self, users, course=None, mode=None, password=None, domain=None, course_staff=False): + def call_command( + self, + users, + course=None, + mode=None, + password=None, + domain=None, + course_staff=False, + ignore_user_already_exists=False + ): """ Helper method to call the management command with various arguments """ - args = ['create_test_users'] - args.extend(users) + args = list(users) if course: args.extend(['--course', course]) if mode: @@ -43,7 +51,10 @@ class CreateTestUsersTestCase(SharedModuleStoreTestCase): args.extend(['--domain', domain]) if course_staff: args.append('--course_staff') - call_command(*args) + if ignore_user_already_exists: + args.append('--ignore_user_already_exists') + + call_command('create_test_users', *args) def test_create_users(self): """ @@ -203,3 +214,19 @@ class CreateTestUsersTestCase(SharedModuleStoreTestCase): user = self.user_model.objects.get(username=username) assert not CourseAccessRole.objects.filter(user=user).exists() assert not CourseEnrollment.objects.filter(user=user).exists() + + def test_create_user__ignore_user_already_exists(self): + """ + Test that ignore_user_already_exists will allow us to specify a username + that already exists without raising an exception + """ + test_username = 'IgnoreUserAlreadyExistsUser' + assert not self.user_model.objects.filter(username=test_username).exists() + + self.call_command([test_username]) + assert self.user_model.objects.filter(username=test_username).exists() + + with self.assertRaises(ValidationError): + self.call_command([test_username], ignore_user_already_exists=False) + + self.call_command([test_username], ignore_user_already_exists=True) diff --git a/lms/djangoapps/ora_staff_grader/README.md b/lms/djangoapps/ora_staff_grader/README.md new file mode 100644 index 0000000000..32e5a932ae --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/README.md @@ -0,0 +1,17 @@ +# Enhanced Staff Grader (ESG) App + +A backend-for-frontend (BFF) for ESG. It provides endpoints at the path `{lms-url}/api/ora_staff_grader/{endpoint}`. + +ESG is an application on top of Open Response Assessments (ORA) designed to simplify staff grading of assignments. The BFF is designed to service the ESG microfrontend (MFE) by aggregating and packaging requests to both `edx-platform` and `edx-ora2`. + +The BFF includes both an API and mock API (/mock) for testing. Exercise either with the attached Postman collections (and included examples) or see [Enhanced Staff Grader Data Flow Design](https://openedx.atlassian.net/wiki/spaces/PT/pages/3154542730/Enhanced+Staff+Grader+Data+Flow+Design) for API reference. + +## Quickstart + +Connect to or exercise endpoints at `{lms-url}/api/ora_staff_grader/{endpoint}`. + +Alternatively, use the attached postman collections to perform headless testing of endpoints. Following the setup below: + +1. Perform headless login: in `lms.postman_collection.json` perform the `GET Login` call to generate a new CSRF token followed by a `POST Login` with valid staff credentials to authenticate with LMS. +2. Configure needed envirionment variables including `{{mock}} = False` +2. Exercise endpoints: in `ora_staff_grader.postman_collection.json` diff --git a/lms/djangoapps/ora_staff_grader/__init__.py b/lms/djangoapps/ora_staff_grader/__init__.py new file mode 100644 index 0000000000..1be6141580 --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/__init__.py @@ -0,0 +1,3 @@ +""" +App for Enhanced Staff Grader (ESG) backend-for-frontend (BFF) +""" diff --git a/lms/djangoapps/ora_staff_grader/constants.py b/lms/djangoapps/ora_staff_grader/constants.py new file mode 100644 index 0000000000..d1ef62287b --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/constants.py @@ -0,0 +1,13 @@ +""" Constants used throughout ESG """ + +# Query params +PARAM_ORA_LOCATION = "oraLocation" +PARAM_SUBMISSION_ID = "submissionUUID" + +# Error codes +ERR_UNKNOWN = "ERR_UNKNOWN" +ERR_INTERNAL = "ERR_INTERNAL" +ERR_MISSING_PARAM = "ERR_MISSING_PARAM" +ERR_BAD_ORA_LOCATION = "ERR_BAD_ORA_LOCATION" +ERR_LOCK_CONTESTED = "ERR_LOCK_CONTESTED" +ERR_GRADE_CONTESTED = "ERR_GRADE_CONTESTED" diff --git a/lms/djangoapps/ora_staff_grader/errors.py b/lms/djangoapps/ora_staff_grader/errors.py new file mode 100644 index 0000000000..801ecc32da --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/errors.py @@ -0,0 +1,116 @@ +""" Error codes and exceptions for ESG """ +from rest_framework import serializers +from rest_framework.response import Response + +from lms.djangoapps.ora_staff_grader.constants import ( + ERR_BAD_ORA_LOCATION, + ERR_GRADE_CONTESTED, + ERR_INTERNAL, + ERR_LOCK_CONTESTED, + ERR_MISSING_PARAM, + ERR_UNKNOWN, +) + + +class ExceptionWithContext(Exception): + """An exception with optional context dict to be supplied in serialized result""" + + def __init__(self, context=None): + super().__init__(self) + self.context = context + + +class XBlockInternalError(ExceptionWithContext): + """Errors from XBlock handlers""" + + +class LockContestedError(ExceptionWithContext): + """Signal for trying to operate on a lock owned by someone else""" + + +class ErrorSerializer(serializers.Serializer): + """Returns error code and unpacks additional context, returns unknown error code if not supplied""" + + error = serializers.CharField(default=ERR_UNKNOWN) + + def to_representation(self, instance): + """Override to unpack context alongside error code""" + output = super().to_representation(instance) + + if self.context: + for key, value in self.context.items(): + output[key] = value + + return output + + +class StaffGraderErrorResponse(Response): + """An HTTP error response that returns serialized error data with additional provided context""" + + status = 500 + err_code = ERR_UNKNOWN + + def __init__(self, context=None): + # Unpack provided content into error structure + content = ErrorSerializer({"error": self.err_code}, context=context).data + super().__init__(content, status=self.status) + + +class BadOraLocationResponse(StaffGraderErrorResponse): + """ + Error response for when the requested ORA_LOCATION could not be found in a course. + Returns an HTTP 400 with error code. + """ + + status = 400 + err_code = ERR_BAD_ORA_LOCATION + + +class MissingParamResponse(StaffGraderErrorResponse): + """ + Error response for when a request is missing a required param/body. + Returns an HTTP 400 with error code. + """ + + status = 400 + err_code = ERR_MISSING_PARAM + + +class LockContestedResponse(StaffGraderErrorResponse): + """ + Error response for when a user tries to operate on a submission that they do not have a lock for. + Returns an HTTP 409 with error code and updated lock status. + """ + + status = 409 + err_code = ERR_LOCK_CONTESTED + + +class GradeContestedResponse(StaffGraderErrorResponse): + """ + Error response for when a user tries to operate on a submission that they do not have a lock for. + Returns an HTTP 409 with error code and updated submission status. + """ + + status = 409 + err_code = ERR_GRADE_CONTESTED + + +class InternalErrorResponse(StaffGraderErrorResponse): + """ + Generic error response for an issue in an XBlock handler. + Returns an HTTP 500 with internal error code. + """ + + status = 500 + err_code = ERR_INTERNAL + + +class UnknownErrorResponse(StaffGraderErrorResponse): + """ + Blanket exception for when something strange breaks + Returns an HTTP 500 with generic error code. + """ + + status = 500 + err_code = ERR_UNKNOWN diff --git a/lms/djangoapps/ora_staff_grader/lms.postman_collection.json b/lms/djangoapps/ora_staff_grader/lms.postman_collection.json new file mode 100644 index 0000000000..db7fb3762f --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/lms.postman_collection.json @@ -0,0 +1,105 @@ +{ + "info": { + "_postman_id": "68587cc2-2edc-45c7-a89a-7be3d5d69cd3", + "name": "LMS", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Login", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var xsrfCookie = postman.getResponseCookie(\"csrftoken\");", + "postman.setEnvironmentVariable('csrftoken', xsrfCookie.value);" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "X-CSRFToken", + "value": "{{csrftoken}}", + "type": "text" + } + ], + "body": { + "mode": "formdata", + "formdata": [] + }, + "url": { + "raw": "{{protocol}}://{{lms_url}}/login", + "protocol": "{{protocol}}", + "host": [ + "{{lms_url}}" + ], + "path": [ + "login" + ], + "query": [ + { + "key": "", + "value": null, + "disabled": true + } + ] + }, + "description": "Run this to get an initial CSRF token. Follow with POST to login as {{user}}" + }, + "response": [] + }, + { + "name": "Login", + "request": { + "method": "POST", + "header": [ + { + "key": "X-CSRFToken", + "value": "{{csrftoken}}", + "type": "text" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "email", + "value": "{{user_email}}", + "type": "text" + }, + { + "key": "password", + "value": "{{user_password}}", + "type": "text" + } + ] + }, + "url": { + "raw": "{{protocol}}://{{lms_url}}/api/user/v1/account/login_session/", + "protocol": "{{protocol}}", + "host": [ + "{{lms_url}}" + ], + "path": [ + "api", + "user", + "v1", + "account", + "login_session", + "" + ] + }, + "description": "Run this to authenticate with LMS. Requires running GET Login first to obtain a CSRF token." + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/lms/djangoapps/ora_staff_grader/mock/README.md b/lms/djangoapps/ora_staff_grader/mock/README.md new file mode 100644 index 0000000000..9eb5486eff --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/mock/README.md @@ -0,0 +1,22 @@ +# Mock Enhanced Staff Grader (ESG) + +A mock backend-for-frontend (BFF) for ESG. It provides mocked endpoints at the path http(s)://{lms-url}/api/ora_staff_grader/mock/{endpoint}. + +This is differentiated from the "real" BFF endpoints, which omit the `mock` part of the path. This should make it easy to switch between real/mocked versions by configuring the base API path. + +The mock is, effectively, a wrapper on top of a JSON data store. All the important data is stored in the lms/djangoapps/ora_staff_grader/mock/data/ directory. Data is generally grouped by a key that would be supplied in the request (usually the submissionUUID and/or ora_location). To add/edit data, simply edit the underlying JSON files. + +For some endpoints (e.g. lock/unlock/grade), there is simple interactivity; hitting the endpoint will save a change to the underlying data. These can be verified by reading the updated JSON files or reverted by doing a git checkout of the file. + +## Quickstart + +Connect to or exercise endpoints at `{devstack-url}/api/ora_staff_grader/mock/{endpoint}`. + +Alternatively, use the attached postman collections to perform headless testing of endpoints. Following the setup below: + +1. Perform headless login: in `lms.postman_collection.json` perform the `GET Login` call to generate a new CSRF token followed by a `POST Login` with valid credentials to authenticate with LMS. +2. Exercise mock endpoints: in `ora_staff_grader.postman_collection.json`, after configuring the environment variables including `{{mock}} = True`, run the example requests. + +## API Reference + +See [Enhanced Staff Grader Data Flow Design](https://openedx.atlassian.net/wiki/spaces/PT/pages/3154542730/Enhanced+Staff+Grader+Data+Flow+Design) diff --git a/lms/djangoapps/ora_staff_grader/mock/__init__.py b/lms/djangoapps/ora_staff_grader/mock/__init__.py new file mode 100644 index 0000000000..af6b04a694 --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/mock/__init__.py @@ -0,0 +1,3 @@ +""" +Mock tooling for ESG +""" diff --git a/lms/djangoapps/ora_staff_grader/mock/data/course_metadata.json b/lms/djangoapps/ora_staff_grader/mock/data/course_metadata.json new file mode 100644 index 0000000000..1b734dab94 --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/mock/data/course_metadata.json @@ -0,0 +1,14 @@ +{ + "block-a": { + "title": "Defense against the dark arts", + "org": "Hogwarts", + "number": "DADA101", + "courseId": "course-v1:Hogwarts+DADA101+2021_Winter" + }, + "block-b": { + "title": "Introduction to Time Travel", + "org": "Oxford", + "number": "TT101", + "courseId": "course-v1:Oxford+TT101+2021_Winter" + } +} diff --git a/lms/djangoapps/ora_staff_grader/mock/data/ora_metadata.json b/lms/djangoapps/ora_staff_grader/mock/data/ora_metadata.json new file mode 100644 index 0000000000..c3b34d8f1b --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/mock/data/ora_metadata.json @@ -0,0 +1,93 @@ +{ + "block-a": { + "name": "Individual ORA", + "prompts": [ + "
Enter a text/files response.
" + ], + "type": "individual", + "rubricConfig": { + "feedback_prompt": "How would you grade this response?", + "feedback_default_text": "I believe this response...", + "feedback": "optional", + "criteria": [ + { + "orderNum": 0, + "name": "grammar", + "label": "Grammar", + "prompt": "How correct is the submitter's grammar?", + "feedback": "optional", + "options": [ + { + "orderNum": 0, + "name": "poor", + "label": "Poor", + "explanation": "Absolute rubbish", + "points": 0 + }, + { + "orderNum": 1, + "name": "good", + "label": "Good", + "explanation": "pretty good", + "points": 3 + }, + { + "orderNum": 2, + "name": "excellent", + "label": "Excelent", + "explanation": "Not absolute rubbish", + "points": 5 + } + ] + } + ] + }, + "textResponseConfig": "optional", + "fileUploadResponseConfig": "optional" + }, + "block-b": { + "name": "Team ORA", + "prompts": [ + "Enter a text/files response.
" + ], + "type": "team", + "rubricConfig": { + "feedbackPrompt": "How would you grade this response?", + "feedback": "optional", + "criteria": [ + { + "orderNum": 0, + "name": "correctness", + "label": "Correctness", + "prompt": "How correct is the team's answer?", + "feedback": "optional", + "options": [ + { + "orderNum": 0, + "name": "poor", + "label": "Poor", + "explanation": "Absolute rubbish", + "points": 0 + }, + { + "orderNum": 1, + "name": "good", + "label": "Good", + "explanation": "pretty good", + "points": 3 + }, + { + "orderNum": 2, + "name": "excellent", + "label": "Excelent", + "explanation": "Not absolute rubbish", + "points": 5 + } + ] + } + ] + }, + "textResponseConfig": "optional", + "fileUploadResponseConfig": "required" + } +} diff --git a/lms/djangoapps/ora_staff_grader/mock/data/responses.json b/lms/djangoapps/ora_staff_grader/mock/data/responses.json new file mode 100644 index 0000000000..7c8d3c9b52 --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/mock/data/responses.json @@ -0,0 +1,137 @@ +{ + "SUBMISSION_ID-0": { + "text": [ + "Enter a text/files response.
\"\n }\n ],\n \"type\": \"individual\",\n \"textResponseConfig\": \"optional\",\n \"fileUploadResponseConfig\": \"required\",\n \"rubricConfig\": {\n \"feedbackPrompt\": \"(Optional) What aspects of this response stood out to you? What did it do well? How could it be improved?\\n\",\n \"criteria\": [\n {\n \"label\": \"Ideas\",\n \"prompt\": \"Determine if there is a unifying theme or main idea.\",\n \"feedback\": \"optional\",\n \"name\": \"Ideas\",\n \"orderNum\": 0,\n \"options\": [\n {\n \"label\": \"Poor\",\n \"points\": 0,\n \"explanation\": \"Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.\",\n \"name\": \"Poor\",\n \"orderNum\": 0\n },\n {\n \"label\": \"Fair\",\n \"points\": 3,\n \"explanation\": \"Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.\",\n \"name\": \"Fair\",\n \"orderNum\": 1\n },\n {\n \"label\": \"Good\",\n \"points\": 5,\n \"explanation\": \"Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.\",\n \"name\": \"Good\",\n \"orderNum\": 2\n }\n ]\n },\n {\n \"label\": \"Content\",\n \"prompt\": \"Assess the content of the submission\",\n \"feedback\": \"disabled\",\n \"name\": \"Content\",\n \"orderNum\": 1,\n \"options\": [\n {\n \"label\": \"Poor\",\n \"points\": 0,\n \"explanation\": \"Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.\",\n \"name\": \"Poor\",\n \"orderNum\": 0\n },\n {\n \"label\": \"Fair\",\n \"points\": 1,\n \"explanation\": \"Includes little information and few or no details. Explores only one or two facets of the topic.\",\n \"name\": \"Fair\",\n \"orderNum\": 1\n },\n {\n \"label\": \"Good\",\n \"points\": 3,\n \"explanation\": \"Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.\",\n \"name\": \"Good\",\n \"orderNum\": 2\n },\n {\n \"label\": \"Excellent\",\n \"points\": 5,\n \"explanation\": \"Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.\",\n \"name\": \"Excellent\",\n \"orderNum\": 3\n }\n ]\n }\n ]\n }\n },\n \"submissions\": {\n \"034442b6-2ae8-4b17-815f-0c2e08aaa7ab\": {\n \"submissionUUID\": \"034442b6-2ae8-4b17-815f-0c2e08aaa7ab\",\n \"username\": \"test1\",\n \"teamName\": null,\n \"dateSubmitted\": \"2020-12-09 21:02:29.493046+00:00\",\n \"dateGraded\": \"2020-12-09 21:06:04.235926+00:00\",\n \"gradedBy\": \"staff\",\n \"gradingStatus\": \"graded\",\n \"lockStatus\": \"unlocked\",\n \"score\": {\n \"pointsEarned\": 6,\n \"pointsPossible\": 10\n }\n },\n \"a80465d1-cf77-405d-9d6d-94a0f94ff13a\": {\n \"submissionUUID\": \"a80465d1-cf77-405d-9d6d-94a0f94ff13a\",\n \"username\": \"test2\",\n \"teamName\": null,\n \"dateSubmitted\": \"2020-12-09 21:09:46.286584+00:00\",\n \"dateGraded\": \"2020-12-09 21:13:53.798227+00:00\",\n \"gradedBy\": \"staff\",\n \"gradingStatus\": \"graded\",\n \"lockStatus\": \"in-progress\",\n \"score\": {\n \"pointsEarned\": 4,\n \"pointsPossible\": 10\n }\n },\n \"e273d6b6-77dc-4161-9efa-ad35ea0ca6af\": {\n \"submissionUUID\": \"e273d6b6-77dc-4161-9efa-ad35ea0ca6af\",\n \"username\": \"verified\",\n \"teamName\": null,\n \"dateSubmitted\": \"2021-03-04 21:01:26.295960+00:00\",\n \"dateGraded\": \"2021-10-27 14:49:16.236821+00:00\",\n \"gradedBy\": \"staff\",\n \"gradingStatus\": \"graded\",\n \"lockStatus\": \"unlocked\",\n \"score\": {\n \"pointsEarned\": 4,\n \"pointsPossible\": 10\n }\n },\n \"3e34fdba-befd-490c-a2bd-a8d2e4dae028\": {\n \"submissionUUID\": \"3e34fdba-befd-490c-a2bd-a8d2e4dae028\",\n \"username\": \"staff\",\n \"teamName\": null,\n \"dateSubmitted\": \"2021-05-19 16:42:36.078273+00:00\",\n \"dateGraded\": \"2021-10-27 14:49:45.967842+00:00\",\n \"gradedBy\": \"staff\",\n \"gradingStatus\": \"graded\",\n \"lockStatus\": \"unlocked\",\n \"score\": {\n \"pointsEarned\": 10,\n \"pointsPossible\": 10\n }\n }\n }\n}" + }, + { + "name": "Missing Param", + "originalRequest": { + "method": "GET", + "header": [ + { + "key": "X-CSRFToken", + "value": "{{csrftoken}}", + "type": "text" + } + ], + "url": { + "raw": "{{protocol}}://{{lms_url}}/api/ora_staff_grader{{mock}}/initialize", + "protocol": "{{protocol}}", + "host": [ + "{{lms_url}}" + ], + "path": [ + "api", + "ora_staff_grader{{mock}}", + "initialize" + ], + "query": [ + { + "key": "oraLocation", + "value": "{{block_id_encoded}}", + "disabled": true + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Mon, 20 Dec 2021 23:01:04 GMT" + }, + { + "key": "Server", + "value": "WSGIServer/0.2 CPython/3.8.10" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Vary", + "value": "Accept, Accept-Language, Origin, Cookie" + }, + { + "key": "Allow", + "value": "GET, HEAD, OPTIONS" + }, + { + "key": "Server-Timing", + "value": "TimerPanel_utime;dur=19.137000000000626;desc=\"User CPU time\", TimerPanel_stime;dur=1.4929999999999666;desc=\"System CPU time\", TimerPanel_total;dur=20.630000000000592;desc=\"Total CPU time\", TimerPanel_total_time;dur=22.34625816345215;desc=\"Elapsed time\", SQLPanel_sql_time;dur=0;desc=\"SQL 0 queries\"" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Content-Language", + "value": "en" + }, + { + "key": "Content-Length", + "value": "29" + }, + { + "key": "Set-Cookie", + "value": "openedx-language-preference=en; expires=Mon, 03 Jan 2022 23:01:04 GMT; Max-Age=1209600; Path=/" + } + ], + "cookie": [], + "body": "{\n \"error\": \"ERR_MISSING_PARAM\"\n}" + }, + { + "name": "Bad ORA location", + "originalRequest": { + "method": "GET", + "header": [ + { + "key": "X-CSRFToken", + "value": "{{csrftoken}}", + "type": "text" + } + ], + "url": { + "raw": "{{protocol}}://{{lms_url}}/api/ora_staff_grader{{mock}}/initialize?oraLocation=foo", + "protocol": "{{protocol}}", + "host": [ + "{{lms_url}}" + ], + "path": [ + "api", + "ora_staff_grader{{mock}}", + "initialize" + ], + "query": [ + { + "key": "oraLocation", + "value": "foo" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Mon, 20 Dec 2021 23:01:58 GMT" + }, + { + "key": "Server", + "value": "WSGIServer/0.2 CPython/3.8.10" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Vary", + "value": "Accept, Accept-Language, Origin, Cookie" + }, + { + "key": "Allow", + "value": "GET, HEAD, OPTIONS" + }, + { + "key": "Server-Timing", + "value": "TimerPanel_utime;dur=77.86000000000115;desc=\"User CPU time\", TimerPanel_stime;dur=12.359000000000009;desc=\"System CPU time\", TimerPanel_total;dur=90.21900000000116;desc=\"Total CPU time\", TimerPanel_total_time;dur=93.50705146789551;desc=\"Elapsed time\", SQLPanel_sql_time;dur=0;desc=\"SQL 0 queries\"" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Content-Language", + "value": "en" + }, + { + "key": "Content-Length", + "value": "32" + }, + { + "key": "Set-Cookie", + "value": "openedx-language-preference=en; expires=Mon, 03 Jan 2022 23:01:58 GMT; Max-Age=1209600; Path=/" + } + ], + "cookie": [], + "body": "{\n \"error\": \"ERR_BAD_ORA_LOCATION\"\n}" + } + ] + }, + { + "name": "Fetch Submission", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-CSRFToken", + "value": "{{csrftoken}}", + "type": "text" + } + ], + "url": { + "raw": "{{protocol}}://{{lms_url}}/api/ora_staff_grader{{mock}}/submission?oraLocation={{block_id_encoded}}&submissionUUID={{submission_id}}", + "protocol": "{{protocol}}", + "host": [ + "{{lms_url}}" + ], + "path": [ + "api", + "ora_staff_grader{{mock}}", + "submission" + ], + "query": [ + { + "key": "oraLocation", + "value": "{{block_id_encoded}}" + }, + { + "key": "submissionUUID", + "value": "{{submission_id}}" + } + ] + } + }, + "response": [ + { + "name": "Fetch Submission Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "key": "X-CSRFToken", + "value": "{{csrftoken}}", + "type": "text" + } + ], + "url": { + "raw": "{{protocol}}://{{lms_url}}/api/ora_staff_grader{{mock}}/submission?oraLocation={{block_id_encoded}}&submissionUUID={{submission_id}}", + "protocol": "{{protocol}}", + "host": [ + "{{lms_url}}" + ], + "path": [ + "api", + "ora_staff_grader{{mock}}", + "submission" + ], + "query": [ + { + "key": "oraLocation", + "value": "{{block_id_encoded}}" + }, + { + "key": "submissionUUID", + "value": "{{submission_id}}" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Date", + "value": "Mon, 15 Nov 2021 18:24:04 GMT" + }, + { + "key": "Server", + "value": "WSGIServer/0.2 CPython/3.8.10" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Vary", + "value": "Accept, Accept-Language, Origin, Cookie" + }, + { + "key": "Allow", + "value": "GET, HEAD, OPTIONS" + }, + { + "key": "Server-Timing", + "value": "TimerPanel_utime;dur=34.623000000001625;desc=\"User CPU time\", TimerPanel_stime;dur=3.391000000000144;desc=\"System CPU time\", TimerPanel_total;dur=38.01400000000177;desc=\"Total CPU time\", TimerPanel_total_time;dur=52.39248275756836;desc=\"Elapsed time\", SQLPanel_sql_time;dur=0;desc=\"SQL 0 queries\"" + }, + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "Content-Language", + "value": "en" + }, + { + "key": "Content-Length", + "value": "1553" + }, + { + "key": "Set-Cookie", + "value": "openedx-language-preference=en; expires=Mon, 29 Nov 2021 18:24:04 GMT; Max-Age=1209600; Path=/" + } + ], + "cookie": [], + "body": "{\n \"gradeData\": {\n \"score\": {\n \"pointsEarned\": 90,\n \"pointsPossible\": 100\n },\n \"overallFeedback\": \"was pretty good\",\n \"criteria\": [\n {\n \"name\": \"firstCriterion\",\n \"feedback\": \"did alright\",\n \"selectedOption\": \"good\"\n }\n ]\n },\n \"response\": {\n \"text\": [\n \"No assessment workflow matching submission_uuid {{submission_uuid}}\n | Request Method: | \nPOST | \n
|---|---|
| Request URL: | \nhttp://localhost:18000/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess | \n
| Django Version: | \n3.2.11 | \n
| Exception Type: | \nAssessmentWorkflowNotFoundError | \n
| Exception Value: | \n\n No assessment workflow matching submission_uuid {{submission_uuid}}\n | \n
| Exception Location: | \n\n /edx/src/edx-ora2/openassessment/workflow/api.py, line 371, in _get_workflow_model\n | \n
| Python Executable: | \n/edx/app/edxapp/venvs/edxapp/bin/python | \n
| Python Version: | \n3.8.10 | \n
| Python Path: | \n\n ['/edx/app/edx_ansible/edx_ansible/docker/plays',\n '/edx/app/edxapp/edx-platform',\n '/edx/app/edxapp/venvs/edxapp/lib/python38.zip',\n '/edx/app/edxapp/venvs/edxapp/lib/python3.8',\n '/edx/app/edxapp/venvs/edxapp/lib/python3.8/lib-dynload',\n '/usr/lib/python3.8',\n '/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages',\n '/edx/app/edxapp/edx-platform/common/lib/capa',\n '/edx/app/edxapp/edx-platform/common/lib/xmodule',\n '/edx/app/edxapp/venvs/edxapp/src/xblock-google-drive',\n '/edx/app/edxapp/edx-platform/openedx/core/lib/xblock_builtin/xblock_discussion',\n '/edx/app/edxapp/edx-platform/common/lib/symmath',\n '/edx/app/edxapp/edx-platform/common/lib/sandbox-packages',\n '/edx/app/edxapp/edx-platform/common/lib/safe_lxml',\n '/edx/app/edxapp/venvs/edxapp/src/rate-xblock',\n '/edx/app/edxapp/edx-platform',\n '/edx/app/edxapp/venvs/edxapp/src/olxcleaner',\n '/edx/app/edxapp/venvs/edxapp/src/edx-jsme',\n '/edx/app/edxapp/venvs/edxapp/src/django-wiki',\n '/edx/app/edxapp/venvs/edxapp/src/codejail',\n '/edx/src/edx-ora2']\n | \n
| Server time: | \nTue, 11 Jan 2022 17:03:37 +0000 | \n
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/django/core/handlers/exception.py, line 47, in inner\n response = await sync_to_async(response_for_exception, thread_sensitive=False)(request, exc)\n
return response\n
return inner\n
else:\n
@wraps(get_response)\n
def inner(request):\n
try:\n
response = get_response(request)\n …\n
except Exception as exc:\n
response = response_for_exception(request, exc)\n
return response\n
return inner\n
| Variable | \nValue | \n
|---|---|
| exc | \n\n AssessmentWorkflowNotFoundError('No assessment workflow matching submission_uuid {{submission_uuid}}')\n | \n
| get_response | \n\n <bound method BaseHandler._get_response of <django.core.handlers.wsgi.WSGIHandler object at 0x7f003285d5e0>>\n | \n
| request | \n\n <WSGIRequest: POST '/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess'>\n | \n
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/django/core/handlers/base.py, line 181, in _get_response\n if response is None:\n
wrapped_callback = self.make_view_atomic(callback)\n
# If it is an asynchronous view, run it in a subthread.\n
if asyncio.iscoroutinefunction(wrapped_callback):\n
wrapped_callback = async_to_sync(wrapped_callback)\n
try:\n
response = wrapped_callback(request, *callback_args, **callback_kwargs)\n …\n
except Exception as e:\n
response = self.process_exception_by_middleware(e, request)\n
if response is None:\n
raise\n
# Complain if the view returned None (a common error).\n
| Variable | \nValue | \n
|---|---|
| callback | \n\n <function handle_xblock_callback at 0x7f00194b7dc0>\n | \n
| callback_args | \n\n ()\n | \n
| callback_kwargs | \n\n {'course_id': 'course-v1:DevX+ORA101+T12020',\n 'handler': 'staff_assess',\n 'usage_id': 'block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69'}\n | \n
| middleware_method | \n\n <bound method EnsureJWTAuthSettingsMiddleware.process_view of <edx_rest_framework_extensions.auth.jwt.middleware.EnsureJWTAuthSettingsMiddleware object at 0x7f00170a28b0>>\n | \n
| request | \n\n <WSGIRequest: POST '/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess'>\n | \n
| response | \n\n None\n | \n
| self | \n\n <django.core.handlers.wsgi.WSGIHandler object at 0x7f003285d5e0>\n | \n
| wrapped_callback | \n\n <function handle_xblock_callback at 0x7f00194b7dc0>\n | \n
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/django/views/decorators/csrf.py, line 54, in wrapped_view\n def csrf_exempt(view_func):\n
"""Mark a view function as being exempt from the CSRF view protection."""\n
# view_func.csrf_exempt = True would also work, but decorators are nicer\n
# if they don't have side effects, so return a new function.\n
def wrapped_view(*args, **kwargs):\n
return view_func(*args, **kwargs)\n …\n
wrapped_view.csrf_exempt = True\n
return wraps(view_func)(wrapped_view)\n
| Variable | \nValue | \n
|---|---|
| args | \n\n (<WSGIRequest: POST '/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess'>,)\n | \n
| kwargs | \n\n {'course_id': 'course-v1:DevX+ORA101+T12020',\n 'handler': 'staff_assess',\n 'usage_id': 'block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69'}\n | \n
| view_func | \n\n <function handle_xblock_callback at 0x7f00194b7d30>\n | \n
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/django/views/decorators/clickjacking.py, line 50, in wrapped_view\n XFrameOptionsMiddleware to NOT set the X-Frame-Options HTTP header. Usage:\n
@xframe_options_exempt\n
def some_view(request):\n
...\n
"""\n
def wrapped_view(*args, **kwargs):\n
resp = view_func(*args, **kwargs)\n …\n
resp.xframe_options_exempt = True\n
return resp\n
return wraps(view_func)(wrapped_view)\n
| Variable | \nValue | \n
|---|---|
| args | \n\n (<WSGIRequest: POST '/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess'>,)\n | \n
| kwargs | \n\n {'course_id': 'course-v1:DevX+ORA101+T12020',\n 'handler': 'staff_assess',\n 'usage_id': 'block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69'}\n | \n
| view_func | \n\n <function handle_xblock_callback at 0x7f00194b7ca0>\n | \n
/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/module_render.py, line 1042, in handle_xblock_callback\n with modulestore().bulk_operations(course_key):\n
try:\n
course = modulestore().get_course(course_key)\n
except ItemNotFoundError:\n
raise Http404(f'{course_id} does not exist in the modulestore') # lint-amnesty, pylint: disable=raise-missing-from\n return _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, course=course)\n …\n
def _get_usage_key_for_course(course_key, usage_id) -> UsageKey:\n
"""\n
Returns UsageKey mapped into the course for a given usage_id string\n
"""\n
| Variable | \nValue | \n
|---|---|
| course | \n\n <CourseBlockWithMixins @5FD6 license=None, parent=None, name=None, tags=[], display_name='ORA Smoke Testing', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x7f00152755b0>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, children=[BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'chapter', '686fc2e208a34d3ba94e81582835b87f')], default_time_limit_minutes=None, exam_review_rules='', hide_after_due=False, is_entrance_exam=False, is_onboarding_exam=False, is_practice_exam=False, is_proctored_enabled=False, is_time_limited=False, position=None, xml_attributes={}, advanced_modules=[], advertised_start=None, allow_anonymous=True, allow_anonymous_to_peers=False, allow_proctoring_opt_out=False, allow_public_wiki_access=False, allow_unsupported_xblocks=False, announcement=None, banner_image='images_course_image.jpg', catalog_visibility='both', ccx_connector='', cert_html_view_enabled=True, cert_html_view_overrides={}, cert_name_long='', cert_name_short='', certificate_available_date=datetime.datetime(2021, 1, 3, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x7f0015275550>), certificates={}, certificates_display_behavior='end', certificates_show_before_end=False, cohort_config={}, cosmetic_display_price=0, course_image='ora-monitor.jpg', course_survey_name=None, course_survey_required=False, course_visibility='private', course_wide_css=[], course_wide_js=[], create_zendesk_tickets=True, css_class='', disable_progress_graph=False, discussion_blackouts=[], discussion_link=None, discussion_sort_alpha=False, discussion_topics={'General': {'id': 'course'}}, discussions_settings={'enable_in_context': True, 'enable_graded_units': False, 'unit_level_visibility': False}, display_coursenumber='', display_organization=None, due_date_display_format=None, enable_ccx=False, enable_proctored_exams=False, enable_subsection_gating=False, enable_timed_exams=True, end=datetime.datetime(2022, 1, 1, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x7f00152755b0>), end_of_course_survey_url=None, enrollment_domain=None, enrollment_end=datetime.datetime(2022, 1, 1, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x7f00152755b0>), enrollment_start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x7f00152755b0>), entrance_exam_enabled=False, entrance_exam_id=None, entrance_exam_minimum_score_pct=65, grading_policy={'GRADER': [{'type': 'Homework', 'short_label': 'HW', 'min_count': 12, 'drop_count': 2, 'weight': 0.15}, {'type': 'Lab', 'min_count': 12, 'drop_count': 2, 'weight': 0.15}, {'type': 'Midterm Exam', 'short_label': 'Midterm', 'min_count': 1, 'drop_count': 0, 'weight': 0.3}, {'type': 'Final Exam', 'short_label': 'Final', 'min_count': 1, 'drop_count': 0, 'weight': 0.4}], 'GRADE_CUTOFFS': {'Pass': 0.5}}, hide_progress_tab=None, highlights_enabled_for_messaging=False, html_textbooks=[], info_sidebar_name='Course Handouts', instructor_info={'instructors': []}, invitation_only=False, is_new=None, issue_badges=True, language='en', learning_info=[], lti_passports=[], max_student_enrollments_allowed=None, minimum_grade_credit=0.8, mobile_available=False, no_grade=Fa… <trimmed 5315 bytes string>\n | \n
| course_id | \n\n 'course-v1:DevX+ORA101+T12020'\n | \n
| course_key | \n\n CourseLocator('DevX', 'ORA101', 'T12020', None, None)\n | \n
| error | \n\n None\n | \n
| handler | \n\n 'staff_assess'\n | \n
| request | \n\n <WSGIRequest: POST '/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess'>\n | \n
| suffix | \n\n None\n | \n
| usage_id | \n\n 'block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69'\n | \n
/edx/app/edxapp/edx-platform/lms/djangoapps/courseware/module_render.py, line 1191, in _invoke_xblock_handler\n if is_xblock_aside(usage_key):\n
# In this case, 'instance' is the XBlock being wrapped by the aside, so\n
# the actual aside instance needs to be retrieved in order to invoke its\n
# handler method.\n
handler_instance = get_aside_from_xblock(instance, usage_key.aside_type)\n
else:\n
handler_instance = instance\n
resp = handler_instance.handle(handler, req, suffix)\n …\n
if suffix == 'problem_check' \\\n
and course \\\n
and getattr(course, 'entrance_exam_enabled', False) \\\n
and getattr(instance, 'in_entrance_exam', False):\n
ee_data = {'entrance_exam_passed': user_has_passed_entrance_exam(request.user, course)}\n resp = append_data_to_webob_response(resp, ee_data)\n
| Variable | \nValue | \n
|---|---|
| _ | \n\n {'module': {'display_name': 'Open Response Assessment',\n 'usage_key': 'block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69'}}\n | \n
| block_usage_key | \n\n BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'openassessment', '8c235f76c46948ec80c9d59bf5686d69')\n | \n
| course | \n\n <CourseBlockWithMixins @5FD6 license=None, parent=None, name=None, tags=[], display_name='ORA Smoke Testing', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x7f00152755b0>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, children=[BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'chapter', '686fc2e208a34d3ba94e81582835b87f')], default_time_limit_minutes=None, exam_review_rules='', hide_after_due=False, is_entrance_exam=False, is_onboarding_exam=False, is_practice_exam=False, is_proctored_enabled=False, is_time_limited=False, position=None, xml_attributes={}, advanced_modules=[], advertised_start=None, allow_anonymous=True, allow_anonymous_to_peers=False, allow_proctoring_opt_out=False, allow_public_wiki_access=False, allow_unsupported_xblocks=False, announcement=None, banner_image='images_course_image.jpg', catalog_visibility='both', ccx_connector='', cert_html_view_enabled=True, cert_html_view_overrides={}, cert_name_long='', cert_name_short='', certificate_available_date=datetime.datetime(2021, 1, 3, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x7f0015275550>), certificates={}, certificates_display_behavior='end', certificates_show_before_end=False, cohort_config={}, cosmetic_display_price=0, course_image='ora-monitor.jpg', course_survey_name=None, course_survey_required=False, course_visibility='private', course_wide_css=[], course_wide_js=[], create_zendesk_tickets=True, css_class='', disable_progress_graph=False, discussion_blackouts=[], discussion_link=None, discussion_sort_alpha=False, discussion_topics={'General': {'id': 'course'}}, discussions_settings={'enable_in_context': True, 'enable_graded_units': False, 'unit_level_visibility': False}, display_coursenumber='', display_organization=None, due_date_display_format=None, enable_ccx=False, enable_proctored_exams=False, enable_subsection_gating=False, enable_timed_exams=True, end=datetime.datetime(2022, 1, 1, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x7f00152755b0>), end_of_course_survey_url=None, enrollment_domain=None, enrollment_end=datetime.datetime(2022, 1, 1, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x7f00152755b0>), enrollment_start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<bson.tz_util.FixedOffset object at 0x7f00152755b0>), entrance_exam_enabled=False, entrance_exam_id=None, entrance_exam_minimum_score_pct=65, grading_policy={'GRADER': [{'type': 'Homework', 'short_label': 'HW', 'min_count': 12, 'drop_count': 2, 'weight': 0.15}, {'type': 'Lab', 'min_count': 12, 'drop_count': 2, 'weight': 0.15}, {'type': 'Midterm Exam', 'short_label': 'Midterm', 'min_count': 1, 'drop_count': 0, 'weight': 0.3}, {'type': 'Final Exam', 'short_label': 'Final', 'min_count': 1, 'drop_count': 0, 'weight': 0.4}], 'GRADE_CUTOFFS': {'Pass': 0.5}}, hide_progress_tab=None, highlights_enabled_for_messaging=False, html_textbooks=[], info_sidebar_name='Course Handouts', instructor_info={'instructors': []}, invitation_only=False, is_new=None, issue_badges=True, language='en', learning_info=[], lti_passports=[], max_student_enrollments_allowed=None, minimum_grade_credit=0.8, mobile_available=False, no_grade=Fa… <trimmed 5315 bytes string>\n | \n
| course_id | \n\n 'course-v1:DevX+ORA101+T12020'\n | \n
| course_key | \n\n CourseLocator('DevX', 'ORA101', 'T12020', None, None)\n | \n
| descriptor | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
| error_msg | \n\n None\n | \n
| files | \n\n {}\n | \n
| handler | \n\n 'staff_assess'\n | \n
| handler_instance | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
| handler_method | \n\n <bound method verify_assessment_parameters.<locals>.verify_and_call of <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open … <trimmed 4218 bytes string>\n | \n
| instance | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
| nr_tx_name | \n\n 'OpenAssessmentBlockWithMixins.staff_assess'\n | \n
| req | \n\n <DjangoWebobRequest at 0x7f00151f7e80 POST http://localhost:18000/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess>\n | \n
| request | \n\n <WSGIRequest: POST '/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess'>\n | \n
| suffix | \n\n None\n | \n
| tracking_context | \n\n {'module': {'display_name': 'Open Response Assessment',\n 'usage_key': 'block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69'}}\n | \n
| tracking_context_name | \n\n 'module_callback_handler'\n | \n
| usage_id | \n\n 'block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69'\n | \n
| usage_key | \n\n BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'openassessment', '8c235f76c46948ec80c9d59bf5686d69')\n | \n
| will_recheck_access | \n\n False\n | \n
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/xblock/mixins.py, line 84, in handle\n The wrapped function must return a `webob.Response` object.\n
"""\n
func._is_xblock_handler = True # pylint: disable=protected-access\n
return func\n
def handle(self, handler_name, request, suffix=''):\n
"""Handle `request` with this block's runtime."""\n
return self.runtime.handle(self, handler_name, request, suffix)\n …\n
class RuntimeServicesMixin:\n
"""\n
This mixin provides all of the machinery needed for an XBlock-style object\n
to declare dependencies on particular runtime services.\n
| Variable | \nValue | \n
|---|---|
| handler_name | \n\n 'staff_assess'\n | \n
| request | \n\n <DjangoWebobRequest at 0x7f00151f7e80 POST http://localhost:18000/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess>\n | \n
| self | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
| suffix | \n\n None\n | \n
/edx/app/edxapp/edx-platform/common/lib/xmodule/xmodule/x_module.py, line 1458, in handle\n getattr(block, 'location', ''),\n
)\n
def handle(self, block, handler_name, request, suffix=''): # lint-amnesty, pylint: disable=missing-function-docstring\n
start_time = time.time()\n
try:\n
status = "success"\n
return super().handle(block, handler_name, request, suffix=suffix)\n …\n
except:\n
status = "failure"\n
raise\n
finally:\n
| Variable | \nValue | \n
|---|---|
| __class__ | \n\n <class 'xmodule.x_module.MetricsMixin'>\n | \n
| block | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
| course_id | \n\n CourseLocator('DevX', 'ORA101', 'T12020', None, None)\n | \n
| duration | \n\n 1.3147268295288086\n | \n
| end_time | \n\n 1641920616.8631818\n | \n
| handler_name | \n\n 'staff_assess'\n | \n
| request | \n\n <DjangoWebobRequest at 0x7f00151f7e80 POST http://localhost:18000/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess>\n | \n
| self | \n\n LmsModuleSystem{'request_token': '695f6868730011eca52d0242ac120009', 'id_reader': <xmodule.modulestore.split_mongo.id_manager.SplitMongoIdManager object at 0x7f0014943e80>, '_services': {'fs': File system object, 'field-data': LmsFieldData(ReadOnlyFieldData(<edx_when.field_data.DateLookupFieldData object at 0x7f001521a820>), KvsFieldData(<lms.djangoapps.courseware.model_data.DjangoKeyValueStore object at 0x7f001531fd30>)), 'mako': <common.djangoapps.edxmako.services.MakoService object at 0x7f0015abe370>, 'user': <common.djangoapps.xblock_django.user_service.DjangoXBlockUserService object at 0x7f0015abe460>, 'verification': <lms.djangoapps.verify_student.services.XBlockVerificationService object at 0x7f00153edb50>, 'proctoring': <edx_proctoring.services.ProctoringService object at 0x7f0004e598e0>, 'milestones': <milestones.services.MilestonesService object at 0x7f0004e59fa0>, 'credit': <openedx.core.djangoapps.credit.services.CreditService object at 0x7f00153ed4c0>, 'bookmarks': <openedx.core.djangoapps.bookmarks.services.BookmarksService object at 0x7f00153ed040>, 'gating': <openedx.core.lib.gating.services.GatingService object at 0x7f00153c83d0>, 'grade_utils': <lms.djangoapps.grades.util_services.GradesUtilService object at 0x7f001529dc10>, 'user_state': <lms.djangoapps.courseware.services.UserStateService object at 0x7f001529d310>, 'content_type_gating': <openedx.features.content_type_gating.services.ContentTypeGatingService object at 0x7f001529d610>, 'xqueue': <capa.xqueue_interface.XQueueService object at 0x7f001521a0d0>, 'completion': <completion.services.CompletionService object at 0x7f001529de80>, 'i18n': <class 'xmodule.modulestore.django.ModuleI18nService'>, 'library_tools': <xmodule.library_tools.LibraryToolsService object at 0x7f001507b6a0>, 'partitions': <xmodule.partitions.partitions_service.PartitionService object at 0x7f001529d460>, 'settings': <xmodule.services.SettingsService object at 0x7f001529dbe0>, 'user_tags': <lms.djangoapps.lms_xblock.runtime.UserTagsService object at 0x7f001529d5e0>, 'teams': <lms.djangoapps.teams.services.TeamsService object at 0x7f001529d040>, 'teams_configuration': <xmodule.services.TeamsConfigurationService object at 0x7f001529d970>, 'call_to_action': <openedx.core.lib.xblock_services.call_to_action.CallToActionService object at 0x7f001529dca0>}, '_deprecated_per_instance_field_data': None, 'default_class': None, 'select': None, '_deprecated_per_instance_user_id': None, 'mixologist': <xblock.runtime.Mixologist object at 0x7f0015290250>, 'id_generator': <xmodule.modulestore.split_mongo.id_manager.SplitMongoIdManager object at 0x7f0014943e80>, 'wrappers': [functools.partial(<function wrap_with_license at 0x7f001f79bb80>, mako_service=<common.djangoapps.edxmako.services.MakoService object at 0x7f0015abe370>), functools.partial(<function wrap_xblock at 0x7f001ae00e50>, 'LmsRuntime', extra_data={'course-id': 'course-v1:DevX+ORA101+T12020'}, usage_id_serializer=<function get_module_system_for_user.<locals>.<lambda> at 0x7f0004190550>, request_token='695f6868730011eca52d0242ac120009'), functools.partial(<function replace_static_urls at 0x7f001ae0d8b0>, None, course_id=CourseLocator('DevX', 'ORA101', 'T12020', None, None), static_asset_path=''), functools.partial(<function replace_course_urls at 0x7f001ae0d820>, CourseLocator('DevX', 'ORA101', 'T12020', None, None)), functools.partial(<function replace_jump_to_id_urls at 0x7f001ae00f70>, CourseLocator('DevX', 'ORA101', 'T12020', None, None), '/courses/course-v1:DevX+ORA101+T12020/jump_to_id/'), functools.partial(<function display_access_messages at 0x7f00194b7700>, <User: staff>), functools.partial(<function course_expiration_wrapper at 0x7f001dbbc790>, <User: staff>), functools.partial(<function offer_banner_wrapper at 0x7f00194e3820>, <User: staff>), functools.partial(<function add_staff_markup at 0x7f001ae0da60>, <User: staff>, False)], 'wrappers_asides': [], 'STATIC_URL': '/static/', 'track_function': <function make_track_function.<locals>.function at 0x7f000419ad30>, 'filestore': OSFS('/edx/var/edxapp/data/DevX/ORA101/T12020'),… <trimmed 5891 bytes string>\n | \n
| start_time | \n\n 1641920615.548455\n | \n
| status | \n\n 'failure'\n | \n
| suffix | \n\n None\n | \n
| tags | \n\n ['handler_name:staff_assess',\n 'action:handle',\n 'action_status:failure',\n 'course_id:course-v1:DevX+ORA101+T12020',\n 'block_type:openassessment',\n 'block_family:xblock.v1']\n | \n
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/xblock/runtime.py, line 1081, in handle\n :param request: The request to handle\n
:type request: webob.Request\n
:param suffix: The remainder of the url, after the handler url prefix, if available\n
"""\n
handler = getattr(block, handler_name, None)\n
if handler and getattr(handler, '_is_xblock_handler', False):\n
# Cache results of the handler call for later saving\n
results = handler(request, suffix)\n …\n
else:\n
fallback_handler = getattr(block, "fallback_handler", None)\n
if fallback_handler and getattr(fallback_handler, '_is_xblock_handler', False):\n
# Cache results of the handler call for later saving\n
results = fallback_handler(handler_name, request, suffix)\n
else:\n
| Variable | \nValue | \n
|---|---|
| block | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
| handler | \n\n <bound method verify_assessment_parameters.<locals>.verify_and_call of <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open … <trimmed 4218 bytes string>\n | \n
| handler_name | \n\n 'staff_assess'\n | \n
| request | \n\n <DjangoWebobRequest at 0x7f00151f7e80 POST http://localhost:18000/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess>\n | \n
| self | \n\n LmsModuleSystem{'request_token': '695f6868730011eca52d0242ac120009', 'id_reader': <xmodule.modulestore.split_mongo.id_manager.SplitMongoIdManager object at 0x7f0014943e80>, '_services': {'fs': File system object, 'field-data': LmsFieldData(ReadOnlyFieldData(<edx_when.field_data.DateLookupFieldData object at 0x7f001521a820>), KvsFieldData(<lms.djangoapps.courseware.model_data.DjangoKeyValueStore object at 0x7f001531fd30>)), 'mako': <common.djangoapps.edxmako.services.MakoService object at 0x7f0015abe370>, 'user': <common.djangoapps.xblock_django.user_service.DjangoXBlockUserService object at 0x7f0015abe460>, 'verification': <lms.djangoapps.verify_student.services.XBlockVerificationService object at 0x7f00153edb50>, 'proctoring': <edx_proctoring.services.ProctoringService object at 0x7f0004e598e0>, 'milestones': <milestones.services.MilestonesService object at 0x7f0004e59fa0>, 'credit': <openedx.core.djangoapps.credit.services.CreditService object at 0x7f00153ed4c0>, 'bookmarks': <openedx.core.djangoapps.bookmarks.services.BookmarksService object at 0x7f00153ed040>, 'gating': <openedx.core.lib.gating.services.GatingService object at 0x7f00153c83d0>, 'grade_utils': <lms.djangoapps.grades.util_services.GradesUtilService object at 0x7f001529dc10>, 'user_state': <lms.djangoapps.courseware.services.UserStateService object at 0x7f001529d310>, 'content_type_gating': <openedx.features.content_type_gating.services.ContentTypeGatingService object at 0x7f001529d610>, 'xqueue': <capa.xqueue_interface.XQueueService object at 0x7f001521a0d0>, 'completion': <completion.services.CompletionService object at 0x7f001529de80>, 'i18n': <class 'xmodule.modulestore.django.ModuleI18nService'>, 'library_tools': <xmodule.library_tools.LibraryToolsService object at 0x7f001507b6a0>, 'partitions': <xmodule.partitions.partitions_service.PartitionService object at 0x7f001529d460>, 'settings': <xmodule.services.SettingsService object at 0x7f001529dbe0>, 'user_tags': <lms.djangoapps.lms_xblock.runtime.UserTagsService object at 0x7f001529d5e0>, 'teams': <lms.djangoapps.teams.services.TeamsService object at 0x7f001529d040>, 'teams_configuration': <xmodule.services.TeamsConfigurationService object at 0x7f001529d970>, 'call_to_action': <openedx.core.lib.xblock_services.call_to_action.CallToActionService object at 0x7f001529dca0>}, '_deprecated_per_instance_field_data': None, 'default_class': None, 'select': None, '_deprecated_per_instance_user_id': None, 'mixologist': <xblock.runtime.Mixologist object at 0x7f0015290250>, 'id_generator': <xmodule.modulestore.split_mongo.id_manager.SplitMongoIdManager object at 0x7f0014943e80>, 'wrappers': [functools.partial(<function wrap_with_license at 0x7f001f79bb80>, mako_service=<common.djangoapps.edxmako.services.MakoService object at 0x7f0015abe370>), functools.partial(<function wrap_xblock at 0x7f001ae00e50>, 'LmsRuntime', extra_data={'course-id': 'course-v1:DevX+ORA101+T12020'}, usage_id_serializer=<function get_module_system_for_user.<locals>.<lambda> at 0x7f0004190550>, request_token='695f6868730011eca52d0242ac120009'), functools.partial(<function replace_static_urls at 0x7f001ae0d8b0>, None, course_id=CourseLocator('DevX', 'ORA101', 'T12020', None, None), static_asset_path=''), functools.partial(<function replace_course_urls at 0x7f001ae0d820>, CourseLocator('DevX', 'ORA101', 'T12020', None, None)), functools.partial(<function replace_jump_to_id_urls at 0x7f001ae00f70>, CourseLocator('DevX', 'ORA101', 'T12020', None, None), '/courses/course-v1:DevX+ORA101+T12020/jump_to_id/'), functools.partial(<function display_access_messages at 0x7f00194b7700>, <User: staff>), functools.partial(<function course_expiration_wrapper at 0x7f001dbbc790>, <User: staff>), functools.partial(<function offer_banner_wrapper at 0x7f00194e3820>, <User: staff>), functools.partial(<function add_staff_markup at 0x7f001ae0da60>, <User: staff>, False)], 'wrappers_asides': [], 'STATIC_URL': '/static/', 'track_function': <function make_track_function.<locals>.function at 0x7f000419ad30>, 'filestore': OSFS('/edx/var/edxapp/data/DevX/ORA101/T12020'),… <trimmed 5891 bytes string>\n | \n
| suffix | \n\n None\n | \n
/edx/app/edxapp/venvs/edxapp/lib/python3.8/site-packages/xblock/mixins.py, line 63, in wrapper\n if request.method != "POST":\n
return JsonHandlerError(405, "Method must be POST").get_response(allow=["POST"])\n
try:\n
request_json = json.loads(request.body.decode('utf-8'))\n except ValueError:\n
return JsonHandlerError(400, "Invalid JSON").get_response()\n
try:\n
response = func(self, request_json, suffix)\n …\n
except JsonHandlerError as err:\n
return err.get_response()\n
if isinstance(response, Response):\n
return response\n
else:\n
return Response(json.dumps(response), content_type='application/json', charset='utf8')\n
| Variable | \nValue | \n
|---|---|
| func | \n\n <function verify_assessment_parameters.<locals>.verify_and_call at 0x7f0016b16e50>\n | \n
| request | \n\n <DjangoWebobRequest at 0x7f00151f7e80 POST http://localhost:18000/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess>\n | \n
| request_json | \n\n {'assess_type': 'full-grade',\n 'criterion_feedback': {'Ideas': 'did alright'},\n 'options_selected': {'Content': 'Good', 'Ideas': 'Good'},\n 'overall_feedback': 'was okay',\n 'submission_uuid': '{{submission_uuid}}'}\n | \n
| self | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
| suffix | \n\n None\n | \n
/edx/src/edx-ora2/openassessment/xblock/staff_area_mixin.py, line 78, in _wrapped\n "STUDENT_GRADE": xblock._("You do not have permission to access ORA staff grading."),\n }\n
if not xblock.is_course_staff and with_json_handler:\n
return {"success": False, "msg": permission_errors[error_key]}\n elif not xblock.is_course_staff or xblock.in_studio_preview:\n
return xblock.render_error(permission_errors[error_key])\n
return func(xblock, *args, **kwargs)\n …\n
return _wrapped\n
return _decorator\n
class StaffAreaMixin:\n
"""\n
| Variable | \nValue | \n
|---|---|
| args | \n\n ({'assess_type': 'full-grade',\n 'criterion_feedback': {'Ideas': 'did alright'},\n 'options_selected': {'Content': 'Good', 'Ideas': 'Good'},\n 'overall_feedback': 'was okay',\n 'submission_uuid': '{{submission_uuid}}'},\n None)\n | \n
| error_key | \n\n 'STUDENT_INFO'\n | \n
| func | \n\n <function verify_assessment_parameters.<locals>.verify_and_call at 0x7f0016b16dc0>\n | \n
| kwargs | \n\n {}\n | \n
| permission_errors | \n\n {'STAFF_AREA': 'You do not have permission to access the ORA staff area',\n 'STUDENT_GRADE': 'You do not have permission to access ORA staff grading.',\n 'STUDENT_INFO': 'You do not have permission to access ORA learner '\n 'information.'}\n | \n
| with_json_handler | \n\n False\n | \n
| xblock | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
/edx/src/edx-ora2/openassessment/xblock/data_conversion.py, line 282, in verify_and_call\n def verify_and_call(instance, data, suffix):\n
""" Inner Method. """\n
# Validate the request\n
msg = _verify_assessment_data(instance._, data)\n
if msg:\n
return {'success': False, 'msg': msg}\n return func(instance, data, suffix)\n …\n
return verify_and_call\n
def verify_multiple_assessment_parameters(func):\n
"""\n
Verify that the wrapped function receives the required parameters.\n
| Variable | \nValue | \n
|---|---|
| data | \n\n {'assess_type': 'full-grade',\n 'criterion_feedback': {'Ideas': 'did alright'},\n 'options_selected': {'Content': 'Good', 'Ideas': 'Good'},\n 'overall_feedback': 'was okay',\n 'submission_uuid': '{{submission_uuid}}'}\n | \n
| func | \n\n <function StaffAssessmentMixin.staff_assess at 0x7f0016b16d30>\n | \n
| instance | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
| msg | \n\n None\n | \n
| suffix | \n\n None\n | \n
/edx/src/edx-ora2/openassessment/xblock/staff_assessment_mixin.py, line 94, in staff_assess\n @XBlock.json_handler\n
@require_course_staff("STUDENT_INFO")\n @verify_assessment_parameters\n
def staff_assess(self, data, suffix=''): # pylint: disable=unused-argument\n
"""\n
Create a staff assessment from a staff submission.\n
"""\n
success, err_msg = self.do_staff_assessment(data)\n …\n
return {'success': success, 'msg': err_msg}\n @XBlock.json_handler\n
@require_course_staff("STUDENT_INFO")\n @verify_multiple_assessment_parameters\n
def bulk_staff_assess(self, data, suffix=''): # pylint: disable=unused-argument\n
| Variable | \nValue | \n
|---|---|
| data | \n\n {'assess_type': 'full-grade',\n 'criterion_feedback': {'Ideas': 'did alright'},\n 'options_selected': {'Content': 'Good', 'Ideas': 'Good'},\n 'overall_feedback': 'was okay',\n 'submission_uuid': '{{submission_uuid}}'}\n | \n
| self | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
| suffix | \n\n None\n | \n
/edx/src/edx-ora2/openassessment/xblock/staff_assessment_mixin.py, line 63, in do_staff_assessment\n data['options_selected'],\n
clean_criterion_feedback(self.rubric_criteria, data['criterion_feedback']),\n
data['overall_feedback'],\n
create_rubric_dict(self.prompts, self.rubric_criteria_with_labels)\n
)\n
assess_type = data.get('assess_type', 'regrade')\n self.publish_assessment_event("openassessmentblock.staff_assess", assessment, type=assess_type)\n workflow_api.update_from_assessments(\n …\n
assessment["submission_uuid"],\n
None,\n
override_submitter_requirements=(assess_type == 'regrade')\n
)\n
except StaffAssessmentRequestError:\n
logger.warning(\n
| Variable | \nValue | \n
|---|---|
| assess_type | \n\n 'full-grade'\n | \n
| assessment | \n\n {'feedback': 'was okay',\n 'id': 45,\n 'parts': [{'criterion': {'label': 'Ideas',\n 'name': 'Ideas',\n 'options': [OrderedDict([('order_num', 0),\n ('points', 0),\n ('name', 'Poor'),\n ('label', 'Poor'),\n ('explanation',\n 'Difficult for the reader '\n 'to discern the main '\n 'idea. Too brief or too '\n 'repetitive to establish '\n 'or maintain a focus.')]),\n OrderedDict([('order_num', 1),\n ('points', 3),\n ('name', 'Fair'),\n ('label', 'Fair'),\n ('explanation',\n 'Presents a unifying theme '\n 'or main idea, but may '\n 'include minor tangents. '\n 'Stays somewhat focused on '\n 'topic and task.')]),\n OrderedDict([('order_num', 2),\n ('points', 5),\n ('name', 'Good'),\n ('label', 'Good'),\n ('explanation',\n 'Presents a unifying theme '\n 'or main idea without '\n 'going off on tangents. '\n 'Stays completely focused '\n 'on topic and task.'),\n ('criterion',\n <Recursion on dict with id=139638062532032>)])],\n 'order_num': 0,\n 'points_possible': 5,\n 'prompt': 'Determine if there is a unifying theme or '\n 'main idea.'},\n 'feedback': 'did alright',\n 'option': OrderedDict([('order_num', 2),\n ('points', 5),\n ('name', 'Good'),\n ('label', 'Good'),\n ('explanation',\n 'Presents a unifying theme or main idea '\n 'without going off on tangents. Stays '\n 'completely focused on topic and task.'),\n ('criterion',\n {'label': 'Ideas',\n 'name': 'Ideas',\n 'options': [OrderedDict([('order_num', 0),\n ('points', 0),\n ('name', 'Poor'),\n ('label', 'Poor'),\n ('explanation',\n 'Difficult for '\n 'the reader to '\n 'discern the '\n … <trimmed 23690 bytes string>\n | \n
| data | \n\n {'assess_type': 'full-grade',\n 'criterion_feedback': {'Ideas': 'did alright'},\n 'options_selected': {'Content': 'Good', 'Ideas': 'Good'},\n 'overall_feedback': 'was okay',\n 'submission_uuid': '{{submission_uuid}}'}\n | \n
| self | \n\n <OpenAssessmentBlockWithMixins @8532 parent=BlockUsageLocator(CourseLocator('DevX', 'ORA101', 'T12020', None, None), 'vertical', 'd01f8519647b420d8f4e7cfdc0a5608b'), name=None, tags=[], display_name='Open Response Assessment', course_edit_method='Studio', days_early_for_beta=None, due=None, edxnotes=False, edxnotes_visibility=True, giturl=None, graceperiod=None, graded=False, group_access={}, in_entrance_exam=False, matlab_api_key=None, max_attempts=None, relative_weeks_due=None, rerandomize='never', self_paced=False, show_correctness='always', show_reset_button=False, showanswer='finished', start=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=<UTC>), static_asset_path='', use_latex_compiler=False, user_partitions=[UserPartition(id=1594940370, name='Content Groups', description='The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.', groups=[Group(id=262595704, name='Test')], scheme=<class 'openedx.core.djangoapps.course_groups.partition_scheme.CohortPartitionScheme'>, parameters={}, active=True)], video_auto_advance=False, video_bumper={}, video_speed_optimizations=True, visible_to_staff_only=False, xqa_key=None, chrome=None, default_tab=None, format=None, hide_from_toc=False, source_file=None, allow_file_upload=False, allow_latex=False, allow_multiple_files=True, editor_assessments_order=['student-training', 'peer-assessment', 'self-assessment', 'staff-assessment'], file_upload_response_raw='optional', file_upload_type_raw='pdf-and-image', has_saved=False, leaderboard_show=0, no_peers=False, prompt='<p>Staff assessment only, files optio...', prompts_type='html', rubric_assessments=[{'required': True, 'name': 'staff-assessment', 'enable_flexible_grading': False, 'due': None, 'start': None}], rubric_criteria=[{'label': 'Ideas', 'prompt': 'Determine if there is a unifying theme or main idea.', 'feedback': 'optional', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 3, 'explanation': 'Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 5, 'explanation': 'Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.', 'name': 'Good', 'order_num': 2}], 'name': 'Ideas', 'order_num': 0}, {'label': 'Content', 'prompt': 'Assess the content of the submission', 'feedback': 'disabled', 'options': [{'label': 'Poor', 'points': 0, 'explanation': 'Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.', 'name': 'Poor', 'order_num': 0}, {'label': 'Fair', 'points': 1, 'explanation': 'Includes little information and few or no details. Explores only one or two facets of the topic.', 'name': 'Fair', 'order_num': 1}, {'label': 'Good', 'points': 3, 'explanation': 'Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.', 'name': 'Good', 'order_num': 2}, {'label': 'Excellent', 'points': 5, 'explanation': 'Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.', 'name': 'Excellent', 'order_num': 3}], 'name': 'Content', 'order_num': 1}], rubric_feedback_default_text='I think that this response...', rubric_feedback_prompt='(Optional) What aspects of this respo...', saved_files_descriptions='', saved_files_names='', saved_files_sizes='', saved_response='', selected_teamset_id='', show_rubric_during_response=False, submission_due='2029-01-01T00:00:00+00:00', submission_start='2001-01-01T00:00:00+00:00', submission_uuid='95c8cc70-2ef6-4846-99e0-bae5f934b2ec', teams_enabled=False, text_response_editor='text', text_response_raw='required', title='Open Response Assessment', weight=None, white_listed_file_types=['pdf', 'gif… <trimmed 4146 bytes string>\n | \n
/edx/src/edx-ora2/openassessment/workflow/api.py, line 270, in update_from_assessments\n 'self': {\n 'complete': False\n
}\n
}\n
}\n
"""\n
workflow = _get_workflow_model(submission_uuid)\n …\n
try:\n
workflow.update_from_assessments(assessment_requirements, override_submitter_requirements)\n
logger.info(\n
"Updated workflow for submission UUID %s with requirements %s",\n
submission_uuid,\n
| Variable | \nValue | \n
|---|---|
| assessment_requirements | \n\n None\n | \n
| override_submitter_requirements | \n\n False\n | \n
| submission_uuid | \n\n '{{submission_uuid}}'\n | \n
/edx/src/edx-ora2/openassessment/workflow/api.py, line 371, in _get_workflow_model\n "Could not get assessment workflow with submission_uuid {} due to error: {}"\n .format(submission_uuid, exc)\n
)\n
logger.exception(err_msg)\n
raise AssessmentWorkflowInternalError(err_msg) from exc\n
if workflow is None:\n
raise AssessmentWorkflowNotFoundError(\n …\n
f"No assessment workflow matching submission_uuid {submission_uuid}"\n )\n
return workflow\n
| Variable | \nValue | \n
|---|---|
| submission_uuid | \n\n '{{submission_uuid}}'\n | \n
| workflow | \n\n None\n | \n
staff
\nNo GET data
\nNo POST data
\nNo FILES data
\n| Variable | \nValue | \n
|---|---|
| csrftoken | \n\n 'uqkKjVhUqNxftsFBVXmmO1cBzKKthLlEd2xcKKR2JvCz2v5Z4VHpqPGOVCqLGT91'\n | \n
| edx-user-info | \n\n ('{"version": 1, "username": "staff", "header_urls": {"logout": '\n '"http://localhost:18000/logout", "account_settings": '\n '"http://localhost:18000/account/settings", "learner_profile": '\n '"http://localhost:18000/u/staff", "resume_block": '\n '"http://localhost:18000/api/user/v1/account/login_session/"}, '\n '"user_image_urls": {"full": '\n '"http://localhost:18000/static/images/profiles/default_500.png", "large": '\n '"http://localhost:18000/static/images/profiles/default_120.png", "medium": '\n '"http://localhost:18000/static/images/profiles/default_50.png", "small": '\n '"http://localhost:18000/static/images/profiles/default_30.png"}}')\n | \n
| edxloggedin | \n\n 'true'\n | \n
| experiments_is_enterprise | \n\n 'false'\n | \n
| lms_sessionid | \n\n '3niydvnut3aj3beefl2lvj8epus0l3b9'\n | \n
| openedx-language-preference | \n\n 'en'\n | \n
| Variable | \nValue | \n
|---|---|
| BOK_CHOY_CMS_PORT | \n\n '18031'\n | \n
| BOK_CHOY_HOSTNAME | \n\n 'edx.devstack.lms'\n | \n
| BOK_CHOY_LMS_PORT | \n\n '18003'\n | \n
| BOTO_CONFIG | \n\n '/edx/app/edxapp/.boto'\n | \n
| CONFIGURATION_REPO | \n\n 'https://github.com/edx/configuration.git'\n | \n
| CONFIGURATION_VERSION | \n\n 'master'\n | \n
| CONFIG_ROOT | \n\n '/edx/app/edxapp'\n | \n
| CONTENT_LENGTH | \n\n '267'\n | \n
| CONTENT_TYPE | \n\n 'application/json'\n | \n
| CSRF_COOKIE | \n\n 'uqkKjVhUqNxftsFBVXmmO1cBzKKthLlEd2xcKKR2JvCz2v5Z4VHpqPGOVCqLGT91'\n | \n
| DJANGO_SETTINGS_MODULE | \n\n 'lms.envs.devstack_docker'\n | \n
| DJANGO_WATCHMAN_TIMEOUT | \n\n '30'\n | \n
| EDXAPP_TEST_MONGO_HOST | \n\n 'edx.devstack.mongo'\n | \n
| EDX_PLATFORM_SETTINGS | \n\n 'devstack_docker'\n | \n
| GATEWAY_INTERFACE | \n\n 'CGI/1.1'\n | \n
| HOME | \n\n '/root'\n | \n
| HOSTNAME | \n\n 'lms.devstack.edx'\n | \n
| HTTP_ACCEPT | \n\n '*/*'\n | \n
| HTTP_ACCEPT_ENCODING | \n\n 'gzip, deflate, br'\n | \n
| HTTP_ACCEPT_LANGUAGE | \n\n 'en;q=1.0'\n | \n
| HTTP_CACHE_CONTROL | \n\n 'no-cache'\n | \n
| HTTP_CONNECTION | \n\n 'keep-alive'\n | \n
| HTTP_COOKIE | \n\n ('csrftoken=uqkKjVhUqNxftsFBVXmmO1cBzKKthLlEd2xcKKR2JvCz2v5Z4VHpqPGOVCqLGT91; '\n 'edx-user-info="{\\\\"version\\\\": 1\\\\054 \\\\"username\\\\": \\\\"staff\\\\"\\\\054 '\n '\\\\"header_urls\\\\": {\\\\"logout\\\\": \\\\"http://localhost:18000/logout\\\\"\\\\054 '\n '\\\\"account_settings\\\\": \\\\"http://localhost:18000/account/settings\\\\"\\\\054 '\n '\\\\"learner_profile\\\\": \\\\"http://localhost:18000/u/staff\\\\"\\\\054 '\n '\\\\"resume_block\\\\": '\n '\\\\"http://localhost:18000/api/user/v1/account/login_session/\\\\"}\\\\054 '\n '\\\\"user_image_urls\\\\": {\\\\"full\\\\": '\n '\\\\"http://localhost:18000/static/images/profiles/default_500.png\\\\"\\\\054 '\n '\\\\"large\\\\": '\n '\\\\"http://localhost:18000/static/images/profiles/default_120.png\\\\"\\\\054 '\n '\\\\"medium\\\\": '\n '\\\\"http://localhost:18000/static/images/profiles/default_50.png\\\\"\\\\054 '\n '\\\\"small\\\\": '\n '\\\\"http://localhost:18000/static/images/profiles/default_30.png\\\\"}}"; '\n 'edxloggedin=true; experiments_is_enterprise=false; '\n 'lms_sessionid=1|3niydvnut3aj3beefl2lvj8epus0l3b9|5uYSHMN3Znql|ImViYWI2YWQyNWJhNzkyMTNmOThjMmI1YjRlOTM2MWQ1N2FjODk2MTY3MjI5ZDdiNDFhYzZmZmVkOTE3OGE5MTEi:1n71AV:lu0S_NhXlzrjrPZ9-n6M0-8Oy6Q; '\n 'openedx-language-preference=en')\n | \n
| HTTP_HOST | \n\n 'localhost:18000'\n | \n
| HTTP_POSTMAN_TOKEN | \n\n '********************'\n | \n
| HTTP_USER_AGENT | \n\n 'PostmanRuntime/7.28.4'\n | \n
| HTTP_X_CSRFTOKEN | \n\n '********************'\n | \n
| LANG | \n\n 'en_US.UTF-8'\n | \n
| LANGUAGE | \n\n 'en_US:en'\n | \n
| LC_ALL | \n\n 'en_US.UTF-8'\n | \n
| LMS_CFG | \n\n '/edx/etc/lms.yml'\n | \n
| NO_PYTHON_UNINSTALL | \n\n '1'\n | \n
| OPENEDX_RELEASE | \n\n 'master'\n | \n
| PATH | \n\n '/edx/app/edxapp/venvs/edxapp/bin:/edx/app/edxapp/edx-platform/bin:/edx/app/edxapp/edx-platform/node_modules/.bin:/edx/app/edxapp/nodeenvs/edxapp/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'\n | \n
| PATH_INFO | \n\n '/courses/course-v1:DevX+ORA101+T12020/xblock/block-v1:DevX+ORA101+T12020+type@openassessment+block@8c235f76c46948ec80c9d59bf5686d69/handler/staff_assess'\n | \n
| PWD | \n\n '/edx/app/edx_ansible/edx_ansible/docker/plays'\n | \n
| QUERY_STRING | \n\n ''\n | \n
| REMOTE_ADDR | \n\n '172.18.0.1'\n | \n
| REMOTE_HOST | \n\n ''\n | \n
| REQUEST_METHOD | \n\n 'POST'\n | \n
| REVISION_CFG | \n\n '/edx/etc/revisions.yml'\n | \n
| RUN_MAIN | \n\n 'true'\n | \n
| SCRIPT_NAME | \n\n ''\n | \n
| SELENIUM_BROWSER | \n\n 'firefox'\n | \n
| SELENIUM_HOST | \n\n 'edx.devstack.firefox'\n | \n
| SELENIUM_PORT | \n\n '4444'\n | \n
| SERVER_NAME | \n\n 'localhost:18000'\n | \n
| SERVER_PORT | \n\n '18000'\n | \n
| SERVER_PROTOCOL | \n\n 'HTTP/1.1'\n | \n
| SERVER_SOFTWARE | \n\n 'WSGIServer/0.2'\n | \n
| SERVICE_VARIANT | \n\n 'lms'\n | \n
| SHLVL | \n\n '2'\n | \n
| SKIP_WS_MIGRATIONS | \n\n '1'\n | \n
| STUDIO_CFG | \n\n '/edx/etc/studio.yml'\n | \n
| TERM | \n\n 'xterm'\n | \n
| TZ | \n\n 'UTC'\n | \n
| _ | \n\n '/edx/app/edxapp/venvs/edxapp/bin/python'\n | \n
| wsgi.errors | \n\n <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>\n | \n
| wsgi.file_wrapper | \n\n <class 'wsgiref.util.FileWrapper'>\n | \n
| wsgi.input | \n\n <django.core.handlers.wsgi.LimitedStream object at 0x7f0015272a00>\n | \n
| wsgi.multiprocess | \n\n False\n | \n
| wsgi.multithread | \n\n True\n | \n
| wsgi.run_once | \n\n False\n | \n
| wsgi.url_scheme | \n\n 'http'\n | \n
| wsgi.version | \n\n (1, 0)\n | \n
lms.envs.devstack_docker\n | Setting | \nValue | \n
|---|---|
| ABSOLUTE_URL_OVERRIDES | \n\n {}\n | \n
| ACCOUNT_MICROFRONTEND_URL | \n\n 'http://localhost:1997'\n | \n
| ACCOUNT_VISIBILITY_CONFIGURATION | \n\n {'admin_fields': ['account_privacy',\n 'profile_image',\n 'username',\n 'bio',\n 'course_certificates',\n 'country',\n 'date_joined',\n 'language_proficiencies',\n 'level_of_education',\n 'social_links',\n 'time_zone',\n 'accomplishments_shared',\n 'name',\n 'email',\n 'id',\n 'verified_name',\n 'extended_profile',\n 'gender',\n 'state',\n 'goals',\n 'is_active',\n 'last_login',\n 'mailing_address',\n 'requires_parental_consent',\n 'secondary_email',\n 'secondary_email_enabled',\n 'year_of_birth',\n 'phone_number',\n 'activation_key',\n 'pending_name_change'],\n 'bulk_shareable_fields': ['account_privacy',\n 'profile_image',\n 'username',\n 'bio',\n 'course_certificates',\n 'country',\n 'date_joined',\n 'language_proficiencies',\n 'level_of_education',\n 'social_links',\n 'time_zone',\n 'accomplishments_shared'],\n 'custom_shareable_fields': ['account_privacy',\n 'profile_image',\n 'username',\n 'bio',\n 'course_certificates',\n 'country',\n 'date_joined',\n 'language_proficiencies',\n 'level_of_education',\n 'social_links',\n 'time_zone',\n 'accomplishments_shared',\n 'name'],\n 'default_visibility': 'all_users',\n 'public_fields': ['account_privacy', 'profile_image', 'username']}\n | \n
| ACE_CHANNEL_DEFAULT_EMAIL | \n\n 'file_email'\n | \n
| ACE_CHANNEL_SAILTHRU_API_KEY | \n\n '********************'\n | \n
| ACE_CHANNEL_SAILTHRU_API_SECRET | \n\n '********************'\n | \n
| ACE_CHANNEL_SAILTHRU_DEBUG | \n\n True\n | \n
| ACE_CHANNEL_SAILTHRU_TEMPLATE_NAME | \n\n 'Automated Communication Engine Email'\n | \n
| ACE_CHANNEL_TRANSACTIONAL_EMAIL | \n\n 'file_email'\n | \n
| ACE_ENABLED_CHANNELS | \n\n ['file_email']\n | \n
| ACE_ENABLED_POLICIES | \n\n ['bulk_email_optout']\n | \n
| ACE_ROUTING_KEY | \n\n '********************'\n | \n
| ACTIVATION_EMAIL_FROM_ADDRESS | \n\n ''\n | \n
| ACTIVATION_EMAIL_SUPPORT_LINK | \n\n ''\n | \n
| ADMINS | \n\n ()\n | \n
| AFFILIATE_COOKIE_NAME | \n\n 'dev_affiliate_id'\n | \n
| ALLOWED_HOSTS | \n\n ['*', 'edx.devstack.lms:18000', 'preview.localhost:18000']\n | \n
| ALL_LANGUAGES | \n\n [['aa', 'Afar'],\n ['ab', 'Abkhazian'],\n ['af', 'Afrikaans'],\n ['ak', 'Akan'],\n ['sq', 'Albanian'],\n ['am', 'Amharic'],\n ['ar', 'Arabic'],\n ['an', 'Aragonese'],\n ['hy', 'Armenian'],\n ['as', 'Assamese'],\n ['av', 'Avaric'],\n ['ae', 'Avestan'],\n ['ay', 'Aymara'],\n ['az', 'Azerbaijani'],\n ['ba', 'Bashkir'],\n ['bm', 'Bambara'],\n ['eu', 'Basque'],\n ['be', 'Belarusian'],\n ['bn', 'Bengali'],\n ['bh', 'Bihari languages'],\n ['bi', 'Bislama'],\n ['bs', 'Bosnian'],\n ['br', 'Breton'],\n ['bg', 'Bulgarian'],\n ['my', 'Burmese'],\n ['ca', 'Catalan'],\n ['ch', 'Chamorro'],\n ['ce', 'Chechen'],\n ['zh', 'Chinese'],\n ['zh_HANS', 'Simplified Chinese'],\n ['zh_HANT', 'Traditional Chinese'],\n ['cu', 'Church Slavic'],\n ['cv', 'Chuvash'],\n ['kw', 'Cornish'],\n ['co', 'Corsican'],\n ['cr', 'Cree'],\n ['cs', 'Czech'],\n ['da', 'Danish'],\n ['dv', 'Divehi'],\n ['nl', 'Dutch'],\n ['dz', 'Dzongkha'],\n ['en', 'English'],\n ['eo', 'Esperanto'],\n ['et', 'Estonian'],\n ['ee', 'Ewe'],\n ['fo', 'Faroese'],\n ['fj', 'Fijian'],\n ['fi', 'Finnish'],\n ['fr', 'French'],\n ['fy', 'Western Frisian'],\n ['ff', 'Fulah'],\n ['ka', 'Georgian'],\n ['de', 'German'],\n ['gd', 'Gaelic'],\n ['ga', 'Irish'],\n ['gl', 'Galician'],\n ['gv', 'Manx'],\n ['el', 'Greek'],\n ['gn', 'Guarani'],\n ['gu', 'Gujarati'],\n ['ht', 'Haitian'],\n ['ha', 'Hausa'],\n ['he', 'Hebrew'],\n ['hz', 'Herero'],\n ['hi', 'Hindi'],\n ['ho', 'Hiri Motu'],\n ['hr', 'Croatian'],\n ['hu', 'Hungarian'],\n ['ig', 'Igbo'],\n ['is', 'Icelandic'],\n ['io', 'Ido'],\n ['ii', 'Sichuan Yi'],\n ['iu', 'Inuktitut'],\n ['ie', 'Interlingue'],\n ['ia', 'Interlingua'],\n ['id', 'Indonesian'],\n ['ik', 'Inupiaq'],\n ['it', 'Italian'],\n ['jv', 'Javanese'],\n ['ja', 'Japanese'],\n ['kl', 'Kalaallisut'],\n ['kn', 'Kannada'],\n ['ks', 'Kashmiri'],\n ['kr', 'Kanuri'],\n ['kk', 'Kazakh'],\n ['km', 'Central Khmer'],\n ['ki', 'Kikuyu'],\n ['rw', 'Kinyarwanda'],\n ['ky', 'Kirghiz'],\n ['kv', 'Komi'],\n ['kg', 'Kongo'],\n ['ko', 'Korean'],\n ['kj', 'Kuanyama'],\n ['ku', 'Kurdish'],\n ['lo', 'Lao'],\n ['la', 'Latin'],\n ['lv', 'Latvian'],\n ['li', 'Limburgan'],\n ['ln', 'Lingala'],\n ['lt', 'Lithuanian'],\n ['lb', 'Luxembourgish'],\n ['lu', 'Luba-Katanga'],\n ['lg', 'Ganda'],\n ['mk', 'Macedonian'],\n ['mh', 'Marshallese'],\n ['ml', 'Malayalam'],\n ['mi', 'Maori'],\n ['mr', 'Marathi'],\n ['ms', 'Malay'],\n ['mg', 'Malagasy'],\n ['mt', 'Maltese'],\n ['mn', 'Mongolian'],\n ['na', 'Nauru'],\n ['nv', 'Navajo'],\n ['nr', 'Ndebele, South'],\n ['nd', 'Ndebele, North'],\n ['ng', 'Ndonga'],\n ['ne', 'Nepali'],\n ['nn', 'Norwegian Nynorsk'],\n ['nb', 'Bokmål, Norwegian'],\n ['no', 'Norwegian'],\n ['ny', 'Chichewa'],\n ['oc', 'Occitan'],\n ['oj', 'Ojibwa'],\n ['or', 'Oriya'],\n ['om', 'Oromo'],\n ['os', 'Ossetian'],\n ['pa', 'Panjabi'],\n ['fa', 'Persian'],\n ['pi', 'Pali'],\n ['pl', 'Polish'],\n ['pt', 'Portuguese'],\n ['ps', 'Pushto'],\n ['qu', 'Quechua'],\n ['rm', 'Romansh'],\n ['ro', 'Romanian'],\n ['rn', 'Rundi'],\n ['ru', 'Russian'],\n ['sg', 'Sango'],\n ['sa', 'Sanskrit'],\n ['si', 'Sinhala'],\n ['sk', 'Slovak'],\n ['sl', 'Slovenian'],\n ['se', 'Northern Sami'],\n ['sm', 'Samoan'],\n ['sn', 'Shona'],\n ['sd', 'Sindhi'],\n ['so', 'Somali'],\n ['st', 'Sotho, Southern'],\n ['es', 'Spanish'],\n ['sc', 'Sardinian'],\n ['sr', 'Serbian'],\n ['ss', 'Swati'],\n ['su', 'Sundanese'],\n ['sw', 'Swahili'],\n ['sv', 'Swedish'],\n ['ty', 'Tahitian'],\n ['ta', 'Tamil'],\n ['tt', 'Tatar'],\n ['te', 'Telugu'],\n ['tg', 'Tajik'],\n ['tl', 'Tagalog'],\n ['th', 'Thai'],\n ['bo', 'Tibetan'],\n ['ti', 'Tigrinya'],\n ['to', 'Tonga (Tonga Islands)'],\n ['tn', 'Tswana'],\n ['ts', 'Tsonga'],\n ['tk', 'Turkmen'],\n ['tr', 'Turkish'],\n ['tw', 'Twi'],\n ['ug', 'Uighur'],\n ['uk', 'Ukrainian'],\n ['ur', 'Urdu'],\n ['uz', 'Uzbek'],\n ['ve', 'Venda'],\n ['vi', 'Vietnamese'],\n ['vo', 'Volapük'],\n ['cy', 'Welsh'],\n ['wa', 'Walloon'],\n ['wo', 'Wolof'],\n ['xh', 'Xhosa'],\n ['yi', 'Yiddish'],\n ['yo', 'Yoruba'],\n ['za', 'Zhuang'],\n ['zu', 'Zulu']]\n | \n
| ALTERNATE_ENV_TASKS | \n\n {}\n | \n
| ALTERNATE_QUEUES | \n\n ['edx.cms.core.default']\n | \n
| ALTERNATE_QUEUE_ENVS | \n\n ['cms']\n | \n
| ALTERNATE_WORKER_QUEUES | \n\n 'cms'\n | \n
| ANALYTICS_API_CLIENT | \n\n '********************'\n | \n
| ANALYTICS_API_KEY | \n\n '********************'\n | \n
| ANALYTICS_API_URL | \n\n '********************'\n | \n
| ANALYTICS_DASHBOARD_NAME | \n\n 'Your Platform Name Here Insights'\n | \n
| ANALYTICS_DASHBOARD_URL | \n\n None\n | \n
| API_ACCESS_FROM_EMAIL | \n\n '********************'\n | \n
| API_ACCESS_MANAGER_EMAIL | \n\n '********************'\n | \n
| API_DOCUMENTATION_URL | \n\n '********************'\n | \n
| APPEND_SLASH | \n\n True\n | \n
| APP_UPGRADE_CACHE_TIMEOUT | \n\n 3600\n | \n
| ASSET_IGNORE_REGEX | \n\n '(^\\\\._.*$)|(^\\\\.DS_Store$)|(^.*~$)'\n | \n
| ASSET_KEY_PATTERN | \n\n '********************'\n | \n
| AUTHENTICATION_BACKENDS | \n\n ['common.djangoapps.third_party_auth.dummy.DummyBackend',\n 'social_core.backends.google.GoogleOAuth2',\n 'social_core.backends.linkedin.LinkedinOAuth2',\n 'social_core.backends.facebook.FacebookOAuth2',\n 'social_core.backends.azuread.AzureADOAuth2',\n 'common.djangoapps.third_party_auth.appleid.AppleIdAuth',\n 'common.djangoapps.third_party_auth.identityserver3.IdentityServer3',\n 'common.djangoapps.third_party_auth.saml.SAMLAuthBackend',\n 'common.djangoapps.third_party_auth.lti.LTIAuthBackend',\n 'rules.permissions.ObjectPermissionBackend',\n 'openedx.core.djangoapps.oauth_dispatch.dot_overrides.backends.EdxRateLimitedAllowAllUsersModelBackend',\n 'bridgekeeper.backends.RulePermissionBackend']\n | \n
| AUTHN_MICROFRONTEND_DOMAIN | \n\n 'localhost:1999'\n | \n
| AUTHN_MICROFRONTEND_URL | \n\n 'http://localhost:1999'\n | \n
| AUTH_DOCUMENTATION_URL | \n\n 'http://course-catalog-api-guide.readthedocs.io/en/latest/authentication/index.html'\n | \n
| AUTH_PASSWORD_VALIDATORS | \n\n '********************'\n | \n
| AUTH_TOKENS | \n\n '********************'\n | \n
| AUTH_USER_MODEL | \n\n 'auth.User'\n | \n
| AWS_ACCESS_KEY_ID | \n\n '********************'\n | \n
| AWS_QUERYSTRING_AUTH | \n\n False\n | \n
| AWS_QUERYSTRING_EXPIRE | \n\n 315360000\n | \n
| AWS_S3_CUSTOM_DOMAIN | \n\n 'SET-ME-PLEASE (ex. bucket-name.s3.amazonaws.com)'\n | \n
| AWS_SECRET_ACCESS_KEY | \n\n '********************'\n | \n
| AWS_SES_REGION_ENDPOINT | \n\n 'email.us-east-1.amazonaws.com'\n | \n
| AWS_SES_REGION_NAME | \n\n 'us-east-1'\n | \n
| AWS_STORAGE_BUCKET_NAME | \n\n 'SET-ME-PLEASE (ex. bucket-name)'\n | \n
| BADGING_BACKEND | \n\n 'lms.djangoapps.badges.backends.badgr.BadgrBackend'\n | \n
| BADGR_BASE_URL | \n\n 'http://localhost:8005'\n | \n
| BADGR_ENABLE_NOTIFICATIONS | \n\n False\n | \n
| BADGR_ISSUER_SLUG | \n\n 'example-issuer'\n | \n
| BADGR_PASSWORD | \n\n '********************'\n | \n
| BADGR_TIMEOUT | \n\n 10\n | \n
| BADGR_TOKENS_CACHE_KEY | \n\n '********************'\n | \n
| BADGR_USERNAME | \n\n None\n | \n
| BASE_COOKIE_DOMAIN | \n\n 'localhost'\n | \n
| BLOCKSTORE_API_AUTH_TOKEN | \n\n '********************'\n | \n
| BLOCKSTORE_API_URL | \n\n '********************'\n | \n
| BLOCKSTORE_BUNDLE_CACHE_TIMEOUT | \n\n 3000\n | \n
| BLOCKSTORE_PUBLIC_URL_ROOT | \n\n 'http://localhost:18250'\n | \n
| BLOCK_STRUCTURES_SETTINGS | \n\n {'COURSE_PUBLISH_TASK_DELAY': 30,\n 'PRUNING_ACTIVE': False,\n 'TASK_DEFAULT_RETRY_DELAY': 30,\n 'TASK_MAX_RETRIES': 5}\n | \n
| BRANCH_IO_KEY | \n\n '********************'\n | \n
| BROKER_CONNECTION_TIMEOUT | \n\n 1\n | \n
| BROKER_HEARTBEAT | \n\n 60.0\n | \n
| BROKER_HEARTBEAT_CHECKRATE | \n\n 2\n | \n
| BROKER_POOL_LIMIT | \n\n 0\n | \n
| BROKER_TRANSPORT_OPTIONS | \n\n {'fanout_patterns': True, 'fanout_prefix': True}\n | \n
| BROKER_URL | \n\n 'redis://:@localhost/'\n | \n
| BROKER_USE_SSL | \n\n False\n | \n
| BUGS_EMAIL | \n\n 'bugs@example.com'\n | \n
| BULK_COURSE_EMAIL_LAST_LOGIN_ELIGIBILITY_PERIOD | \n\n None\n | \n
| BULK_EMAIL_DEFAULT_FROM_EMAIL | \n\n 'no-reply@example.com'\n | \n
| BULK_EMAIL_DEFAULT_RETRY_DELAY | \n\n 30\n | \n
| BULK_EMAIL_EMAILS_PER_TASK | \n\n 500\n | \n
| BULK_EMAIL_INFINITE_RETRY_CAP | \n\n 1000\n | \n
| BULK_EMAIL_JOB_SIZE_THRESHOLD | \n\n 100\n | \n
| BULK_EMAIL_LOG_SENT_EMAILS | \n\n False\n | \n
| BULK_EMAIL_MAX_RETRIES | \n\n 5\n | \n
| BULK_EMAIL_RETRY_DELAY_BETWEEN_SENDS | \n\n 0.02\n | \n
| BULK_EMAIL_ROUTING_KEY | \n\n '********************'\n | \n
| BULK_EMAIL_ROUTING_KEY_SMALL_JOBS | \n\n '********************'\n | \n
| CACHES | \n\n {'celery': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',\n 'KEY_FUNCTION': '********************',\n 'KEY_PREFIX': '********************',\n 'LOCATION': ['edx.devstack.memcached:11211'],\n 'TIMEOUT': '7200'},\n 'configuration': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',\n 'KEY_FUNCTION': '********************',\n 'KEY_PREFIX': '********************',\n 'LOCATION': ['edx.devstack.memcached:11211']},\n 'course_structure_cache': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',\n 'KEY_FUNCTION': '********************',\n 'KEY_PREFIX': '********************',\n 'LOCATION': ['edx.devstack.memcached:11211'],\n 'TIMEOUT': '7200'},\n 'default': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',\n 'KEY_FUNCTION': '********************',\n 'KEY_PREFIX': '********************',\n 'LOCATION': ['edx.devstack.memcached:11211'],\n 'VERSION': '1'},\n 'general': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',\n 'KEY_FUNCTION': '********************',\n 'KEY_PREFIX': '********************',\n 'LOCATION': ['edx.devstack.memcached:11211']},\n 'loc_cache': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',\n 'LOCATION': 'edx_location_mem_cache'},\n 'mongo_metadata_inheritance': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',\n 'KEY_FUNCTION': '********************',\n 'KEY_PREFIX': '********************',\n 'LOCATION': ['edx.devstack.memcached:11211'],\n 'TIMEOUT': 300},\n 'staticfiles': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',\n 'KEY_FUNCTION': '********************',\n 'KEY_PREFIX': '********************',\n 'LOCATION': ['edx.devstack.memcached:11211']}}\n | \n
| CACHE_MIDDLEWARE_ALIAS | \n\n 'default'\n | \n
| CACHE_MIDDLEWARE_KEY_PREFIX | \n\n '********************'\n | \n
| CACHE_MIDDLEWARE_SECONDS | \n\n 600\n | \n
| CALCULATOR_HELP_URL | \n\n 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/exercises_tools/calculator.html'\n | \n
| CAS_ATTRIBUTE_CALLBACK | \n\n ''\n | \n
| CAS_EXTRA_LOGIN_PARAMS | \n\n ''\n | \n
| CAS_SERVER_URL | \n\n ''\n | \n
| CCX_MAX_STUDENTS_ALLOWED | \n\n 200\n | \n
| CC_MERCHANT_NAME | \n\n 'Your Platform Name Here'\n | \n
| CELERYBEAT_SCHEDULE | \n\n {'refresh-saml-metadata': {'schedule': datetime.timedelta(days=1),\n 'task': 'common.djangoapps.third_party_auth.fetch_saml_metadata'}}\n | \n
| CELERYBEAT_SCHEDULER | \n\n 'celery.beat:PersistentScheduler'\n | \n
| CELERYD_HIJACK_ROOT_LOGGER | \n\n False\n | \n
| CELERYD_PREFETCH_MULTIPLIER | \n\n 1\n | \n
| CELERY_ALWAYS_EAGER | \n\n True\n | \n
| CELERY_BROKER_HOSTNAME | \n\n 'localhost'\n | \n
| CELERY_BROKER_PASSWORD | \n\n '********************'\n | \n
| CELERY_BROKER_TRANSPORT | \n\n 'redis'\n | \n
| CELERY_BROKER_USER | \n\n ''\n | \n
| CELERY_BROKER_USE_SSL | \n\n False\n | \n
| CELERY_BROKER_VHOST | \n\n ''\n | \n
| CELERY_CREATE_MISSING_QUEUES | \n\n True\n | \n
| CELERY_DEFAULT_EXCHANGE | \n\n 'edx.lms.core'\n | \n
| CELERY_DEFAULT_EXCHANGE_TYPE | \n\n 'direct'\n | \n
| CELERY_DEFAULT_QUEUE | \n\n 'edx.lms.core.default'\n | \n
| CELERY_DEFAULT_ROUTING_KEY | \n\n '********************'\n | \n
| CELERY_EVENT_QUEUE_TTL | \n\n None\n | \n
| CELERY_IGNORE_RESULT | \n\n False\n | \n
| CELERY_IMPORTS | \n\n ('poll.tasks',)\n | \n
| CELERY_MESSAGE_COMPRESSION | \n\n 'gzip'\n | \n
| CELERY_QUEUES | \n\n {'edx.cms.core.default': {},\n 'edx.lms.core.default': {},\n 'edx.lms.core.high': {},\n 'edx.lms.core.high_mem': {}}\n | \n
| CELERY_QUEUE_HA_POLICY | \n\n 'all'\n | \n
| CELERY_RESULT_BACKEND | \n\n 'django-cache'\n | \n
| CELERY_RESULT_SERIALIZER | \n\n 'json'\n | \n
| CELERY_ROUTES | \n\n 'openedx.core.lib.celery.routers.route_task'\n | \n
| CELERY_SEND_EVENTS | \n\n True\n | \n
| CELERY_SEND_TASK_SENT_EVENT | \n\n True\n | \n
| CELERY_STORE_ERRORS_EVEN_IF_IGNORED | \n\n True\n | \n
| CELERY_TASK_SERIALIZER | \n\n 'json'\n | \n
| CELERY_TIMEZONE | \n\n 'UTC'\n | \n
| CELERY_TRACK_STARTED | \n\n True\n | \n
| CERTIFICATE_DATE_FORMAT | \n\n '%B %-d, %Y'\n | \n
| CERTIFICATE_TEMPLATE_LANGUAGES | \n\n {'en': 'English', 'es': 'Español'}\n | \n
| CERT_NAME_LONG | \n\n 'Certificate of Achievement'\n | \n
| CERT_NAME_SHORT | \n\n 'Certificate'\n | \n
| CERT_QUEUE | \n\n 'certificates'\n | \n
| CHECKPOINT_PATTERN | \n\n '(?P<checkpoint_name>[^/]+)'\n | \n
| CHROME_DISABLE_SUBFRAME_DIALOG_SUPPRESSION_TOKEN | \n\n '********************'\n | \n
| CMS_BASE | \n\n 'localhost:18010'\n | \n
| CODE_JAIL | \n\n {'limits': {'CPU': 1,\n 'FSIZE': 1048576,\n 'PROXY': 0,\n 'REALTIME': 3,\n 'VMEM': 536870912},\n 'python_bin': '/edx/app/edxapp/venvs/edxapp-sandbox/bin/python',\n 'user': 'sandbox'}\n | \n
| CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT | \n\n 0.5\n | \n
| CODE_JAIL_REST_SERVICE_HOST | \n\n 'http://127.0.0.1:8550'\n | \n
| CODE_JAIL_REST_SERVICE_READ_TIMEOUT | \n\n 3.5\n | \n
| CODE_JAIL_REST_SERVICE_REMOTE_EXEC | \n\n 'common.lib.capa.capa.safe_exec.remote_exec.send_safe_exec_request_v0'\n | \n
| COMMENTS_SERVICE_KEY | \n\n '********************'\n | \n
| COMMENTS_SERVICE_URL | \n\n 'http://edx.devstack.forum:4567'\n | \n
| COMMON_ROOT | \n\n Path('/edx/app/edxapp/edx-platform/common')\n | \n
| COMPLETION_BY_VIEWING_DELAY_MS | \n\n 5000\n | \n
| COMPLETION_VIDEO_COMPLETE_PERCENTAGE | \n\n 0.95\n | \n
| COMPREHENSIVE_THEME_DIRS | \n\n ['']\n | \n
| COMPREHENSIVE_THEME_LOCALE_PATHS | \n\n []\n | \n
| CONFIG_FILE | \n\n '/edx/etc/lms.yml'\n | \n
| CONFIG_PREFIX | \n\n 'lms.'\n | \n
| CONTACT_EMAIL | \n\n 'info@example.com'\n | \n
| CONTACT_MAILING_ADDRESS | \n\n 'SET-ME-PLEASE'\n | \n
| CONTENTSTORE | \n\n {'ADDITIONAL_OPTIONS': {},\n 'DOC_STORE_CONFIG': {'authsource': '',\n 'collection': 'modulestore',\n 'connectTimeoutMS': 2000,\n 'db': 'edxapp',\n 'host': ['edx.devstack.mongo'],\n 'password': '********************',\n 'port': 27017,\n 'read_preference': 'SECONDARY_PREFERRED',\n 'replicaSet': '',\n 'socketTimeoutMS': 3000,\n 'ssl': False,\n 'user': 'edxapp'},\n 'ENGINE': 'xmodule.contentstore.mongo.MongoContentStore',\n 'OPTIONS': {'auth_source': '',\n 'db': 'edxapp',\n 'host': ['edx.devstack.mongo'],\n 'password': '********************',\n 'port': 27017,\n 'ssl': False,\n 'user': 'edxapp'}}\n | \n
| CONTENT_TYPE_GATE_GROUP_IDS | \n\n {'full_access': 2, 'limited_access': 1}\n | \n
| CONTEXT_PROCESSORS | \n\n ['django.template.context_processors.request',\n 'django.template.context_processors.static',\n 'django.template.context_processors.i18n',\n 'django.contrib.auth.context_processors.auth',\n 'django.template.context_processors.csrf',\n 'django.template.context_processors.media',\n 'django.template.context_processors.tz',\n 'django.contrib.messages.context_processors.messages',\n 'sekizai.context_processors.sekizai',\n 'common.djangoapps.edxmako.shortcuts.marketing_link_context_processor',\n 'lms.djangoapps.courseware.context_processor.user_timezone_locale_prefs',\n 'help_tokens.context_processor',\n 'openedx.core.djangoapps.site_configuration.context_processors.configuration_context',\n 'lms.djangoapps.mobile_api.context_processor.is_from_mobile_app',\n 'social_django.context_processors.backends',\n 'social_django.context_processors.login_redirect']\n | \n
| CORS_ALLOW_CREDENTIALS | \n\n True\n | \n
| CORS_ALLOW_HEADERS | \n\n ('accept',\n 'accept-encoding',\n 'authorization',\n 'content-type',\n 'dnt',\n 'origin',\n 'user-agent',\n 'x-csrftoken',\n 'x-requested-with',\n 'use-jwt-cookie')\n | \n
| CORS_ORIGIN_ALLOW_ALL | \n\n True\n | \n
| CORS_ORIGIN_WHITELIST | \n\n ()\n | \n
| COUNTRIES_FIRST | \n\n []\n | \n
| COUNTRIES_OVERRIDE | \n\n {'TW': 'Taiwan', 'XK': 'Kosovo'}\n | \n
| COURSES_API_CACHE_TIMEOUT | \n\n '********************'\n | \n
| COURSES_ROOT | \n\n Path('/edx/app/edxapp/data')\n | \n
| COURSES_WITH_UNSAFE_CODE | \n\n []\n | \n
| COURSE_ABOUT_VISIBILITY_PERMISSION | \n\n 'see_exists'\n | \n
| COURSE_BLOCKS_API_EXTRA_FIELDS | \n\n '********************'\n | \n
| COURSE_CATALOG_API_URL | \n\n '********************'\n | \n
| COURSE_CATALOG_URL_ROOT | \n\n 'http://edx.devstack.discovery:18381'\n | \n
| COURSE_CATALOG_VISIBILITY_PERMISSION | \n\n 'see_exists'\n | \n
| COURSE_DISCOVERY_MEANINGS | \n\n {'language': {'name': 'Language',\n 'terms': {'aa': 'Afar',\n 'ab': 'Abkhazian',\n 'ae': 'Avestan',\n 'af': 'Afrikaans',\n 'ak': 'Akan',\n 'am': 'Amharic',\n 'an': 'Aragonese',\n 'ar': 'Arabic',\n 'as': 'Assamese',\n 'av': 'Avaric',\n 'ay': 'Aymara',\n 'az': 'Azerbaijani',\n 'ba': 'Bashkir',\n 'be': 'Belarusian',\n 'bg': 'Bulgarian',\n 'bh': 'Bihari languages',\n 'bi': 'Bislama',\n 'bm': 'Bambara',\n 'bn': 'Bengali',\n 'bo': 'Tibetan',\n 'br': 'Breton',\n 'bs': 'Bosnian',\n 'ca': 'Catalan',\n 'ce': 'Chechen',\n 'ch': 'Chamorro',\n 'co': 'Corsican',\n 'cr': 'Cree',\n 'cs': 'Czech',\n 'cu': 'Church Slavic',\n 'cv': 'Chuvash',\n 'cy': 'Welsh',\n 'da': 'Danish',\n 'de': 'German',\n 'dv': 'Divehi',\n 'dz': 'Dzongkha',\n 'ee': 'Ewe',\n 'el': 'Greek',\n 'en': 'English',\n 'eo': 'Esperanto',\n 'es': 'Spanish',\n 'et': 'Estonian',\n 'eu': 'Basque',\n 'fa': 'Persian',\n 'ff': 'Fulah',\n 'fi': 'Finnish',\n 'fj': 'Fijian',\n 'fo': 'Faroese',\n 'fr': 'French',\n 'fy': 'Western Frisian',\n 'ga': 'Irish',\n 'gd': 'Gaelic',\n 'gl': 'Galician',\n 'gn': 'Guarani',\n 'gu': 'Gujarati',\n 'gv': 'Manx',\n 'ha': 'Hausa',\n 'he': 'Hebrew',\n 'hi': 'Hindi',\n 'ho': 'Hiri Motu',\n 'hr': 'Croatian',\n 'ht': 'Haitian',\n 'hu': 'Hungarian',\n 'hy': 'Armenian',\n 'hz': 'Herero',\n 'ia': 'Interlingua',\n 'id': 'Indonesian',\n 'ie': 'Interlingue',\n 'ig': 'Igbo',\n 'ii': 'Sichuan Yi',\n 'ik': 'Inupiaq',\n 'io': 'Ido',\n 'is': 'Icelandic',\n 'it': 'Italian',\n 'iu': 'Inuktitut',\n 'ja': 'Japanese',\n 'jv': 'Javanese',\n 'ka': 'Georgian',\n 'kg': 'Kongo',\n 'ki': 'Kikuyu',\n 'kj': 'Kuanyama',\n 'kk': 'Kazakh',\n 'kl': 'Kalaallisut',\n 'km': 'Central Khmer',\n 'kn': 'Kannada',\n 'ko': 'Korean',\n 'kr': 'Kanuri',\n 'ks': 'Kashmiri',\n 'ku': 'Kurdish',\n 'kv': 'Komi',\n 'kw': 'Cornish',\n 'ky': 'Kirghiz',\n 'la': 'Latin',\n 'lb': 'Luxembourgish',\n 'lg': 'Ganda',\n 'li': 'Limburgan',\n 'ln': 'Lingala',\n 'lo': 'Lao',\n 'lt': 'Lithuanian',\n 'lu': 'Luba-Katanga',\n 'lv': 'Latvian',\n 'mg': 'Malagasy',\n 'mh': 'Marshallese',\n 'mi': 'Maori',\n 'mk': 'Macedonian',\n 'ml': 'Malayalam',\n 'mn': 'Mongolian',\n 'mr': 'Marathi',\n 'ms': 'Malay',\n 'mt': 'Maltese',\n 'my': 'Burmese',\n 'na': 'Nauru',\n 'nb': 'Bokmål, Norwegian',\n 'nd': 'Ndebele, North',\n 'ne': 'Nepali',\n 'ng': 'Ndonga',\n 'nl': 'Dutch',\n 'nn': 'Norwegian Nynorsk',\n 'no': 'Norwegian',\n 'nr': 'Ndebele, South',\n 'nv': 'Navajo',\n 'ny': 'Chichewa',\n 'oc': 'Occitan',\n 'oj': 'Ojibwa',\n 'om': 'Oromo',\n 'or': 'Oriya',\n 'os': 'Ossetian',\n 'pa': 'Panjabi',\n 'pi': 'Pali',\n 'pl': 'Polish',\n 'ps': 'Pushto',\n 'pt': 'Portuguese',\n 'qu': 'Quechua',\n 'rm': 'Romansh',\n 'rn': 'Rundi',\n 'ro': 'Romanian',\n 'ru': 'Russian',\n 'rw': 'Kinyarwanda',\n 'sa': 'Sanskrit',\n 'sc': 'Sardinian',\n 'sd': 'Sindhi',\n 'se': 'Northern Sami',\n 'sg': 'Sango',\n 'si': 'Sinhala',\n 'sk': 'Slovak',\n 'sl': 'Slovenian',\n 'sm': 'Samoan',\n 'sn': 'Shona',\n 'so': 'Somali',\n 'sq': 'Albanian',\n 'sr': 'Serbian',\n 'ss': 'Swati',\n 'st': 'Sotho, Southern',\n 'su': 'Sundanese',\n 'sv': 'Swedish',\n 'sw': 'Swahili',\n 'ta': 'Tamil',\n 'te': 'Telugu',\n 'tg': 'Tajik',\n 'th': 'Thai',\n 'ti': 'Tigrinya',\n 'tk': 'Turkmen',\n 'tl': 'Tagalog',\n 'tn': 'Tswana',\n 'to': 'Tonga (Tonga Islands)',\n 'tr': 'Turkish',\n 'ts': 'Tsonga',\n 'tt': 'Tatar',\n 'tw': 'Twi',\n 'ty': 'Tahitian',\n 'ug': 'Uighur',\n 'uk': 'Ukrainian',\n 'ur': 'Urdu',\n 'uz': 'Uzbek',\n 've': 'Venda',\n 'vi': 'Vietnamese',\n 'vo': 'Volapük',\n 'wa': 'Walloon',\n 'wo': 'Wolof',\n 'xh': 'Xhosa',\n 'yi': 'Yiddish',\n 'yo': 'Yoruba',\n 'za': 'Zhuang',\n 'zh': 'Chinese',\n 'zh_HANS': 'Simplified Chinese',\n 'zh_HANT': 'Traditional Chinese',\n 'zu': 'Zulu'}},\n 'modes': {'name': 'Course Type',\n 'terms': {'honor': 'Honor', 'verified': 'Verified'}},\n 'org': {'name': 'Organization'}}\n | \n
| COURSE_ENROLLMENT_MODES | \n\n {'audit': {'display_name': 'Audit', 'id': 1, 'min_price': 0, 'slug': 'audit'},\n 'credit': {'display_name': 'Credit',\n 'id': 5,\n 'min_price': 0,\n 'slug': 'credit'},\n 'executive-education': {'display_name': 'Executive Education',\n 'id': 8,\n 'min_price': 1,\n 'slug': 'executive-educations'},\n 'honor': {'display_name': 'Honor', 'id': 6, 'min_price': 0, 'slug': 'honor'},\n 'masters': {'display_name': "Master's",\n 'id': 7,\n 'min_price': 0,\n 'slug': 'masters'},\n 'no-id-professional': {'display_name': 'No-Id-Professional',\n 'id': 4,\n 'min_price': 0,\n 'slug': 'no-id-professional'},\n 'professional': {'display_name': 'Professional',\n 'id': 3,\n 'min_price': 1,\n 'slug': 'professional'},\n 'verified': {'display_name': 'Verified',\n 'id': 2,\n 'min_price': 1,\n 'slug': 'verified'}}\n | \n
| COURSE_ID_PATTERN | \n\n '(?P<course_id>[^/+]+(/|\\\\+)[^/+]+(/|\\\\+)[^/?]+)'\n | \n
| COURSE_KEY_PATTERN | \n\n '********************'\n | \n
| COURSE_KEY_REGEX | \n\n '********************'\n | \n
| COURSE_LISTINGS | \n\n {}\n | \n
| COURSE_MEMBER_API_ENROLLMENT_LIMIT | \n\n '********************'\n | \n
| COURSE_MESSAGE_ALERT_DURATION_IN_DAYS | \n\n 14\n | \n
| COURSE_MODE_DEFAULTS | \n\n {'bulk_sku': None,\n 'currency': 'usd',\n 'description': None,\n 'expiration_datetime': None,\n 'min_price': 0,\n 'name': 'Audit',\n 'sku': None,\n 'slug': 'audit',\n 'suggested_prices': ''}\n | \n
| COURSE_OLX_VALIDATION_IGNORE_LIST | \n\n None\n | \n
| COURSE_OLX_VALIDATION_STAGE | \n\n 1\n | \n
| CREDENTIALS_GENERATION_ROUTING_KEY | \n\n '********************'\n | \n
| CREDENTIALS_INTERNAL_SERVICE_URL | \n\n 'http://edx.devstack.credentials:18150'\n | \n
| CREDENTIALS_PUBLIC_SERVICE_URL | \n\n 'http://localhost:18150'\n | \n
| CREDENTIALS_SERVICE_USERNAME | \n\n 'credentials_worker'\n | \n
| CREDIT_HELP_LINK_URL | \n\n ''\n | \n
| CREDIT_NOTIFICATION_CACHE_TIMEOUT | \n\n 18000\n | \n
| CREDIT_PROVIDER_SECRET_KEYS | \n\n '********************'\n | \n
| CREDIT_PROVIDER_TIMESTAMP_EXPIRATION | \n\n 900\n | \n
| CREDIT_TASK_DEFAULT_RETRY_DELAY | \n\n 30\n | \n
| CREDIT_TASK_MAX_RETRIES | \n\n 5\n | \n
| CROSS_DOMAIN_CSRF_COOKIE_DOMAIN | \n\n ''\n | \n
| CROSS_DOMAIN_CSRF_COOKIE_NAME | \n\n ''\n | \n
| CSRF_COOKIE_AGE | \n\n 31449600\n | \n
| CSRF_COOKIE_DOMAIN | \n\n None\n | \n
| CSRF_COOKIE_HTTPONLY | \n\n False\n | \n
| CSRF_COOKIE_NAME | \n\n 'csrftoken'\n | \n
| CSRF_COOKIE_PATH | \n\n '/'\n | \n
| CSRF_COOKIE_SAMESITE | \n\n 'Lax'\n | \n
| CSRF_COOKIE_SECURE | \n\n False\n | \n
| CSRF_FAILURE_VIEW | \n\n 'django.views.csrf.csrf_failure'\n | \n
| CSRF_HEADER_NAME | \n\n 'HTTP_X_CSRFTOKEN'\n | \n
| CSRF_TRUSTED_ORIGINS | \n\n []\n | \n
| CSRF_USE_SESSIONS | \n\n False\n | \n
| CSV_EXPIRATION_DAYS | \n\n 90\n | \n
| CUSTOM_PAGES_HELP_URL | \n\n 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_assets/pages.html#adding-custom-pages'\n | \n
| DASHBOARD_COURSE_LIMIT | \n\n None\n | \n
| DATABASES | \n\n {'default': {'ATOMIC_REQUESTS': True,\n 'AUTOCOMMIT': True,\n 'CONN_MAX_AGE': 0,\n 'ENGINE': 'django.db.backends.mysql',\n 'HOST': 'edx.devstack.mysql57',\n 'NAME': 'edxapp',\n 'OPTIONS': {'isolation_level': 'read committed'},\n 'PASSWORD': '********************',\n 'PORT': '3306',\n 'TEST': {'CHARSET': None,\n 'COLLATION': None,\n 'MIGRATE': True,\n 'MIRROR': None,\n 'NAME': None},\n 'TIME_ZONE': None,\n 'USER': 'edxapp001'},\n 'read_replica': {'ATOMIC_REQUESTS': False,\n 'AUTOCOMMIT': True,\n 'CONN_MAX_AGE': 0,\n 'ENGINE': 'django.db.backends.mysql',\n 'HOST': 'edx.devstack.mysql57',\n 'NAME': 'edxapp',\n 'OPTIONS': {'isolation_level': 'read committed'},\n 'PASSWORD': '********************',\n 'PORT': '3306',\n 'TEST': {'CHARSET': None,\n 'COLLATION': None,\n 'MIGRATE': True,\n 'MIRROR': None,\n 'NAME': None},\n 'TIME_ZONE': None,\n 'USER': 'edxapp001'},\n 'student_module_history': {'ATOMIC_REQUESTS': False,\n 'AUTOCOMMIT': True,\n 'CONN_MAX_AGE': 0,\n 'ENGINE': 'django.db.backends.mysql',\n 'HOST': 'edx.devstack.mysql57',\n 'NAME': 'edxapp_csmh',\n 'OPTIONS': {'isolation_level': 'read committed'},\n 'PASSWORD': '********************',\n 'PORT': '3306',\n 'TEST': {'CHARSET': None,\n 'COLLATION': None,\n 'MIGRATE': True,\n 'MIRROR': None,\n 'NAME': None},\n 'TIME_ZONE': None,\n 'USER': 'edxapp001'}}\n | \n
| DATABASE_ROUTERS | \n\n ['openedx.core.lib.django_courseware_routers.StudentModuleHistoryExtendedRouter',\n 'edx_django_utils.db.read_replica.ReadReplicaRouter']\n | \n
| DATADOG | \n\n {}\n | \n
| DATA_CONSENT_SHARE_CACHE_TIMEOUT | \n\n 28800\n | \n
| DATA_DIR | \n\n Path('/edx/var/edxapp')\n | \n
| DATA_UPLOAD_MAX_MEMORY_SIZE | \n\n None\n | \n
| DATA_UPLOAD_MAX_NUMBER_FIELDS | \n\n None\n | \n
| DATETIME_FORMAT | \n\n 'N j, Y, P'\n | \n
| DATETIME_INPUT_FORMATS | \n\n ['%Y-%m-%d %H:%M:%S',\n '%Y-%m-%d %H:%M:%S.%f',\n '%Y-%m-%d %H:%M',\n '%m/%d/%Y %H:%M:%S',\n '%m/%d/%Y %H:%M:%S.%f',\n '%m/%d/%Y %H:%M',\n '%m/%d/%y %H:%M:%S',\n '%m/%d/%y %H:%M:%S.%f',\n '%m/%d/%y %H:%M']\n | \n
| DATE_FORMAT | \n\n 'N j, Y'\n | \n
| DATE_INPUT_FORMATS | \n\n ['%Y-%m-%d',\n '%m/%d/%Y',\n '%m/%d/%y',\n '%b %d %Y',\n '%b %d, %Y',\n '%d %b %Y',\n '%d %b, %Y',\n '%B %d %Y',\n '%B %d, %Y',\n '%d %B %Y',\n '%d %B, %Y']\n | \n
| DCS_SESSION_COOKIE_SAMESITE | \n\n 'Lax'\n | \n
| DCS_SESSION_COOKIE_SAMESITE_FORCE_ALL | \n\n True\n | \n
| DEBUG | \n\n True\n | \n
| DEBUG_PROPAGATE_EXCEPTIONS | \n\n False\n | \n
| DEBUG_TOOLBAR_CONFIG | \n\n {'SHOW_TOOLBAR_CALLBACK': 'lms.envs.devstack.should_show_debug_toolbar'}\n | \n
| DEBUG_TOOLBAR_PANELS | \n\n ('debug_toolbar.panels.versions.VersionsPanel',\n 'debug_toolbar.panels.timer.TimerPanel',\n 'debug_toolbar.panels.settings.SettingsPanel',\n 'debug_toolbar.panels.headers.HeadersPanel',\n 'debug_toolbar.panels.request.RequestPanel',\n 'debug_toolbar.panels.sql.SQLPanel',\n 'debug_toolbar.panels.signals.SignalsPanel',\n 'debug_toolbar.panels.logging.LoggingPanel',\n 'debug_toolbar.panels.history.HistoryPanel')\n | \n
| DEBUG_TOOLBAR_PATCH_SETTINGS | \n\n False\n | \n
| DEBUG_TRACK_LOG | \n\n False\n | \n
| DECIMAL_SEPARATOR | \n\n '.'\n | \n
| DEFAULT_AUTO_FIELD | \n\n 'django.db.models.AutoField'\n | \n
| DEFAULT_CHARSET | \n\n 'utf-8'\n | \n
| DEFAULT_COURSE_ABOUT_IMAGE_URL | \n\n 'images/pencils.jpg'\n | \n
| DEFAULT_COURSE_VISIBILITY_IN_CATALOG | \n\n 'both'\n | \n
| DEFAULT_EMAIL_LOGO_URL | \n\n 'https://edx-cdn.org/v3/default/logo.png'\n | \n
| DEFAULT_ENTERPRISE_API_URL | \n\n '********************'\n | \n
| DEFAULT_ENTERPRISE_CONSENT_API_URL | \n\n '********************'\n | \n
| DEFAULT_EXCEPTION_REPORTER | \n\n 'django.views.debug.ExceptionReporter'\n | \n
| DEFAULT_EXCEPTION_REPORTER_FILTER | \n\n 'django.views.debug.SafeExceptionReporterFilter'\n | \n
| DEFAULT_FEEDBACK_EMAIL | \n\n 'feedback@example.com'\n | \n
| DEFAULT_FILE_STORAGE | \n\n 'django.core.files.storage.FileSystemStorage'\n | \n
| DEFAULT_FROM_EMAIL | \n\n 'registration@example.com'\n | \n
| DEFAULT_GROUPS | \n\n []\n | \n
| DEFAULT_HASHING_ALGORITHM | \n\n 'sha1'\n | \n
| DEFAULT_INDEX_TABLESPACE | \n\n ''\n | \n
| DEFAULT_JWT_ISSUER | \n\n {'AUDIENCE': 'lms-key',\n 'ISSUER': 'http://edx.devstack.lms:18000/oauth2',\n 'SECRET_KEY': '********************'}\n | \n
| DEFAULT_MOBILE_AVAILABLE | \n\n False\n | \n
| DEFAULT_PRIORITY_QUEUE | \n\n 'edx.lms.core.default'\n | \n
| DEFAULT_SITE_THEME | \n\n ''\n | \n
| DEFAULT_TABLESPACE | \n\n ''\n | \n
| DEFAULT_TEMPLATE_ENGINE | \n\n {'APP_DIRS': False,\n 'BACKEND': 'django.template.backends.django.DjangoTemplates',\n 'DIRS': [Path('/edx/app/edxapp/edx-platform/lms/templates'),\n Path('/edx/app/edxapp/edx-platform/common/templates'),\n Path('/edx/app/edxapp/edx-platform/common/lib/capa/capa/templates'),\n Path('/edx/app/edxapp/edx-platform/common/djangoapps/pipeline_mako/templates'),\n Path('/edx/app/edxapp/edx-platform/common/static')],\n 'NAME': 'django',\n 'OPTIONS': {'context_processors': ['django.template.context_processors.request',\n 'django.template.context_processors.static',\n 'django.template.context_processors.i18n',\n 'django.contrib.auth.context_processors.auth',\n 'django.template.context_processors.csrf',\n 'django.template.context_processors.media',\n 'django.template.context_processors.tz',\n 'django.contrib.messages.context_processors.messages',\n 'sekizai.context_processors.sekizai',\n 'common.djangoapps.edxmako.shortcuts.marketing_link_context_processor',\n 'lms.djangoapps.courseware.context_processor.user_timezone_locale_prefs',\n 'help_tokens.context_processor',\n 'openedx.core.djangoapps.site_configuration.context_processors.configuration_context',\n 'lms.djangoapps.mobile_api.context_processor.is_from_mobile_app',\n 'social_django.context_processors.backends',\n 'social_django.context_processors.login_redirect'],\n 'debug': True,\n 'loaders': ['openedx.core.djangoapps.theming.template_loaders.ThemeTemplateLoader',\n 'common.djangoapps.edxmako.makoloader.MakoFilesystemLoader',\n 'common.djangoapps.edxmako.makoloader.MakoAppDirectoriesLoader']}}\n | \n
| DEFAULT_TEMPLATE_ENGINE_DIRS | \n\n [Path('/edx/app/edxapp/edx-platform/lms/templates'),\n Path('/edx/app/edxapp/edx-platform/common/templates'),\n Path('/edx/app/edxapp/edx-platform/common/lib/capa/capa/templates'),\n Path('/edx/app/edxapp/edx-platform/common/djangoapps/pipeline_mako/templates'),\n Path('/edx/app/edxapp/edx-platform/common/static')]\n | \n
| DEPRECATED_ADVANCED_COMPONENT_TYPES | \n\n []\n | \n
| DEV_CONTENT | \n\n True\n | \n
| DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH | \n\n 'verify_student_disable_account_activation_requirement'\n | \n
| DISALLOWED_USER_AGENTS | \n\n []\n | \n
| DISCUSSIONS_HELP_URL | \n\n 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_components/create_discussion.html'\n | \n
| DISCUSSIONS_MICROFRONTEND_URL | \n\n 'http://localhost:2002'\n | \n
| DISCUSSION_SETTINGS | \n\n {'COURSE_PUBLISH_TASK_DELAY': 30, 'MAX_COMMENT_DEPTH': 2}\n | \n
| DJFS | \n\n {'directory_root': 'lms/static/djpyfs',\n 'type': 'osfs',\n 'url_root': '/static/djpyfs'}\n | \n
| DOC_STORE_CONFIG | \n\n {'authsource': '',\n 'collection': 'modulestore',\n 'connectTimeoutMS': 2000,\n 'db': 'edxapp',\n 'host': ['edx.devstack.mongo'],\n 'password': '********************',\n 'port': 27017,\n 'read_preference': 'SECONDARY_PREFERRED',\n 'replicaSet': '',\n 'socketTimeoutMS': 3000,\n 'ssl': False,\n 'user': 'edxapp'}\n | \n
| ECOMMERCE_API_SIGNING_KEY | \n\n '********************'\n | \n
| ECOMMERCE_API_TIMEOUT | \n\n '********************'\n | \n
| ECOMMERCE_API_URL | \n\n '********************'\n | \n
| ECOMMERCE_ORDERS_API_CACHE_TIMEOUT | \n\n '********************'\n | \n
| ECOMMERCE_PUBLIC_URL_ROOT | \n\n 'http://localhost:18130'\n | \n
| ECOMMERCE_SERVICE_WORKER_USERNAME | \n\n 'ecommerce_worker'\n | \n
| EDXMKTG_LOGGED_IN_COOKIE_NAME | \n\n 'edxloggedin'\n | \n
| EDXMKTG_USER_INFO_COOKIE_NAME | \n\n 'edx-user-info'\n | \n
| EDXMKTG_USER_INFO_COOKIE_VERSION | \n\n 1\n | \n
| EDXNOTES_CLIENT_NAME | \n\n 'edx_notes_api-backend-service'\n | \n
| EDXNOTES_CONNECT_TIMEOUT | \n\n 0.5\n | \n
| EDXNOTES_HELP_URL | \n\n 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/exercises_tools/notes.html'\n | \n
| EDXNOTES_INTERNAL_API | \n\n '********************'\n | \n
| EDXNOTES_PUBLIC_API | \n\n '********************'\n | \n
| EDXNOTES_READ_TIMEOUT | \n\n 1.5\n | \n
| EDX_API_KEY | \n\n '********************'\n | \n
| EDX_BRAZE_API_KEY | \n\n '********************'\n | \n
| EDX_BRAZE_API_SERVER | \n\n '********************'\n | \n
| EDX_DRF_EXTENSIONS | \n\n {'JWT_PAYLOAD_USER_ATTRIBUTE_MAPPING': {}}\n | \n
| EDX_PLATFORM_REVISION | \n\n 'master'\n | \n
| EDX_ROOT_URL | \n\n ''\n | \n
| ELASTIC_SEARCH_CONFIG | \n\n [{}]\n | \n
| EMAIL_BACKEND | \n\n 'django.core.mail.backends.filebased.EmailBackend'\n | \n
| EMAIL_FILE_PATH | \n\n '/edx/src/ace_messages/'\n | \n
| EMAIL_HOST | \n\n 'localhost'\n | \n
| EMAIL_HOST_PASSWORD | \n\n '********************'\n | \n
| EMAIL_HOST_USER | \n\n ''\n | \n
| EMAIL_OPTIN_MINIMUM_AGE | \n\n 13\n | \n
| EMAIL_PORT | \n\n 25\n | \n
| EMAIL_SSL_CERTFILE | \n\n None\n | \n
| EMAIL_SSL_KEYFILE | \n\n '********************'\n | \n
| EMAIL_SUBJECT_PREFIX | \n\n '[Django] '\n | \n
| EMAIL_TIMEOUT | \n\n None\n | \n
| EMAIL_USE_LOCALTIME | \n\n False\n | \n
| EMAIL_USE_SSL | \n\n False\n | \n
| EMAIL_USE_TLS | \n\n False\n | \n
| EMBARGO_SITE_REDIRECT_URL | \n\n None\n | \n
| ENABLE_AUTHN_RESET_PASSWORD_HIBP_POLICY | \n\n '********************'\n | \n
| ENABLE_CODEJAIL_REST_SERVICE | \n\n False\n | \n
| ENABLE_COMPREHENSIVE_THEMING | \n\n False\n | \n
| ENABLE_COPPA_COMPLIANCE | \n\n False\n | \n
| ENABLE_CREDIT_ELIGIBILITY | \n\n True\n | \n
| ENABLE_JASMINE | \n\n False\n | \n
| ENABLE_MKTG_SITE | \n\n False\n | \n
| ENABLE_MULTICOURSE | \n\n False\n | \n
| ENABLE_REQUIRE_THIRD_PARTY_AUTH | \n\n False\n | \n
| ENABLE_SAVE_FOR_LATER | \n\n False\n | \n
| ENROLLMENT_COURSE_DETAILS_CACHE_TIMEOUT | \n\n 60\n | \n
| ENTERPRISE_ADMIN_PORTAL_BASE_URL | \n\n 'http://localhost:1991'\n | \n
| ENTERPRISE_ADMIN_PORTAL_NETLOC | \n\n 'localhost:1991'\n | \n
| ENTERPRISE_ADMIN_ROLE | \n\n 'enterprise_admin'\n | \n
| ENTERPRISE_ALL_SERVICE_USERNAMES | \n\n ['ecommerce_worker',\n 'enterprise_worker',\n 'license_manager_worker',\n 'enterprise_catalog_worker',\n 'enterprise_channel_worker']\n | \n
| ENTERPRISE_API_CACHE_TIMEOUT | \n\n '********************'\n | \n
| ENTERPRISE_API_URL | \n\n '********************'\n | \n
| ENTERPRISE_CATALOG_ADMIN_ROLE | \n\n 'catalog_admin'\n | \n
| ENTERPRISE_CATALOG_INTERNAL_ROOT_URL | \n\n 'http://enterprise.catalog.app:18160'\n | \n
| ENTERPRISE_CONSENT_API_URL | \n\n '********************'\n | \n
| ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES | \n\n ['audit', 'honor']\n | \n
| ENTERPRISE_CUSTOMER_CATALOG_DEFAULT_CONTENT_FILTER | \n\n {}\n | \n
| ENTERPRISE_CUSTOMER_COOKIE_NAME | \n\n 'enterprise_customer_uuid'\n | \n
| ENTERPRISE_CUSTOMER_LOGO_IMAGE_SIZE | \n\n 512\n | \n
| ENTERPRISE_CUSTOMER_SUCCESS_EMAIL | \n\n 'customersuccess@edx.org'\n | \n
| ENTERPRISE_DASHBOARD_ADMIN_ROLE | \n\n 'dashboard_admin'\n | \n
| ENTERPRISE_ENROLLMENT_API_ADMIN_ROLE | \n\n '********************'\n | \n
| ENTERPRISE_ENROLLMENT_API_URL | \n\n '********************'\n | \n
| ENTERPRISE_EXCLUDED_REGISTRATION_FIELDS | \n\n {'age',\n 'gender',\n 'goals',\n 'level_of_education',\n 'mailing_address',\n 'year_of_birth'}\n | \n
| ENTERPRISE_INTEGRATIONS_EMAIL | \n\n 'enterprise-integrations@edx.org'\n | \n
| ENTERPRISE_LEARNER_PORTAL_BASE_URL | \n\n 'http://localhost:8734'\n | \n
| ENTERPRISE_LEARNER_PORTAL_NETLOC | \n\n 'localhost:8734'\n | \n
| ENTERPRISE_MARKETING_FOOTER_QUERY_PARAMS | \n\n {}\n | \n
| ENTERPRISE_OPERATOR_ROLE | \n\n 'enterprise_openedx_operator'\n | \n
| ENTERPRISE_PLATFORM_WELCOME_TEMPLATE | \n\n 'Welcome to {platform_name}.'\n | \n
| ENTERPRISE_PROXY_LOGIN_WELCOME_TEMPLATE | \n\n "{start_bold}{enterprise_name}{end_bold} has partnered with {start_bold}{platform_name}{end_bold} to offer you high-quality learning opportunities from the world's best institutions and universities."\n | \n
| ENTERPRISE_PUBLIC_ENROLLMENT_API_URL | \n\n '********************'\n | \n
| ENTERPRISE_READONLY_ACCOUNT_FIELDS | \n\n ['username', 'name', 'email', 'country']\n | \n
| ENTERPRISE_REPORTING_CONFIG_ADMIN_ROLE | \n\n 'reporting_config_admin'\n | \n
| ENTERPRISE_SERVICE_WORKER_USERNAME | \n\n 'enterprise_worker'\n | \n
| ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE | \n\n 'You have left the {start_bold}{enterprise_name}{end_bold} website and are now on the {platform_name} site. {enterprise_name} has partnered with {platform_name} to offer you high-quality, always available learning programs to help you advance your knowledge and career. {line_break}Please note that {platform_name} has a different {privacy_policy_link_start}Privacy Policy{privacy_policy_link_end} from {enterprise_name}.'\n | \n
| ENTERPRISE_SUPPORT_URL | \n\n ''\n | \n
| ENTERPRISE_TAGLINE | \n\n ''\n | \n
| ENTITLEMENTS_EXPIRATION_ROUTING_KEY | \n\n '********************'\n | \n
| ENTITLEMENT_EXPIRED_ALERT_PERIOD | \n\n 90\n | \n
| ENV_CELERY_QUEUES | \n\n ['edx.lms.core.default', 'edx.lms.core.high', 'edx.lms.core.high_mem']\n | \n
| ENV_FEATURES | \n\n {'AUTH_USE_OPENID_PROVIDER': True,\n 'AUTOMATIC_AUTH_FOR_TESTING': False,\n 'CUSTOM_COURSES_EDX': False,\n 'ENABLE_BULK_ENROLLMENT_VIEW': False,\n 'ENABLE_COMBINED_LOGIN_REGISTRATION': True,\n 'ENABLE_CORS_HEADERS': False,\n 'ENABLE_COUNTRY_ACCESS': False,\n 'ENABLE_CREDIT_API': '********************',\n 'ENABLE_CREDIT_ELIGIBILITY': False,\n 'ENABLE_CROSS_DOMAIN_CSRF_COOKIE': False,\n 'ENABLE_CSMH_EXTENDED': True,\n 'ENABLE_DISCUSSION_HOME_PANEL': True,\n 'ENABLE_DISCUSSION_SERVICE': True,\n 'ENABLE_EDXNOTES': True,\n 'ENABLE_ENROLLMENT_RESET': False,\n 'ENABLE_EXPORT_GIT': False,\n 'ENABLE_GRADE_DOWNLOADS': True,\n 'ENABLE_LTI_PROVIDER': False,\n 'ENABLE_MKTG_SITE': False,\n 'ENABLE_MOBILE_REST_API': '********************',\n 'ENABLE_OAUTH2_PROVIDER': False,\n 'ENABLE_PUBLISHER': False,\n 'ENABLE_READING_FROM_MULTIPLE_HISTORY_TABLES': True,\n 'ENABLE_SPECIAL_EXAMS': False,\n 'ENABLE_SYSADMIN_DASHBOARD': False,\n 'ENABLE_THIRD_PARTY_AUTH': True,\n 'ENABLE_VIDEO_UPLOAD_PIPELINE': False,\n 'PREVIEW_LMS_BASE': 'preview.localhost:18000',\n 'SHOW_FOOTER_LANGUAGE_SELECTOR': False,\n 'SHOW_HEADER_LANGUAGE_SELECTOR': False}\n | \n
| ENV_ROOT | \n\n Path('/edx/app/edxapp')\n | \n
| ENV_TOKENS | \n\n '********************'\n | \n
| EVENT_TRACKING_BACKENDS | \n\n {'segmentio': {'ENGINE': 'eventtracking.backends.routing.RoutingBackend',\n 'OPTIONS': {'backends': {'segment': {'ENGINE': 'eventtracking.backends.segment.SegmentBackend'}},\n 'processors': [{'ENGINE': 'eventtracking.processors.whitelist.NameWhitelistProcessor',\n 'OPTIONS': {'whitelist': []}},\n {'ENGINE': 'common.djangoapps.track.shim.GoogleAnalyticsProcessor'}]}},\n 'tracking_logs': {'ENGINE': 'eventtracking.backends.routing.RoutingBackend',\n 'OPTIONS': {'backends': {'logger': {'ENGINE': 'eventtracking.backends.logger.LoggerBackend',\n 'OPTIONS': {'max_event_size': 50000,\n 'name': 'tracking'}}},\n 'processors': [{'ENGINE': 'common.djangoapps.track.shim.LegacyFieldMappingProcessor'},\n {'ENGINE': 'common.djangoapps.track.shim.PrefixedEventProcessor'}]}}}\n | \n
| EVENT_TRACKING_ENABLED | \n\n True\n | \n
| EVENT_TRACKING_PROCESSORS | \n\n []\n | \n
| EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST | \n\n []\n | \n
| EXPLICIT_QUEUES | \n\n {'common.djangoapps.entitlements.tasks.expire_old_entitlements': {'queue': 'edx.lms.core.default'},\n 'lms.djangoapps.bulk_email.tasks.send_course_email': {'queue': 'edx.lms.core.high'},\n 'lms.djangoapps.grades.tasks.recalculate_course_and_subsection_grades_for_user': {'queue': 'edx.lms.core.default'},\n 'lms.djangoapps.grades.tasks.recalculate_subsection_grade_v3': {'queue': 'edx.lms.core.default'},\n 'lms.djangoapps.instructor_task.tasks.calculate_grades_csv': {'queue': 'edx.lms.core.high_mem'},\n 'lms.djangoapps.instructor_task.tasks.calculate_problem_grade_report': {'queue': 'edx.lms.core.high_mem'},\n 'lms.djangoapps.instructor_task.tasks.generate_certificates': {'queue': 'edx.lms.core.high_mem'},\n 'lms.djangoapps.verify_student.tasks.send_ace_message': {'queue': 'edx.lms.core.default'},\n 'lms.djangoapps.verify_student.tasks.send_request_to_ss_for_user': {'queue': 'edx.lms.core.high'},\n 'lms.djangoapps.verify_student.tasks.send_verification_status_email': {'queue': 'edx.lms.core.default'},\n 'openedx.core.djangoapps.content.course_overviews.tasks.async_course_overview_update': {'queue': 'edx.lms.core.high_mem'},\n 'openedx.core.djangoapps.heartbeat.tasks.sample_task': {'queue': 'edx.lms.core.high'},\n 'openedx.core.djangoapps.programs.tasks.award_course_certificate': {'queue': 'edx.lms.core.default'},\n 'openedx.core.djangoapps.programs.tasks.award_program_certificates': {'queue': 'edx.lms.core.default'},\n 'openedx.core.djangoapps.programs.tasks.revoke_program_certificates': {'queue': 'edx.lms.core.default'},\n 'openedx.core.djangoapps.programs.tasks.update_certificate_visible_date_on_course_update': {'queue': 'edx.lms.core.default'},\n 'openedx.core.djangoapps.schedules.tasks._course_update_schedule_send': {'queue': 'edx.lms.core.default'},\n 'openedx.core.djangoapps.schedules.tasks._recurring_nudge_schedule_send': {'queue': 'edx.lms.core.default'},\n 'openedx.core.djangoapps.schedules.tasks._upgrade_reminder_schedule_send': {'queue': 'edx.lms.core.default'},\n 'openedx.core.djangoapps.schedules.tasks.v1.tasks.send_grade_to_credentials': {'queue': 'edx.lms.core.default'}}\n | \n
| EXTRA_MIDDLEWARE_CLASSES | \n\n []\n | \n
| FACEBOOK_API_VERSION | \n\n '********************'\n | \n
| FACEBOOK_APP_ID | \n\n 'FACEBOOK_APP_ID'\n | \n
| FACEBOOK_APP_SECRET | \n\n '********************'\n | \n
| FAVICON_PATH | \n\n 'images/favicon.ico'\n | \n
| FAVICON_URL | \n\n None\n | \n
| FEATURES | \n\n {'ALLOW_ADMIN_ENTERPRISE_COURSE_ENROLLMENT_DELETION': False,\n 'ALLOW_AUTOMATED_SIGNUPS': False,\n 'ALLOW_COURSE_STAFF_GRADE_DOWNLOADS': False,\n 'ALLOW_EMAIL_ADDRESS_CHANGE': True,\n 'ALLOW_HIDING_DISCUSSION_TAB': False,\n 'ALLOW_PUBLIC_ACCOUNT_CREATION': True,\n 'ALLOW_WIKI_ROOT_ACCESS': True,\n 'ALWAYS_REDIRECT_HOMEPAGE_TO_DASHBOARD_FOR_AUTHENTICATED_USER': True,\n 'ANNOUNCEMENTS_PER_PAGE': 5,\n 'AUTH_USE_OPENID_PROVIDER': True,\n 'AUTOMATIC_AUTH_FOR_TESTING': True,\n 'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True,\n 'AUTOPLAY_VIDEOS': False,\n 'BATCH_ENROLLMENT_NOTIFY_USERS_DEFAULT': True,\n 'CERTIFICATES_HTML_VIEW': True,\n 'CERTIFICATES_INSTRUCTOR_GENERATION': False,\n 'COURSES_ARE_BROWSABLE': True,\n 'COURSES_ARE_BROWSEABLE': True,\n 'CUSTOM_CERTIFICATE_TEMPLATES_ENABLED': False,\n 'CUSTOM_COURSES_EDX': False,\n 'DISABLE_AUDIT_CERTIFICATES': False,\n 'DISABLE_HONOR_CERTIFICATES': False,\n 'DISABLE_LOGIN_BUTTON': False,\n 'DISABLE_MOBILE_COURSE_AVAILABLE': False,\n 'DISABLE_START_DATES': False,\n 'DISPLAY_ANALYTICS_ENROLLMENTS': True,\n 'DISPLAY_DEBUG_INFO_TO_STAFF': True,\n 'DISPLAY_HISTOGRAMS_TO_STAFF': False,\n 'EMBARGO': False,\n 'ENABLED_PAYMENT_REPORTS': ['refund_report',\n 'itemized_purchase_report',\n 'university_revenue_share',\n 'certificate_status'],\n 'ENABLE_ACCOUNT_DELETION': True,\n 'ENABLE_ANNOUNCEMENTS': False,\n 'ENABLE_AUTHN_MICROFRONTEND': False,\n 'ENABLE_AUTOADVANCE_VIDEOS': False,\n 'ENABLE_AUTOMATED_SIGNUPS_EXTRA_FIELDS': False,\n 'ENABLE_BULK_ENROLLMENT_VIEW': False,\n 'ENABLE_BULK_USER_RETIREMENT': False,\n 'ENABLE_CCX_ANALYTICS_DASHBOARD_URL': False,\n 'ENABLE_CHANGE_USER_PASSWORD_ADMIN': '********************',\n 'ENABLE_COMBINED_LOGIN_REGISTRATION': True,\n 'ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER': False,\n 'ENABLE_COOKIE_CONSENT': False,\n 'ENABLE_CORS_HEADERS': True,\n 'ENABLE_COSMETIC_DISPLAY_PRICE': True,\n 'ENABLE_COUNTRY_ACCESS': False,\n 'ENABLE_COURSEWARE_SEARCH': False,\n 'ENABLE_COURSEWARE_SEARCH_FOR_COURSE_STAFF': True,\n 'ENABLE_COURSE_ASSESSMENT_GRADE_CHANGE_SIGNAL': False,\n 'ENABLE_COURSE_DISCOVERY': False,\n 'ENABLE_COURSE_FILENAME_CCX_SUFFIX': False,\n 'ENABLE_COURSE_HOME_REDIRECT': True,\n 'ENABLE_COURSE_OLX_VALIDATION': False,\n 'ENABLE_COURSE_SORTING_BY_START_DATE': True,\n 'ENABLE_CREDIT_API': '********************',\n 'ENABLE_CREDIT_ELIGIBILITY': False,\n 'ENABLE_CROSS_DOMAIN_CSRF_COOKIE': False,\n 'ENABLE_CSMH_EXTENDED': True,\n 'ENABLE_DASHBOARD_SEARCH': False,\n 'ENABLE_DEBUG_RUN_PYTHON': False,\n 'ENABLE_DISCUSSION_EMAIL_DIGEST': False,\n 'ENABLE_DISCUSSION_HOME_PANEL': True,\n 'ENABLE_DISCUSSION_SERVICE': True,\n 'ENABLE_DJANGO_ADMIN_SITE': True,\n 'ENABLE_EDXNOTES': True,\n 'ENABLE_ENROLLMENT_RESET': True,\n 'ENABLE_ENROLLMENT_TRACK_USER_PARTITION': True,\n 'ENABLE_ENTERPRISE_INTEGRATION': True,\n 'ENABLE_EXPORT_GIT': False,\n 'ENABLE_FOOTER_MOBILE_APP_LINKS': False,\n 'ENABLE_GRADE_DOWNLOADS': True,\n 'ENABLE_HELP_LINK': True,\n 'ENABLE_HTML_XBLOCK_STUDENT_VIEW_DATA': False,\n 'ENABLE_INSTRUCTOR_BACKGROUND_TASKS': True,\n 'ENABLE_LMS_MIGRATION': False,\n 'ENABLE_LOGIN_MICROFRONTEND': False,\n 'ENABLE_LTI_PROVIDER': False,\n 'ENABLE_MASQUERADE': True,\n 'ENABLE_MAX_FAILED_LOGIN_ATTEMPTS': False,\n 'ENABLE_MKTG_EMAIL_OPT_IN': False,\n 'ENABLE_MKTG_SITE': False,\n 'ENABLE_MOBILE_REST_API': '********************',\n 'ENABLE_OAUTH2_PROVIDER': True,\n 'ENABLE_ONE_CLICK_PROGRAM_PURCHASE': False,\n 'ENABLE_OPENBADGES': False,\n 'ENABLE_ORA_ALL_FILE_URLS': False,\n 'ENABLE_ORA_USERNAMES_ON_DATA_EXPORT': False,\n 'ENABLE_ORA_USER_STATE_UPLOAD_DATA': False,\n 'ENABLE_PASSWORD_RESET_FAILURE_EMAIL': '********************',\n 'ENABLE_PREREQUISITE_COURSES': True,\n 'ENABLE_PUBLISHER': False,\n 'ENABLE_READING_FROM_MULTIPLE_HISTORY_TABLES': True,\n 'ENABLE_SERVICE_STATUS': False,\n 'ENABLE_SOFTWARE_SECURE_FAKE': True,\n 'ENABLE_SPECIAL_EXAMS': True,\n 'ENABLE_STUDENT_HISTORY_VIEW': True,\n 'ENABLE_SYSADMIN_DASHBOARD': False,\n 'ENABLE_TEAMS': True,\n 'ENABLE_TEXTBOOK': True,\n 'ENABLE_THIRD_PARTY_AUTH': True,\n 'ENABLE_UNICODE_USERNAME': False,\n 'ENABLE_V2_CERT_DISPLAY_SETTINGS': False,\n 'ENABLE_VERIFIED_CERTIFICATES': False,\n 'ENABLE_VIDEO_ABSTRACTION_LAYER_API': '********************',\n 'ENABLE_VIDEO_BUMPER': False,\n 'ENABLE_VIDEO_UPLOAD_PIPELINE': False,\n 'ENABLE_XBLOCK_VIEW_ENDPOINT': False,\n 'ENABLE_XBLOCK_XML_VALIDATION': True,\n 'ENTRANCE_EXAMS': True,\n 'EXPOSE_CACHE_PROGRAMS_ENDPOINT': False,\n 'FALLBACK_TO_ENGLISH_TRANSCRIPTS': True,\n 'HIDE_DASHBOARD_COURSES_UNTIL_ACTIVATED': False,\n 'INDIVIDUAL_DUE_DATES': False,\n 'LICENSING': True,\n 'LOG_POSTPAY_CALLBACKS': True,\n 'MAX_ENROLLMENT_INSTR_BUTTONS': 200,\n 'MAX_PROBLEM_RESPONSES_COUNT': 5000,\n 'MILESTONES_APP': True,\n 'MODE_CREATION_FOR_TESTING': False,\n 'PREVENT_CONCURRENT_LOGINS': False,\n 'PREVIEW_LMS_BASE': 'preview.localhost:18000',\n 'REROUTE_ACTIVATION_EMAIL': False,\n 'RESTRICT_AUTOMATIC_AUTH': True,\n 'SHOW_BUMPER_PERIODICITY': 604800,\n 'SHOW_FOOTER_LANGUAGE_SELECTOR': False,\n 'SHOW_HEADER_LANGUAGE_SELECTOR': True,\n 'SKIP_EMAIL_VALIDATION': False,\n 'SQUELCH_PII_IN_LOGS': False,\n 'UNSUPPORTED_BROWSER_ALERT_VERSIONS': '{i:10,f:-3,o:-3,s:-3,c:-3}',\n 'test_django_plugin': True}\n | \n
| FEEDBACK_SUBMISSION_EMAIL | \n\n ''\n | \n
| FERNET_KEYS | \n\n '********************'\n | \n
| FIELDS_STORED_IN_SESSION | \n\n ['auth_entry', 'next']\n | \n
| FIELD_OVERRIDE_PROVIDERS | \n\n ('openedx.features.personalized_learner_schedules.show_answer.show_answer_field_override.ShowAnswerFieldOverride',)\n | \n
| FILE_UPLOAD_DIRECTORY_PERMISSIONS | \n\n None\n | \n
| FILE_UPLOAD_HANDLERS | \n\n ['django.core.files.uploadhandler.MemoryFileUploadHandler',\n 'django.core.files.uploadhandler.TemporaryFileUploadHandler']\n | \n
| FILE_UPLOAD_MAX_MEMORY_SIZE | \n\n 2621440\n | \n
| FILE_UPLOAD_PERMISSIONS | \n\n 420\n | \n
| FILE_UPLOAD_STORAGE_BUCKET_NAME | \n\n 'SET-ME-PLEASE (ex. bucket-name)'\n | \n
| FILE_UPLOAD_STORAGE_PREFIX | \n\n 'submissions_attachments'\n | \n
| FILE_UPLOAD_TEMP_DIR | \n\n None\n | \n
| FINANCE_EMAIL | \n\n ''\n | \n
| FINANCIAL_ASSISTANCE_MAX_LENGTH | \n\n 2500\n | \n
| FINANCIAL_ASSISTANCE_MIN_LENGTH | \n\n 1250\n | \n
| FINANCIAL_REPORTS | \n\n {'BUCKET': None, 'ROOT_PATH': 'sandbox', 'STORAGE_TYPE': 'localfs'}\n | \n
| FIRST_DAY_OF_WEEK | \n\n 0\n | \n
| FIXTURE_DIRS | \n\n []\n | \n
| FOOTER_BROWSER_CACHE_MAX_AGE | \n\n 300\n | \n
| FOOTER_CACHE_TIMEOUT | \n\n 1800\n | \n
| FOOTER_CSS | \n\n {'edx': {'ltr': 'style-lms-footer-edx', 'rtl': 'style-lms-footer-edx-rtl'},\n 'openedx': {'ltr': 'style-lms-footer', 'rtl': 'style-lms-footer-rtl'}}\n | \n
| FOOTER_OPENEDX_LOGO_IMAGE | \n\n 'https://files.edx.org/openedx-logos/open-edx-logo-tag.png'\n | \n
| FOOTER_OPENEDX_URL | \n\n 'https://open.edx.org'\n | \n
| FOOTER_ORGANIZATION_IMAGE | \n\n 'images/logo.png'\n | \n
| FORCE_SCRIPT_NAME | \n\n None\n | \n
| FORMAT_MODULE_PATH | \n\n None\n | \n
| FORM_RENDERER | \n\n 'django.forms.renderers.DjangoTemplates'\n | \n
| GENERATE_PROFILE_SCORES | \n\n False\n | \n
| GEOIP_PATH | \n\n Path('/edx/app/edxapp/edx-platform/common/static/data/geoip/GeoLite2-Country.mmdb')\n | \n
| GITHUB_REPO_ROOT | \n\n '/edx/var/edxapp/data'\n | \n
| GIT_REPO_DIR | \n\n '/edx/var/edxapp/course_repos'\n | \n
| GOOGLE_ANALYTICS_ACCOUNT | \n\n None\n | \n
| GOOGLE_ANALYTICS_LINKEDIN | \n\n ''\n | \n
| GOOGLE_ANALYTICS_TRACKING_ID | \n\n ''\n | \n
| GOOGLE_SITE_VERIFICATION_ID | \n\n ''\n | \n
| GRADES_DOWNLOAD | \n\n {'BUCKET': '',\n 'ROOT_PATH': '',\n 'STORAGE_CLASS': 'django.core.files.storage.FileSystemStorage',\n 'STORAGE_KWARGS': {'location': '/tmp/edx-s3/grades'},\n 'STORAGE_TYPE': ''}\n | \n
| GRADES_DOWNLOAD_ROUTING_KEY | \n\n '********************'\n | \n
| HEARTBEAT_CELERY_ROUTING_KEY | \n\n '********************'\n | \n
| HEARTBEAT_CELERY_TIMEOUT | \n\n 5\n | \n
| HEARTBEAT_CHECKS | \n\n ['openedx.core.djangoapps.heartbeat.default_checks.check_modulestore',\n 'openedx.core.djangoapps.heartbeat.default_checks.check_database']\n | \n
| HEARTBEAT_EXTENDED_CHECKS | \n\n ('openedx.core.djangoapps.heartbeat.default_checks.check_celery',)\n | \n
| HELP_TOKENS_BOOKS | \n\n '********************'\n | \n
| HELP_TOKENS_INI_FILE | \n\n '********************'\n | \n
| HELP_TOKENS_LANGUAGE_CODE | \n\n '********************'\n | \n
| HELP_TOKENS_VERSION | \n\n '********************'\n | \n
| HIGH_MEM_QUEUE | \n\n 'edx.lms.core.high_mem'\n | \n
| HIGH_PRIORITY_QUEUE | \n\n 'edx.lms.core.high'\n | \n
| HOMEPAGE_COURSE_MAX | \n\n 9\n | \n
| HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS | \n\n {'preview.localhost': 'draft-preferred'}\n | \n
| HTTPS | \n\n 'off'\n | \n
| ICP_LICENSE | \n\n None\n | \n
| ICP_LICENSE_INFO | \n\n {}\n | \n
| IDA_LOGOUT_URI_LIST | \n\n ['http://localhost:18130/logout/',\n 'http://localhost:18150/logout/',\n 'http://localhost:18381/logout/',\n 'http://localhost:18010/logout/']\n | \n
| ID_VERIFICATION_SUPPORT_LINK | \n\n ''\n | \n
| IGNORABLE_404_URLS | \n\n []\n | \n
| INSTALLED_APPS | \n\n ['django.contrib.auth',\n 'django.contrib.contenttypes',\n 'django.contrib.humanize',\n 'django.contrib.messages',\n 'django.contrib.redirects',\n 'django.contrib.sessions',\n 'django.contrib.sites',\n 'django.contrib.staticfiles',\n 'django_celery_results',\n 'openedx.core.djangoapps.common_initialization.apps.CommonInitializationConfig',\n 'lms.djangoapps.lms_initialization.apps.LMSInitializationConfig',\n 'openedx.core.djangoapps.common_views',\n 'simple_history',\n 'config_models',\n 'openedx.core.djangoapps.config_model_utils',\n 'waffle',\n 'openedx.core.djangoapps.service_status',\n 'common.djangoapps.status',\n 'common.djangoapps.edxmako.apps.EdxMakoConfig',\n 'pipeline',\n 'common.djangoapps.static_replace',\n 'webpack_loader',\n 'web_fragments',\n 'openedx.core.djangoapps.plugin_api',\n 'openedx.core.djangoapps.contentserver',\n 'openedx.core.djangoapps.site_configuration',\n 'openedx.core.djangoapps.video_config',\n 'openedx.core.djangoapps.video_pipeline',\n 'lms.djangoapps.courseware',\n 'lms.djangoapps.coursewarehistoryextended',\n 'common.djangoapps.student.apps.StudentConfig',\n 'common.djangoapps.split_modulestore_django.apps.SplitModulestoreDjangoBackendAppConfig',\n 'lms.djangoapps.static_template_view',\n 'lms.djangoapps.staticbook',\n 'common.djangoapps.track',\n 'eventtracking.django.apps.EventTrackingConfig',\n 'common.djangoapps.util',\n 'lms.djangoapps.certificates.apps.CertificatesConfig',\n 'lms.djangoapps.instructor_task',\n 'openedx.core.djangoapps.course_groups',\n 'lms.djangoapps.bulk_email',\n 'lms.djangoapps.branding',\n 'lms.djangoapps.course_home_api',\n 'lms.djangoapps.user_tours',\n 'openedx.core.djangoapps.xblock.apps.LmsXBlockAppConfig',\n 'lms.djangoapps.support',\n 'oauth2_provider',\n 'openedx.core.djangoapps.oauth_dispatch.apps.OAuthDispatchAppConfig',\n 'common.djangoapps.third_party_auth',\n 'openedx.core.djangoapps.system_wide_roles',\n 'openedx.core.djangoapps.auth_exchange',\n 'wiki',\n 'django_notify',\n 'lms.djangoapps.course_wiki',\n 'mptt',\n 'sekizai',\n 'wiki.plugins.links',\n 'lms.djangoapps.course_wiki.plugins.markdownedx',\n 'django.contrib.admin',\n 'lms.djangoapps.debug',\n 'openedx.core.djangoapps.util.apps.UtilConfig',\n 'openedx.core.djangoapps.django_comment_common',\n 'lms.djangoapps.edxnotes',\n 'splash',\n 'rest_framework',\n 'openedx.core.djangoapps.user_api',\n 'common.djangoapps.course_modes.apps.CourseModesConfig',\n 'openedx.core.djangoapps.enrollments.apps.EnrollmentsConfig',\n 'common.djangoapps.entitlements.apps.EntitlementsConfig',\n 'lms.djangoapps.bulk_enroll',\n 'lms.djangoapps.verify_student.apps.VerifyStudentConfig',\n 'openedx.core.djangoapps.dark_lang',\n 'lms.djangoapps.rss_proxy',\n 'openedx.core.djangoapps.embargo',\n 'common.djangoapps.course_action_state',\n 'edx_jsme',\n 'django_countries',\n 'lms.djangoapps.mobile_api.apps.MobileApiConfig',\n 'social_django',\n 'lms.djangoapps.survey.apps.SurveyConfig',\n 'lms.djangoapps.lms_xblock.apps.LMSXBlockConfig',\n 'submissions',\n 'openassessment',\n 'openassessment.assessment',\n 'openassessment.fileupload',\n 'openassessment.workflow',\n 'openassessment.xblock',\n 'edxval',\n 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig',\n 'openedx.core.djangoapps.content.block_structure.apps.BlockStructureConfig',\n 'lms.djangoapps.course_blocks',\n 'openedx.core.djangoapps.coursegraph.apps.CoursegraphConfig',\n 'lms.djangoapps.mailing',\n 'corsheaders',\n 'openedx.core.djangoapps.cors_csrf',\n 'lms.djangoapps.commerce.apps.CommerceConfig',\n 'openedx.core.djangoapps.credit.apps.CreditConfig',\n 'lms.djangoapps.teams',\n 'common.djangoapps.xblock_django',\n 'openedx.core.djangoapps.programs.apps.ProgramsConfig',\n 'openedx.core.djangoapps.catalog',\n 'openedx.core.djangoapps.self_paced',\n 'sorl.thumbnail',\n 'milestones',\n 'lms.djangoapps.gating.apps.GatingConfig',\n 'statici18n',\n 'openedx.core.djangoapps.api_admin',\n 'openedx.core.djangoapps.verified_track_content',\n 'lms.djangoapps.learner_dashboard',\n 'lms.djangoapps.badges.apps.BadgesConfig',\n 'django_sites_extensions',\n 'lms.djangoapps.email_marketing.apps.EmailMarketingConfig',\n 'release_util',\n 'rules.apps.AutodiscoverRulesConfig',\n 'bridgekeeper',\n 'celery_utils',\n 'openedx.core.djangoapps.crawlers',\n 'common.djangoapps.database_fixups',\n 'openedx.core.djangoapps.waffle_utils',\n 'lms.djangoapps.course_goals.apps.CourseGoalsConfig',\n 'openedx.features.calendar_sync',\n 'openedx.features.course_bookmarks',\n 'openedx.features.course_experience',\n 'openedx.features.course_search',\n 'openedx.features.enterprise_support.apps.EnterpriseSupportConfig',\n 'openedx.features.learner_profile',\n 'openedx.features.course_duration_limits',\n 'openedx.features.content_type_gating',\n 'openedx.features.discounts',\n 'openedx.features.effort_estimation',\n 'openedx.features.name_affirmation_api.apps.NameAffirmationApiConfig',\n 'lms.djangoapps.experiments',\n 'django_filters',\n 'drf_yasg',\n 'csrf.apps.CsrfAppConfig',\n 'xss_utils',\n 'openedx.core.djangoapps.heartbeat',\n 'openedx.core.djangoapps.course_date_signals',\n 'openedx.core.djangoapps.external_user_ids',\n 'openedx.core.djangoapps.demographics',\n 'openedx.core.djangoapps.schedules',\n 'rest_framework_jwt',\n 'openedx.core.djangoapps.content.learning_sequences.apps.LearningSequencesConfig',\n 'ratelimitbackend',\n 'organizations',\n 'lms.djangoapps.bulk_user_retirement',\n 'openedx.core.djangoapps.agreements',\n 'edx_django_utils.user',\n 'pylti1p3.contrib.django.lti1p3_tool_config',\n 'edx_ace',\n 'lms.djangoapps.save_for_later',\n 'edx_sga',\n 'enterprise',\n 'consent',\n 'integrated_channels.integrated_channel',\n 'integrated_channels.degreed',\n 'integrated_channels.degreed2',\n 'integrated_channels.sap_success_factors',\n 'integrated_channels.cornerstone',\n 'integrated_channels.xapi',\n 'integrated_channels.blackboard',\n 'integrated_channels.canvas',\n 'integrated_channels.moodle',\n 'django_object_actions',\n 'openedx.core.djangoapps.ace_common.apps.AceCommonConfig',\n 'openedx.features.announcements.apps.AnnouncementsConfig',\n 'openedx.core.djangoapps.bookmarks.apps.BookmarksConfig',\n 'openedx.core.djangoapps.content_libraries.apps.ContentLibrariesConfig',\n 'openedx.core.djangoapps.course_apps.apps.CourseAppsConfig',\n 'openedx.core.djangoapps.courseware_api.apps.CoursewareAPIConfig',\n 'openedx.core.djangoapps.credentials.apps.CredentialsConfig',\n 'lms.djangoapps.discussion.apps.DiscussionConfig',\n 'openedx.core.djangoapps.discussions.apps.DiscussionsConfig',\n 'lms.djangoapps.grades.apps.GradesConfig',\n 'lms.djangoapps.instructor.apps.InstructorConfig',\n 'openedx.core.djangoapps.password_policy.apps.PasswordPolicyConfig',\n 'openedx.core.djangoapps.plugins.apps.PluginsConfig',\n 'lms.djangoapps.program_enrollments.apps.ProgramEnrollmentsConfig',\n 'openedx.core.djangoapps.theming.apps.ThemingConfig',\n 'openedx.core.djangoapps.user_authn.apps.UserAuthnConfig',\n 'openedx.core.djangoapps.zendesk_proxy.apps.ZendeskProxyConfig',\n 'edx_toggles.apps.TogglesConfig',\n 'super_csv.apps.SuperCSVConfig',\n 'bulk_grades.apps.BulkGradesConfig',\n 'completion.apps.CompletionAppConfig',\n 'edx_proctoring.apps.EdxProctoringConfig',\n 'lti_consumer.apps.LTIConsumerApp',\n 'edx_name_affirmation.apps.EdxNameAffirmationConfig',\n 'edx_when.apps.EdxWhenConfig',\n 'debug_toolbar']\n | \n
| INTEGRATED_CHANNELS_API_CHUNK_TRANSMISSION_LIMIT | \n\n '********************'\n | \n
| INTERNAL_IPS | \n\n ('127.0.0.1',)\n | \n
| INVOICE_CORP_ADDRESS | \n\n 'Please place your corporate address\\nin this configuration'\n | \n
| INVOICE_PAYMENT_INSTRUCTIONS | \n\n 'This is where you can\\nput directions on how people\\nbuying registration codes'\n | \n
| JWT_AUTH | \n\n {'JWT_ALGORITHM': 'HS256',\n 'JWT_AUDIENCE': 'lms-key',\n 'JWT_AUTH_COOKIE': 'edx-jwt-cookie',\n 'JWT_AUTH_COOKIE_HEADER_PAYLOAD': 'edx-jwt-cookie-header-payload',\n 'JWT_AUTH_COOKIE_SIGNATURE': '********************',\n 'JWT_AUTH_HEADER_PREFIX': 'JWT',\n 'JWT_AUTH_REFRESH_COOKIE': 'edx-jwt-refresh-cookie',\n 'JWT_DECODE_HANDLER': 'edx_rest_framework_extensions.auth.jwt.decoder.jwt_decode_handler',\n 'JWT_EXPIRATION': 30,\n 'JWT_IN_COOKIE_EXPIRATION': 3600,\n 'JWT_ISSUER': 'http://localhost:18000/oauth2',\n 'JWT_ISSUERS': [{'AUDIENCE': 'lms-key',\n 'ISSUER': 'http://localhost:18000/oauth2',\n 'SECRET_KEY': '********************'}],\n 'JWT_LEEWAY': 1,\n 'JWT_LOGIN_CLIENT_ID': 'login-service-client-id',\n 'JWT_LOGIN_SERVICE_USERNAME': 'login_service_user',\n 'JWT_PAYLOAD_GET_USERNAME_HANDLER': <function <lambda> at 0x7f0032c42d30>,\n 'JWT_PRIVATE_SIGNING_JWK': '{"e": "AQAB", "d": '\n '"RQ6k4NpRU3RB2lhwCbQ452W86bMMQiPsa7EJiFJUg-qBJthN0FMNQVbArtrCQ0xA1BdnQHThFiUnHcXfsTZUwmwvTuiqEGR_MI6aI7h5D8vRj_5x-pxOz-0MCB8TY8dcuK9FkljmgtYvV9flVzCk_uUb3ZJIBVyIW8En7n7nV7JXpS9zey1yVLld2AbRG6W5--Pgqr9JCI5-bLdc2otCLuen2sKyuUDHO5NIj30qGTaKUL-OW_PgVmxrwKwccF3w5uGNEvMQ-IcicosCOvzBwdIm1uhdm9rnHU1-fXz8VLRHNhGVv7z6moghjNI0_u4smhUkEsYeshPv7RQEWTdkOQ", '\n '"n": '\n '"smKFSYowG6nNUAdeqH1jQQnH1PmIHphzBmwJ5vRf1vu48BUI5VcVtUWIPqzRK_LDSlZYh9D0YFL0ZTxIrlb6Tn3Xz7pYvpIAeYuQv3_H5p8tbz7Fb8r63c1828wXPITVTv8f7oxx5W3lFFgpFAyYMmROC4Ee9qG5T38LFe8_oAuFCEntimWxN9F3P-FJQy43TL7wG54WodgiM0EgzkeLr5K6cDnyckWjTuZbWI-4ffcTgTZsL_Kq1owa_J2ngEfxMCObnzGy5ZLcTUomo4rZLjghVpq6KZxfS6I1Vz79ZsMVUWEdXOYePCKKsrQG20ogQEkmTf9FT_SouC6jPcHLXw", '\n '"q": '\n '"7KWj7l-ZkfCElyfvwsl7kiosvi-ppOO7Imsv90cribf88DexcO67xdMPesjM9Nh5X209IT-TzbsOtVTXSQyEsy42NY72WETnd1_nAGLAmfxGdo8VV4ZDnRsA8N8POnWjRDwYlVBUEEeuT_MtMWzwIKU94bzkWVnHCY5vbhBYLeM", '\n '"p": '\n '"wPkfnjavNV1Hqb5Qqj2crBS9HQS6GDQIZ7WF9hlBb2ofDNe2K2dunddFqCOdvLXr7ydRcK51ZwSeHjcjgD1aJkHA9i1zqyboxgd0uAbxVDo6ohnlVqYLtap2tXXcavKm4C9MTpob_rk6FBfEuq4uSsuxFvCER4yG3CYBBa4gZVU", '\n '"kid": "devstack_key", "kty": "RSA"}',\n 'JWT_PUBLIC_SIGNING_JWK_SET': '{"keys": [{"kid": "devstack_key", "e": "AQAB", '\n '"kty": "RSA", "n": '\n '"smKFSYowG6nNUAdeqH1jQQnH1PmIHphzBmwJ5vRf1vu48BUI5VcVtUWIPqzRK_LDSlZYh9D0YFL0ZTxIrlb6Tn3Xz7pYvpIAeYuQv3_H5p8tbz7Fb8r63c1828wXPITVTv8f7oxx5W3lFFgpFAyYMmROC4Ee9qG5T38LFe8_oAuFCEntimWxN9F3P-FJQy43TL7wG54WodgiM0EgzkeLr5K6cDnyckWjTuZbWI-4ffcTgTZsL_Kq1owa_J2ngEfxMCObnzGy5ZLcTUomo4rZLjghVpq6KZxfS6I1Vz79ZsMVUWEdXOYePCKKsrQG20ogQEkmTf9FT_SouC6jPcHLXw"}]}',\n 'JWT_SECRET_KEY': '********************',\n 'JWT_SIGNING_ALGORITHM': 'RS512',\n 'JWT_SUPPORTED_VERSION': '1.2.0',\n 'JWT_VERIFY_EXPIRATION': True}\n | \n
| JWT_EXPIRATION | \n\n 30\n | \n
| JWT_ISSUER | \n\n 'http://edx.devstack.lms:18000/oauth2'\n | \n
| JWT_PRIVATE_SIGNING_KEY | \n\n '********************'\n | \n
| KEYS_WITH_MERGED_VALUES | \n\n '********************'\n | \n
| LANGUAGES | \n\n [('en', 'English'),\n ('rtl', 'Right-to-Left Test Language'),\n ('eo', 'Dummy Language (Esperanto)'),\n ('am', 'አማርኛ'),\n ('ar', 'العربية'),\n ('az', 'azərbaycanca'),\n ('bg-bg', 'български (България)'),\n ('bn-bd', 'বাংলা (বাংলাদেশ)'),\n ('bn-in', 'বাংলা (ভারত)'),\n ('bs', 'bosanski'),\n ('ca', 'Català'),\n ('ca@valencia', 'Català (València)'),\n ('cs', 'Čeština'),\n ('cy', 'Cymraeg'),\n ('da', 'dansk'),\n ('de-de', 'Deutsch (Deutschland)'),\n ('el', 'Ελληνικά'),\n ('en-uk', 'English (United Kingdom)'),\n ('en@lolcat', 'LOLCAT English'),\n ('en@pirate', 'Pirate English'),\n ('es-419', 'Español (Latinoamérica)'),\n ('es-ar', 'Español (Argentina)'),\n ('es-ec', 'Español (Ecuador)'),\n ('es-es', 'Español (España)'),\n ('es-mx', 'Español (México)'),\n ('es-pe', 'Español (Perú)'),\n ('et-ee', 'Eesti (Eesti)'),\n ('eu-es', 'euskara (Espainia)'),\n ('fa', 'فارسی'),\n ('fa-ir', 'فارسی (ایران)'),\n ('fi-fi', 'Suomi (Suomi)'),\n ('fil', 'Filipino'),\n ('fr', 'Français'),\n ('gl', 'Galego'),\n ('gu', 'ગુજરાતી'),\n ('he', 'עברית'),\n ('hi', 'हिन्दी'),\n ('hr', 'hrvatski'),\n ('hu', 'magyar'),\n ('hy-am', 'Հայերեն (Հայաստան)'),\n ('id', 'Bahasa Indonesia'),\n ('it-it', 'Italiano (Italia)'),\n ('ja-jp', '日本語 (日本)'),\n ('kk-kz', 'қазақ тілі (Қазақстан)'),\n ('km-kh', 'ភាសាខ្មែរ (កម្ពុជា)'),\n ('kn', 'ಕನ್ನಡ'),\n ('ko-kr', '한국어 (대한민국)'),\n ('lt-lt', 'Lietuvių (Lietuva)'),\n ('ml', 'മലയാളം'),\n ('mn', 'Монгол хэл'),\n ('mr', 'मराठी'),\n ('ms', 'Bahasa Melayu'),\n ('nb', 'Norsk bokmål'),\n ('ne', 'नेपाली'),\n ('nl-nl', 'Nederlands (Nederland)'),\n ('or', 'ଓଡ଼ିଆ'),\n ('pl', 'Polski'),\n ('pt-br', 'Português (Brasil)'),\n ('pt-pt', 'Português (Portugal)'),\n ('ro', 'română'),\n ('ru', 'Русский'),\n ('si', 'සිංහල'),\n ('sk', 'Slovenčina'),\n ('sl', 'Slovenščina'),\n ('sq', 'shqip'),\n ('sr', 'Српски'),\n ('sv', 'svenska'),\n ('sw', 'Kiswahili'),\n ('ta', 'தமிழ்'),\n ('te', 'తెలుగు'),\n ('th', 'ไทย'),\n ('tr-tr', 'Türkçe (Türkiye)'),\n ('uk', 'Українська'),\n ('ur', 'اردو'),\n ('vi', 'Tiếng Việt'),\n ('uz', 'Ўзбек'),\n ('zh-cn', '中文 (简体)'),\n ('zh-hk', '中文 (香港)'),\n ('zh-tw', '中文 (台灣)')]\n | \n
| LANGUAGES_BIDI | \n\n ('he', 'ar', 'fa', 'ur', 'fa-ir', 'rtl')\n | \n
| LANGUAGE_CODE | \n\n 'en'\n | \n
| LANGUAGE_COOKIE | \n\n 'openedx-language-preference'\n | \n
| LANGUAGE_COOKIE_AGE | \n\n None\n | \n
| LANGUAGE_COOKIE_DOMAIN | \n\n None\n | \n
| LANGUAGE_COOKIE_HTTPONLY | \n\n False\n | \n
| LANGUAGE_COOKIE_NAME | \n\n 'openedx-language-preference'\n | \n
| LANGUAGE_COOKIE_PATH | \n\n '/'\n | \n
| LANGUAGE_COOKIE_SAMESITE | \n\n None\n | \n
| LANGUAGE_COOKIE_SECURE | \n\n False\n | \n
| LANGUAGE_DICT | \n\n {'am': 'አማርኛ',\n 'ar': 'العربية',\n 'az': 'azərbaycanca',\n 'bg-bg': 'български (България)',\n 'bn-bd': 'বাংলা (বাংলাদেশ)',\n 'bn-in': 'বাংলা (ভারত)',\n 'bs': 'bosanski',\n 'ca': 'Català',\n 'ca@valencia': 'Català (València)',\n 'cs': 'Čeština',\n 'cy': 'Cymraeg',\n 'da': 'dansk',\n 'de-de': 'Deutsch (Deutschland)',\n 'el': 'Ελληνικά',\n 'en': 'English',\n 'en-uk': 'English (United Kingdom)',\n 'en@lolcat': 'LOLCAT English',\n 'en@pirate': 'Pirate English',\n 'eo': 'Dummy Language (Esperanto)',\n 'es-419': 'Español (Latinoamérica)',\n 'es-ar': 'Español (Argentina)',\n 'es-ec': 'Español (Ecuador)',\n 'es-es': 'Español (España)',\n 'es-mx': 'Español (México)',\n 'es-pe': 'Español (Perú)',\n 'et-ee': 'Eesti (Eesti)',\n 'eu-es': 'euskara (Espainia)',\n 'fa': 'فارسی',\n 'fa-ir': 'فارسی (ایران)',\n 'fi-fi': 'Suomi (Suomi)',\n 'fil': 'Filipino',\n 'fr': 'Français',\n 'gl': 'Galego',\n 'gu': 'ગુજરાતી',\n 'he': 'עברית',\n 'hi': 'हिन्दी',\n 'hr': 'hrvatski',\n 'hu': 'magyar',\n 'hy-am': 'Հայերեն (Հայաստան)',\n 'id': 'Bahasa Indonesia',\n 'it-it': 'Italiano (Italia)',\n 'ja-jp': '日本語 (日本)',\n 'kk-kz': 'қазақ тілі (Қазақстан)',\n 'km-kh': 'ភាសាខ្មែរ (កម្ពុជា)',\n 'kn': 'ಕನ್ನಡ',\n 'ko-kr': '한국어 (대한민국)',\n 'lt-lt': 'Lietuvių (Lietuva)',\n 'ml': 'മലയാളം',\n 'mn': 'Монгол хэл',\n 'mr': 'मराठी',\n 'ms': 'Bahasa Melayu',\n 'nb': 'Norsk bokmål',\n 'ne': 'नेपाली',\n 'nl-nl': 'Nederlands (Nederland)',\n 'or': 'ଓଡ଼ିଆ',\n 'pl': 'Polski',\n 'pt-br': 'Português (Brasil)',\n 'pt-pt': 'Português (Portugal)',\n 'ro': 'română',\n 'rtl': 'Right-to-Left Test Language',\n 'ru': 'Русский',\n 'si': 'සිංහල',\n 'sk': 'Slovenčina',\n 'sl': 'Slovenščina',\n 'sq': 'shqip',\n 'sr': 'Српски',\n 'sv': 'svenska',\n 'sw': 'Kiswahili',\n 'ta': 'தமிழ்',\n 'te': 'తెలుగు',\n 'th': 'ไทย',\n 'tr-tr': 'Türkçe (Türkiye)',\n 'uk': 'Українська',\n 'ur': 'اردو',\n 'uz': 'Ўзбек',\n 'vi': 'Tiếng Việt',\n 'zh-cn': '中文 (简体)',\n 'zh-hk': '中文 (香港)',\n 'zh-tw': '中文 (台灣)'}\n | \n
| LANGUAGE_MAP | \n\n {'name': 'Language',\n 'terms': {'aa': 'Afar',\n 'ab': 'Abkhazian',\n 'ae': 'Avestan',\n 'af': 'Afrikaans',\n 'ak': 'Akan',\n 'am': 'Amharic',\n 'an': 'Aragonese',\n 'ar': 'Arabic',\n 'as': 'Assamese',\n 'av': 'Avaric',\n 'ay': 'Aymara',\n 'az': 'Azerbaijani',\n 'ba': 'Bashkir',\n 'be': 'Belarusian',\n 'bg': 'Bulgarian',\n 'bh': 'Bihari languages',\n 'bi': 'Bislama',\n 'bm': 'Bambara',\n 'bn': 'Bengali',\n 'bo': 'Tibetan',\n 'br': 'Breton',\n 'bs': 'Bosnian',\n 'ca': 'Catalan',\n 'ce': 'Chechen',\n 'ch': 'Chamorro',\n 'co': 'Corsican',\n 'cr': 'Cree',\n 'cs': 'Czech',\n 'cu': 'Church Slavic',\n 'cv': 'Chuvash',\n 'cy': 'Welsh',\n 'da': 'Danish',\n 'de': 'German',\n 'dv': 'Divehi',\n 'dz': 'Dzongkha',\n 'ee': 'Ewe',\n 'el': 'Greek',\n 'en': 'English',\n 'eo': 'Esperanto',\n 'es': 'Spanish',\n 'et': 'Estonian',\n 'eu': 'Basque',\n 'fa': 'Persian',\n 'ff': 'Fulah',\n 'fi': 'Finnish',\n 'fj': 'Fijian',\n 'fo': 'Faroese',\n 'fr': 'French',\n 'fy': 'Western Frisian',\n 'ga': 'Irish',\n 'gd': 'Gaelic',\n 'gl': 'Galician',\n 'gn': 'Guarani',\n 'gu': 'Gujarati',\n 'gv': 'Manx',\n 'ha': 'Hausa',\n 'he': 'Hebrew',\n 'hi': 'Hindi',\n 'ho': 'Hiri Motu',\n 'hr': 'Croatian',\n 'ht': 'Haitian',\n 'hu': 'Hungarian',\n 'hy': 'Armenian',\n 'hz': 'Herero',\n 'ia': 'Interlingua',\n 'id': 'Indonesian',\n 'ie': 'Interlingue',\n 'ig': 'Igbo',\n 'ii': 'Sichuan Yi',\n 'ik': 'Inupiaq',\n 'io': 'Ido',\n 'is': 'Icelandic',\n 'it': 'Italian',\n 'iu': 'Inuktitut',\n 'ja': 'Japanese',\n 'jv': 'Javanese',\n 'ka': 'Georgian',\n 'kg': 'Kongo',\n 'ki': 'Kikuyu',\n 'kj': 'Kuanyama',\n 'kk': 'Kazakh',\n 'kl': 'Kalaallisut',\n 'km': 'Central Khmer',\n 'kn': 'Kannada',\n 'ko': 'Korean',\n 'kr': 'Kanuri',\n 'ks': 'Kashmiri',\n 'ku': 'Kurdish',\n 'kv': 'Komi',\n 'kw': 'Cornish',\n 'ky': 'Kirghiz',\n 'la': 'Latin',\n 'lb': 'Luxembourgish',\n 'lg': 'Ganda',\n 'li': 'Limburgan',\n 'ln': 'Lingala',\n 'lo': 'Lao',\n 'lt': 'Lithuanian',\n 'lu': 'Luba-Katanga',\n 'lv': 'Latvian',\n 'mg': 'Malagasy',\n 'mh': 'Marshallese',\n 'mi': 'Maori',\n 'mk': 'Macedonian',\n 'ml': 'Malayalam',\n 'mn': 'Mongolian',\n 'mr': 'Marathi',\n 'ms': 'Malay',\n 'mt': 'Maltese',\n 'my': 'Burmese',\n 'na': 'Nauru',\n 'nb': 'Bokmål, Norwegian',\n 'nd': 'Ndebele, North',\n 'ne': 'Nepali',\n 'ng': 'Ndonga',\n 'nl': 'Dutch',\n 'nn': 'Norwegian Nynorsk',\n 'no': 'Norwegian',\n 'nr': 'Ndebele, South',\n 'nv': 'Navajo',\n 'ny': 'Chichewa',\n 'oc': 'Occitan',\n 'oj': 'Ojibwa',\n 'om': 'Oromo',\n 'or': 'Oriya',\n 'os': 'Ossetian',\n 'pa': 'Panjabi',\n 'pi': 'Pali',\n 'pl': 'Polish',\n 'ps': 'Pushto',\n 'pt': 'Portuguese',\n 'qu': 'Quechua',\n 'rm': 'Romansh',\n 'rn': 'Rundi',\n 'ro': 'Romanian',\n 'ru': 'Russian',\n 'rw': 'Kinyarwanda',\n 'sa': 'Sanskrit',\n 'sc': 'Sardinian',\n 'sd': 'Sindhi',\n 'se': 'Northern Sami',\n 'sg': 'Sango',\n 'si': 'Sinhala',\n 'sk': 'Slovak',\n 'sl': 'Slovenian',\n 'sm': 'Samoan',\n 'sn': 'Shona',\n 'so': 'Somali',\n 'sq': 'Albanian',\n 'sr': 'Serbian',\n 'ss': 'Swati',\n 'st': 'Sotho, Southern',\n 'su': 'Sundanese',\n 'sv': 'Swedish',\n 'sw': 'Swahili',\n 'ta': 'Tamil',\n 'te': 'Telugu',\n 'tg': 'Tajik',\n 'th': 'Thai',\n 'ti': 'Tigrinya',\n 'tk': 'Turkmen',\n 'tl': 'Tagalog',\n 'tn': 'Tswana',\n 'to': 'Tonga (Tonga Islands)',\n 'tr': 'Turkish',\n 'ts': 'Tsonga',\n 'tt': 'Tatar',\n 'tw': 'Twi',\n 'ty': 'Tahitian',\n 'ug': 'Uighur',\n 'uk': 'Ukrainian',\n 'ur': 'Urdu',\n 'uz': 'Uzbek',\n 've': 'Venda',\n 'vi': 'Vietnamese',\n 'vo': 'Volapük',\n 'wa': 'Walloon',\n 'wo': 'Wolof',\n 'xh': 'Xhosa',\n 'yi': 'Yiddish',\n 'yo': 'Yoruba',\n 'za': 'Zhuang',\n 'zh': 'Chinese',\n 'zh_HANS': 'Simplified Chinese',\n 'zh_HANT': 'Traditional Chinese',\n 'zu': 'Zulu'}}\n | \n
| LEARNER_PORTAL_URL_ROOT | \n\n 'http://localhost:8734'\n | \n
| LEARNING_MICROFRONTEND_URL | \n\n 'http://localhost:2000'\n | \n
| LMS_BASE | \n\n 'localhost:18000'\n | \n
| LMS_ENROLLMENT_API_PATH | \n\n '********************'\n | \n
| LMS_INTERNAL_ROOT_URL | \n\n 'http://localhost:18000'\n | \n
| LMS_MIGRATION_ALLOWED_IPS | \n\n []\n | \n
| LMS_ROOT_URL | \n\n 'http://localhost:18000'\n | \n
| LMS_SEGMENT_KEY | \n\n '********************'\n | \n
| LOCALE_PATHS | \n\n [Path('/edx/app/edxapp/edx-platform/conf/locale')]\n | \n
| LOCAL_LOGLEVEL | \n\n 'INFO'\n | \n
| LOGGING | \n\n {'disable_existing_loggers': False,\n 'filters': {'remoteip_context': {'()': 'edx_django_utils.logging.RemoteIpFilter'},\n 'require_debug_false': {'()': 'django.utils.log.RequireDebugFalse'},\n 'userid_context': {'()': 'edx_django_utils.logging.UserIdFilter'}},\n 'formatters': {'raw': {'format': '%(message)s'},\n 'standard': {'format': '%(asctime)s %(levelname)s %(process)d '\n '[%(name)s] [user %(userid)s] [ip '\n '%(remoteip)s] %(filename)s:%(lineno)d '\n '- %(message)s'},\n 'syslog_format': {'format': '[service_variant=lms][%(name)s][env:sandbox] '\n '%(levelname)s [lms %(process)d] '\n '[user %(userid)s] [ip '\n '%(remoteip)s] '\n '[%(filename)s:%(lineno)d] - '\n '%(message)s'}},\n 'handlers': {'console': {'class': 'logging.StreamHandler',\n 'filters': ['userid_context', 'remoteip_context'],\n 'formatter': 'standard',\n 'level': 'INFO',\n 'stream': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>},\n 'local': {'class': 'logging.NullHandler'},\n 'mail_admins': {'class': 'django.utils.log.AdminEmailHandler',\n 'filters': ['require_debug_false'],\n 'level': 'ERROR'},\n 'tracking': {'class': 'logging.NullHandler'}},\n 'loggers': {'': {'handlers': ['console', 'local'],\n 'level': 'INFO',\n 'propagate': False},\n 'django.request': {'handlers': ['mail_admins'],\n 'level': 'ERROR',\n 'propagate': True},\n 'requests.packages.urllib3': {'level': 'WARN'},\n 'tracking': {'handlers': ['console'],\n 'level': 'DEBUG',\n 'propagate': False}},\n 'version': 1}\n | \n
| LOGGING_CONFIG | \n\n 'logging.config.dictConfig'\n | \n
| LOGGING_ENV | \n\n 'sandbox'\n | \n
| LOGIN_AND_REGISTER_FORM_RATELIMIT | \n\n '100/5m'\n | \n
| LOGIN_ISSUE_SUPPORT_LINK | \n\n ''\n | \n
| LOGIN_REDIRECT_URL | \n\n '/login'\n | \n
| LOGIN_REDIRECT_WHITELIST | \n\n ['localhost:18010',\n 'localhost:1997',\n 'localhost:1976',\n 'localhost:1994',\n 'localhost:2000',\n 'localhost:2001',\n 'localhost:3001',\n 'localhost:18400',\n 'localhost:1993',\n 'localhost:8734',\n 'localhost:1991']\n | \n
| LOGIN_URL | \n\n '/login'\n | \n
| LOGISTRATION_API_RATELIMIT | \n\n '********************'\n | \n
| LOGISTRATION_PER_EMAIL_RATELIMIT_RATE | \n\n '30/5m'\n | \n
| LOGISTRATION_RATELIMIT_RATE | \n\n '100/5m'\n | \n
| LOGOUT_REDIRECT_URL | \n\n None\n | \n
| LOGO_IMAGE_EXTRA_TEXT | \n\n ''\n | \n
| LOGO_TRADEMARK_URL | \n\n None\n | \n
| LOGO_URL | \n\n None\n | \n
| LOGO_URL_PNG | \n\n None\n | \n
| LOG_DIR | \n\n '/edx/var/log/edx'\n | \n
| LOG_OVERRIDES | \n\n [('common.djangoapps.track.contexts', 50),\n ('common.djangoapps.track.middleware', 50),\n ('lms.djangoapps.discussion.django_comment_client.utils', 50)]\n | \n
| LTI_AGGREGATE_SCORE_PASSBACK_DELAY | \n\n '********************'\n | \n
| LTI_USER_EMAIL_DOMAIN | \n\n 'lti.example.com'\n | \n
| MAILCHIMP_NEW_USER_LIST_ID | \n\n None\n | \n
| MAINTENANCE_BANNER_TEXT | \n\n 'Sample banner message'\n | \n
| MAKO_MODULE_DIR | \n\n '/tmp/mako_lms'\n | \n
| MAKO_TEMPLATE_DIRS_BASE | \n\n [Path('/edx/app/edxapp/edx-platform/lms/templates'),\n Path('/edx/app/edxapp/edx-platform/common/templates'),\n Path('/edx/app/edxapp/edx-platform/common/lib/capa/capa/templates'),\n Path('/edx/app/edxapp/edx-platform/common/djangoapps/pipeline_mako/templates'),\n Path('/edx/app/edxapp/edx-platform/openedx/core/djangoapps/cors_csrf/templates'),\n Path('/edx/app/edxapp/edx-platform/openedx/core/djangoapps/dark_lang/templates'),\n Path('/edx/app/edxapp/edx-platform/openedx/core/lib/license/templates'),\n Path('/edx/app/edxapp/edx-platform/openedx/features/course_experience/templates')]\n | \n
| MANAGERS | \n\n ()\n | \n
| MARKETING_EMAILS_OPT_IN | \n\n False\n | \n
| MARKETING_SITE_ROOT | \n\n 'http://localhost:8080'\n | \n
| MAX_BLOCKS_PER_CONTENT_LIBRARY | \n\n 1000\n | \n
| MAX_BOOKMARKS_PER_COURSE | \n\n 100\n | \n
| MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED | \n\n 6\n | \n
| MAX_FAILED_LOGIN_ATTEMPTS_LOCKOUT_PERIOD_SECS | \n\n 1800\n | \n
| MAX_FILEUPLOADS_PER_INPUT | \n\n 20\n | \n
| MEDIA_ROOT | \n\n '/edx/var/edxapp/uploads'\n | \n
| MEDIA_URL | \n\n '/media/'\n | \n
| MESSAGE_STORAGE | \n\n 'django.contrib.messages.storage.session.SessionStorage'\n | \n
| MICROSITE_CONFIGURATION | \n\n {}\n | \n
| MICROSITE_ROOT_DIR | \n\n '/edx/app/edxapp/edx-microsite'\n | \n
| MIDDLEWARE | \n\n ['openedx.core.lib.x_forwarded_for.middleware.XForwardedForMiddleware',\n 'crum.CurrentRequestUserMiddleware',\n 'edx_django_utils.monitoring.DeploymentMonitoringMiddleware',\n 'edx_django_utils.cache.middleware.RequestCacheMiddleware',\n 'edx_django_utils.monitoring.CodeOwnerMonitoringMiddleware',\n 'openedx.core.djangoapps.cookie_metadata.middleware.CookieNameChange',\n 'openedx.core.lib.request_utils.ExpectedErrorMiddleware',\n 'edx_django_utils.monitoring.CachedCustomMonitoringMiddleware',\n 'openedx.core.lib.request_utils.CookieMonitoringMiddleware',\n 'lms.djangoapps.mobile_api.middleware.AppVersionUpgrade',\n 'openedx.core.djangoapps.header_control.middleware.HeaderControlMiddleware',\n 'lms.djangoapps.discussion.django_comment_client.middleware.AjaxExceptionMiddleware',\n 'django.middleware.common.CommonMiddleware',\n 'django.contrib.sites.middleware.CurrentSiteMiddleware',\n 'edx_rest_framework_extensions.auth.jwt.middleware.JwtAuthCookieMiddleware',\n 'django_sites_extensions.middleware.RedirectMiddleware',\n 'openedx.core.djangoapps.safe_sessions.middleware.SafeSessionMiddleware',\n 'openedx.core.djangoapps.cache_toolbox.middleware.CacheBackedAuthenticationMiddleware',\n 'common.djangoapps.student.middleware.UserStandingMiddleware',\n 'openedx.core.djangoapps.contentserver.middleware.StaticContentServer',\n 'openedx.core.djangoapps.user_api.middleware.UserTagsEventContextMiddleware',\n 'django.contrib.messages.middleware.MessageMiddleware',\n 'common.djangoapps.track.middleware.TrackMiddleware',\n 'corsheaders.middleware.CorsMiddleware',\n 'openedx.core.djangoapps.cors_csrf.middleware.CorsCSRFMiddleware',\n 'openedx.core.djangoapps.cors_csrf.middleware.CsrfCrossDomainCookieMiddleware',\n 'django.middleware.csrf.CsrfViewMiddleware',\n 'splash.middleware.SplashMiddleware',\n 'openedx.core.djangoapps.geoinfo.middleware.CountryMiddleware',\n 'openedx.core.djangoapps.embargo.middleware.EmbargoMiddleware',\n 'enterprise.middleware.EnterpriseLanguagePreferenceMiddleware',\n 'openedx.core.djangoapps.lang_pref.middleware.LanguagePreferenceMiddleware',\n 'openedx.core.djangoapps.dark_lang.middleware.DarkLangMiddleware',\n 'django.middleware.locale.LocaleMiddleware',\n 'lms.djangoapps.discussion.django_comment_client.utils.ViewNameMiddleware',\n 'codejail.django_integration.ConfigureCodeJailMiddleware',\n 'ratelimitbackend.middleware.RateLimitMiddleware',\n 'openedx.core.djangoapps.session_inactivity_timeout.middleware.SessionInactivityTimeout',\n 'django.middleware.clickjacking.XFrameOptionsMiddleware',\n 'lms.djangoapps.courseware.middleware.CacheCourseIdMiddleware',\n 'lms.djangoapps.courseware.middleware.RedirectMiddleware',\n 'lms.djangoapps.course_wiki.middleware.WikiAccessMiddleware',\n 'openedx.core.djangoapps.theming.middleware.CurrentSiteThemeMiddleware',\n 'waffle.middleware.WaffleMiddleware',\n 'edx_django_utils.cache.middleware.TieredCacheMiddleware',\n 'edx_rest_framework_extensions.middleware.RequestCustomAttributesMiddleware',\n 'edx_rest_framework_extensions.auth.jwt.middleware.EnsureJWTAuthSettingsMiddleware',\n 'simple_history.middleware.HistoryRequestMiddleware',\n 'openedx.core.djangoapps.site_configuration.middleware.SessionCookieDomainOverrideMiddleware',\n 'lms.djangoapps.discussion.django_comment_client.utils.QueryCountDebugMiddleware',\n 'debug_toolbar.middleware.DebugToolbarMiddleware',\n 'common.djangoapps.third_party_auth.middleware.ExceptionMiddleware']\n | \n
| MIGRATION_MODULES | \n\n {}\n | \n
| MKTG_URLS | \n\n {'ABOUT': '/about',\n 'ACCESSIBILITY': '/accessibility',\n 'AFFILIATES': '/affiliate-program',\n 'BLOG': '/blog',\n 'CAREERS': '/careers',\n 'CONTACT': '/support/contact_us',\n 'COURSES': '/course',\n 'DONATE': '/donate',\n 'ENTERPRISE': '/enterprise',\n 'FAQ': '/student-faq',\n 'HONOR': '/edx-terms-service',\n 'HOW_IT_WORKS': '/how-it-works',\n 'MEDIA_KIT': '/media-kit',\n 'NEWS': '/news-announcements',\n 'PRESS': '/press',\n 'PRIVACY': '/edx-privacy-policy',\n 'ROOT': 'http://localhost:8080',\n 'SCHOOLS': '/schools-partners',\n 'SITE_MAP': '/sitemap',\n 'TOS': '/edx-terms-service',\n 'TOS_AND_HONOR': '/edx-terms-service',\n 'TRADEMARKS': '/trademarks',\n 'WHAT_IS_VERIFIED_CERT': '/verified-certificate'}\n | \n
| MKTG_URL_LINK_MAP | \n\n {'ABOUT': 'about',\n 'BLOG': 'blog',\n 'CONTACT': 'contact',\n 'COURSES': 'courses',\n 'DONATE': 'donate',\n 'FAQ': 'help',\n 'HONOR': 'honor',\n 'PRESS': 'press',\n 'PRIVACY': 'privacy',\n 'ROOT': 'root',\n 'SITEMAP.XML': 'sitemap_xml',\n 'TOS': 'tos',\n 'TOS_AND_HONOR': 'edx-terms-service',\n 'WHAT_IS_VERIFIED_CERT': 'verified-certificate'}\n | \n
| MKTG_URL_OVERRIDES | \n\n {}\n | \n
| MOBILE_APP_USER_AGENT_REGEXES | \n\n ['edX/org.edx.mobile']\n | \n
| MOBILE_STORE_URLS | \n\n {}\n | \n
| MODULESTORE | \n\n {'default': {'ENGINE': 'xmodule.modulestore.mixed.MixedModuleStore',\n 'OPTIONS': {'mappings': {},\n 'stores': [{'DOC_STORE_CONFIG': {'authsource': '',\n 'collection': 'modulestore',\n 'connectTimeoutMS': 2000,\n 'db': 'edxapp',\n 'host': ['edx.devstack.mongo'],\n 'password': '********************',\n 'port': 27017,\n 'read_preference': 'SECONDARY_PREFERRED',\n 'replicaSet': '',\n 'socketTimeoutMS': 3000,\n 'ssl': False,\n 'user': 'edxapp'},\n 'ENGINE': 'xmodule.modulestore.split_mongo.split_draft.DraftVersioningModuleStore',\n 'NAME': 'split',\n 'OPTIONS': {'default_class': 'xmodule.hidden_module.HiddenDescriptor',\n 'fs_root': '/edx/var/edxapp/data',\n 'render_template': 'common.djangoapps.edxmako.shortcuts.render_to_string'}},\n {'DOC_STORE_CONFIG': {'authsource': '',\n 'collection': 'modulestore',\n 'connectTimeoutMS': 2000,\n 'db': 'edxapp',\n 'host': ['edx.devstack.mongo'],\n 'password': '********************',\n 'port': 27017,\n 'read_preference': 'PRIMARY',\n 'replicaSet': '',\n 'socketTimeoutMS': 3000,\n 'ssl': False,\n 'user': 'edxapp'},\n 'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',\n 'NAME': 'draft',\n 'OPTIONS': {'default_class': 'xmodule.hidden_module.HiddenDescriptor',\n 'fs_root': '/edx/var/edxapp/data',\n 'render_template': 'common.djangoapps.edxmako.shortcuts.render_to_string'}}]}}}\n | \n
| MODULESTORE_BRANCH | \n\n 'published-only'\n | \n
| MODULESTORE_FIELD_OVERRIDE_PROVIDERS | \n\n ('openedx.features.content_type_gating.field_override.ContentTypeGatingFieldOverride',\n 'lms.djangoapps.courseware.self_paced_overrides.SelfPacedDateOverrideProvider')\n | \n
| MONGODB_LOG | \n\n {}\n | \n
| MONTH_DAY_FORMAT | \n\n 'F j'\n | \n
| NODE_MODULES_ROOT | \n\n Path('/edx/app/edxapp/edx-platform/node_modules')\n | \n
| NODE_PATH | \n\n '/edx/app/edxapp/edx-platform/common/static/js/vendor:/edx/app/edxapp/edx-platform/node_modules'\n | \n
| NOTES_DISABLED_TABS | \n\n ['course_structure', 'tags']\n | \n
| NOTIFICATION_EMAIL_CSS | \n\n 'templates/credit_notifications/credit_notification.css'\n | \n
| NOTIFICATION_EMAIL_EDX_LOGO | \n\n 'templates/credit_notifications/edx-logo-header.png'\n | \n
| NUMBER_GROUPING | \n\n 0\n | \n
| OAUTH2_DEFAULT_SCOPES | \n\n {'email': 'Know your email address',\n 'profile': 'Know your name and username',\n 'read': 'Read access',\n 'write': 'Write access'}\n | \n
| OAUTH2_PROVIDER | \n\n {'DEFAULT_SCOPES': {'email': 'Know your email address',\n 'profile': 'Know your name and username',\n 'read': 'Read access',\n 'write': 'Write access'},\n 'ERROR_RESPONSE_WITH_SCOPES': True,\n 'OAUTH2_VALIDATOR_CLASS': 'openedx.core.djangoapps.oauth_dispatch.dot_overrides.validators.EdxOAuth2Validator',\n 'REFRESH_TOKEN_EXPIRE_SECONDS': '********************',\n 'REQUEST_APPROVAL_PROMPT': 'auto_even_if_expired',\n 'SCOPES': {'certificates:read': 'Retrieve your course certificates',\n 'email': 'Know your email address',\n 'grades:read': 'Retrieve your grades for your enrolled courses',\n 'profile': 'Know your name and username',\n 'read': 'Read access',\n 'tpa:read': 'Retrieve your third-party authentication username mapping',\n 'user_id': 'Know your user identifier',\n 'write': 'Write access'},\n 'SCOPES_BACKEND_CLASS': 'openedx.core.djangoapps.oauth_dispatch.scopes.ApplicationModelScopes'}\n | \n
| OAUTH2_PROVIDER_APPLICATION_MODEL | \n\n 'oauth2_provider.Application'\n | \n
| OAUTH_DELETE_EXPIRED | \n\n True\n | \n
| OAUTH_ENFORCE_SECURE | \n\n False\n | \n
| OAUTH_EXPIRE_CONFIDENTIAL_CLIENT_DAYS | \n\n 365\n | \n
| OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS | \n\n 30\n | \n
| OAUTH_ID_TOKEN_EXPIRATION | \n\n '********************'\n | \n
| OPENAPI_CACHE_TIMEOUT | \n\n '********************'\n | \n
| OPENEDX_ROOT | \n\n Path('/edx/app/edxapp/edx-platform/openedx')\n | \n
| OPTIMIZELY_PROJECT_ID | \n\n None\n | \n
| OPTIONAL_APPS | \n\n [('problem_builder',\n 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'),\n ('edx_sga', None),\n ('submissions',\n 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'),\n ('openassessment',\n 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'),\n ('openassessment.assessment',\n 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'),\n ('openassessment.fileupload',\n 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'),\n ('openassessment.workflow',\n 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'),\n ('openassessment.xblock',\n 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'),\n ('edxval',\n 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'),\n ('enterprise', None),\n ('consent', None),\n ('integrated_channels.integrated_channel', None),\n ('integrated_channels.degreed', None),\n ('integrated_channels.degreed2', None),\n ('integrated_channels.sap_success_factors', None),\n ('integrated_channels.cornerstone', None),\n ('integrated_channels.xapi', None),\n ('integrated_channels.blackboard', None),\n ('integrated_channels.canvas', None),\n ('integrated_channels.moodle', None),\n ('django_object_actions', None)]\n | \n
| ORA2_FILEUPLOAD_BACKEND | \n\n 'django'\n | \n
| ORA2_FILE_PREFIX | \n\n 'default_env-default_deployment/ora2'\n | \n
| ORA_GRADING_MICROFRONTEND_URL | \n\n None\n | \n
| ORDER_HISTORY_MICROFRONTEND_URL | \n\n None\n | \n
| ORGANIZATIONS_AUTOCREATE | \n\n True\n | \n
| P3P_HEADER | \n\n 'CP="Open EdX does not have a P3P policy."'\n | \n
| PAID_COURSE_REGISTRATION_CURRENCY | \n\n ['usd', '$']\n | \n
| PARENTAL_CONSENT_AGE_LIMIT | \n\n 13\n | \n
| PARTNER_SUPPORT_EMAIL | \n\n ''\n | \n
| PASSWORD_HASHERS | \n\n '********************'\n | \n
| PASSWORD_POLICY_COMPLIANCE_ROLLOUT_CONFIG | \n\n '********************'\n | \n
| PASSWORD_RESET_EMAIL_RATE | \n\n '********************'\n | \n
| PASSWORD_RESET_IP_RATE | \n\n '********************'\n | \n
| PASSWORD_RESET_SUPPORT_LINK | \n\n '********************'\n | \n
| PASSWORD_RESET_TIMEOUT | \n\n '********************'\n | \n
| PASSWORD_RESET_TIMEOUT_DAYS | \n\n '********************'\n | \n
| PAYMENT_SUPPORT_EMAIL | \n\n 'billing@example.com'\n | \n
| PDF_RECEIPT_BILLING_ADDRESS | \n\n 'Enter your receipt billing\\naddress here.\\n'\n | \n
| PDF_RECEIPT_COBRAND_LOGO_HEIGHT_MM | \n\n 12\n | \n
| PDF_RECEIPT_COBRAND_LOGO_PATH | \n\n ''\n | \n
| PDF_RECEIPT_DISCLAIMER_TEXT | \n\n 'ENTER YOUR RECEIPT DISCLAIMER TEXT HERE.\\n'\n | \n
| PDF_RECEIPT_FOOTER_TEXT | \n\n 'Enter your receipt footer text here.\\n'\n | \n
| PDF_RECEIPT_LOGO_HEIGHT_MM | \n\n 12\n | \n
| PDF_RECEIPT_LOGO_PATH | \n\n ''\n | \n
| PDF_RECEIPT_TAX_ID | \n\n '00-0000000'\n | \n
| PDF_RECEIPT_TAX_ID_LABEL | \n\n 'fake Tax ID'\n | \n
| PDF_RECEIPT_TERMS_AND_CONDITIONS | \n\n 'Enter your receipt terms and conditions here.\\n'\n | \n
| PIPELINE | \n\n {'CSS_COMPRESSOR': None,\n 'DISABLE_WRAPPER': True,\n 'JAVASCRIPT': {'application': {'output_filename': 'js/lms-application.js',\n 'source_filenames': ['js/src/ajax_prefix.js',\n 'js/src/jquery.immediateDescendents.js',\n 'js/src/xproblem.js',\n 'common/js/xblock/core.js',\n 'common/js/xblock/runtime.v1.js',\n 'lms/js/xblock/lms.runtime.v1.js',\n 'js/src/utility.js',\n 'js/src/logger.js',\n 'js/user_dropdown_v1.js',\n 'js/dialog_tab_controls.js',\n 'js/src/string_utils.js',\n 'js/form.ext.js',\n 'js/src/ie_shim.js',\n 'js/src/accessibility_tools.js',\n 'js/toggle_login_modal.js',\n 'js/src/lang_edx.js',\n 'js/calculator.js',\n 'js/feedback_form.js',\n 'js/main.js',\n 'js/sticky_filter.js',\n 'js/query-params.js',\n 'common/js/vendor/moment-with-locales.js',\n 'common/js/vendor/moment-timezone-with-data.js']},\n 'base_application': {'output_filename': 'js/lms-base-application.js',\n 'source_filenames': ['js/src/utility.js',\n 'js/src/logger.js',\n 'js/user_dropdown_v1.js',\n 'js/dialog_tab_controls.js',\n 'js/src/string_utils.js',\n 'js/form.ext.js',\n 'js/src/ie_shim.js',\n 'js/src/accessibility_tools.js',\n 'js/toggle_login_modal.js',\n 'js/src/lang_edx.js']},\n 'base_vendor': {'output_filename': 'js/lms-base-vendor.js',\n 'source_filenames': ['common/js/vendor/jquery.js',\n 'common/js/vendor/jquery-migrate.js',\n 'js/vendor/jquery.cookie.js',\n 'js/vendor/url.min.js',\n 'common/js/vendor/underscore.js',\n 'common/js/vendor/underscore.string.js',\n 'common/js/vendor/picturefill.js',\n 'edx-ui-toolkit/js/utils/global-loader.js',\n 'edx-ui-toolkit/js/utils/string-utils.js',\n 'edx-ui-toolkit/js/utils/html-utils.js',\n 'common/js/vendor/require.js',\n 'js/RequireJS-namespace-undefine.js',\n 'js/vendor/URI.min.js',\n 'common/js/vendor/backbone.js']},\n 'ccx': {'output_filename': 'js/ccx.js',\n 'source_filenames': ['js/ccx/schedule.js']},\n 'certificates_wv': {'output_filename': 'js/certificates/web_view.js',\n 'source_filenames': ['common/js/vendor/jquery.js',\n 'common/js/vendor/jquery-migrate.js',\n 'js/vendor/jquery.cookie.js',\n 'js/src/logger.js',\n 'js/utils/facebook.js']},\n 'courseware': {'output_filename': 'js/lms-courseware.js',\n 'source_filenames': ['js/ajax-error.js',\n 'js/courseware.js',\n 'js/histogram.js',\n 'js/navigation.js',\n 'js/modules/tab.js']},\n 'credit_wv': {'output_filename': 'js/credit/web_view.js',\n 'source_filenames': ['common/js/vendor/jquery.js',\n 'common/js/vendor/jquery-migrate.js',\n 'js/vendor/jquery.cookie.js',\n 'js/src/logger.js']},\n 'dashboard': {'output_filename': 'js/dashboard.js',\n 'source_filenames': ['js/dashboard/credit.js',\n 'js/dashboard/donation.js',\n 'js/dashboard/dropdown.js',\n 'js/dashboard/legacy.js',\n 'js/dashboard/track_events.js']},\n 'discussion': {'output_filename': 'js/discussion.js',\n 'source_filenames': ['js/customwmd.js',\n 'js/mathjax_accessible.js',\n 'js/mathjax_delay_renderer.js',\n 'common/js/discussion/content.js',\n 'common/js/discussion/discussion.js',\n 'common/js/discussion/mathjax_include.js',\n 'common/js/discussion/models/discussion_course_settings.js',\n 'common/js/discussion/models/discussion_user.js',\n 'common/js/discussion/utils.js',\n 'common/js/discussion/views/discussion_content_view.js',\n 'common/js/discussion/views/discussion_inline_view.js',\n 'common/js/discussion/views/discussion_thread_edit_view.js',\n 'common/js/discussion/views/discussion_thread_list_view.js',\n 'common/js/discussion/views/discussion_thread_profile_view.js',\n 'common/js/discussion/views/discussion_thread_show_view.js',\n 'common/js/discussion/views/discussion_thread_view.js',\n 'common/js/discussion/views/discussion_topic_menu_view.js',\n 'common/js/discussion/views/new_post_view.js',\n 'common/js/discussion/views/response_comment_edit_view.js',\n 'common/js/discussion/views/response_comment_show_view.js',\n 'common/js/discussion/views/response_comment_view.js',\n 'common/js/discussion/views/thread_response_edit_view.js',\n 'common/js/discussion/views/thread_response_show_view.js',\n 'common/js/discussion/views/thread_response_view.js']},\n 'discussion_vendor': {'output_filename': 'js/discussion_vendor.js',\n 'source_filenames': ['js/Markdown.Converter.js',\n 'js/Markdown.Sanitizer.js',\n 'js/Markdown.Editor.js',\n 'js/vendor/jquery.timeago.js',\n 'js/src/jquery.timeago.locale.js',\n 'js/vendor/jquery.truncate.js',\n 'js/jquery.ajaxfileupload.js',\n 'js/split.js']},\n 'footer_edx': {'output_filename': 'js/footer-edx.js',\n 'source_filenames': ['js/footer-edx.js']},\n 'incourse_reverify': {'output_filename': 'js/incourse_reverify.js',\n 'source_filenames': ['js/verify_student/views/error_view.js',\n 'js/verify_student/views/image_input_view.js',\n 'js/verify_student/views/webcam_photo_view.js',\n 'js/verify_student/models/verification_model.js',\n 'js/verify_student/views/incourse_reverify_view.js',\n 'js/verify_student/incourse_reverify.js']},\n 'instructor_dash': {'output_filename': 'js/instructor_dash.js',\n 'source_filenames': ['js/instructor_dashboard/certificates.js',\n 'js/instructor_dashboard/cohort_management.js',\n 'js/instructor_dashboard/course_info.js',\n 'js/instructor_dashboard/data_download.js',\n 'js/instructor_dashboard/data_download_2.js',\n 'js/instructor_dashboard/discussions_management.js',\n 'js/instructor_dashboard/e-commerce.js',\n 'js/instructor_dashboard/ecommerce.js',\n 'js/instructor_dashboard/extensions.js',\n 'js/instructor_dashboard/instructor_dashboard.js',\n 'js/instructor_dashboard/membership.js',\n 'js/instructor_dashboard/metrics.js',\n 'js/instructor_dashboard/open_response_assessment.js',\n 'js/instructor_dashboard/proctoring.js',\n 'js/instructor_dashboard/send_email.js',\n 'js/instructor_dashboard/student_admin.js',\n 'js/instructor_dashboard/util.js']},\n 'main_vendor': {'output_filename': 'js/lms-main_vendor.js',\n 'source_filenames': ['common/js/vendor/jquery.js',\n 'common/js/vendor/jquery-migrate.js',\n 'js/vendor/jquery.cookie.js',\n 'js/vendor/url.min.js',\n 'common/js/vendor/underscore.js',\n 'common/js/vendor/underscore.string.js',\n 'common/js/vendor/picturefill.js',\n 'edx-ui-toolkit/js/utils/global-loader.js',\n 'edx-ui-toolkit/js/utils/string-utils.js',\n 'edx-ui-toolkit/js/utils/html-utils.js',\n 'common/js/vendor/require.js',\n 'js/RequireJS-namespace-undefine.js',\n 'js/vendor/URI.min.js',\n 'common/js/vendor/backbone.js',\n 'js/vendor/json2.js',\n 'js/vendor/jquery-ui.min.js',\n 'js/vendor/jquery.qtip.min.js',\n 'js/vendor/jquery.ba-bbq.min.js']},\n 'module-descriptor-js': {'output_filename': 'js/lms-module-descriptors.js',\n 'source_filenames': ['xmodule/descriptors/js/000-58032517f54c5c1a704a908d850cbe64.js',\n 'xmodule/descriptors/js/001-043e45378109d53c4919131b4001dff2.js',\n 'xmodule/descriptors/js/001-81f6a04b11b9b6b4b1bcd710dcf5777a.js',\n 'xmodule/descriptors/js/001-8723f83c97a354f267ea559bc714ee1a.js',\n 'xmodule/descriptors/js/001-91056aaf5a20b34890b4f435926e57f5.js',\n 'xmodule/descriptors/js/001-c88750f95f4884a8666f9261eecfa285.js',\n 'xmodule/descriptors/js/001-d7842ab69993e5eb58e8d4a4e80c23a2.js',\n 'xmodule/descriptors/js/001-f7c2cfb3cff0dd3aefa932f8e02d1435.js']},\n 'module-js': {'output_filename': 'js/lms-modules.js',\n 'source_filenames': ['xmodule/modules/js/000-58032517f54c5c1a704a908d850cbe64.js',\n 'xmodule/modules/js/001-3918b2d4f383c04fed8227cc9f523d6e.js',\n 'xmodule/modules/js/001-3ed86006526f75d6c844739193a84c11.js',\n 'xmodule/modules/js/001-550e26b7e4efbc0c68a580f6dbecf66c.js',\n 'xmodule/modules/js/001-8705061d9ba87dfcb875b9db3026aff6.js',\n 'xmodule/modules/js/001-8ba509e3404fc2aea58a9e95e718fefb.js',\n 'xmodule/modules/js/001-ce60a84636ea45ab98f1d6e5bfc70965.js',\n 'xmodule/modules/js/001-e45ae357eab0e4e2784fa6d04d4e16c1.js',\n 'xmodule/modules/js/002-37ce0eea8f9a85c26819ef3b8b3a37d4.js',\n 'xmodule/modules/js/002-3918b2d4f383c04fed8227cc9f523d6e.js',\n 'xmodule/modules/js/002-8b615179a51250c4dc203f1daa97be63.js',\n 'xmodule/modules/js/002-e32c61651b0379c8503ad932a91e7651.js',\n 'xmodule/modules/js/003-3918b2d4f383c04fed8227cc9f523d6e.js',\n 'xmodule/modules/js/003-b3206f2283964743c4772b9d72c67d64.js',\n 'xmodule/modules/js/003-e9bdd6eeb6d44351f9504a2e45b3fa98.js',\n 'xmodule/modules/js/004-866df6ea65aa331217cdf46290ead28e.js',\n 'xmodule/modules/js/004-b0c34afa95eaa6b45d843d92ca523a94.js',\n 'xmodule/modules/js/004-b3206f2283964743c4772b9d72c67d64.js',\n 'xmodule/modules/js/005-26caba6f71877f63a7dd4f6796109bf6.js',\n 'xmodule/modules/js/005-fc8bd2dc5b96b86d1abefdd417dd8ba5.js']},\n 'proctoring': {'output_filename': 'js/lms-proctoring.js',\n 'source_filenames': ['proctoring/js/models/proctored_exam_allowance_model.js',\n 'proctoring/js/models/proctored_exam_attempt_model.js',\n 'proctoring/js/models/proctored_exam_bulk_allowance_model.js',\n 'proctoring/js/models/proctored_exam_model.js',\n 'proctoring/js/models/learner_onboarding_model.js',\n 'proctoring/js/collections/proctored_exam_allowance_collection.js',\n 'proctoring/js/collections/proctored_exam_attempt_grouped_collection.js',\n 'proctoring/js/collections/proctored_exam_onboarding_collection.js',\n 'proctoring/js/collections/proctored_exam_collection.js',\n 'proctoring/js/views/Backbone.ModalDialog.js',\n 'proctoring/js/views/proctored_exam_add_allowance_view.js',\n 'proctoring/js/views/proctored_exam_add_bulk_allowance_view.js',\n 'proctoring/js/views/proctored_exam_allowance_view.js',\n 'proctoring/js/views/proctored_exam_attempt_view.js',\n 'proctoring/js/views/proctored_exam_edit_allowance_view.js',\n 'proctoring/js/views/proctored_exam_onboarding_view.js',\n 'proctoring/js/views/proctored_exam_view.js',\n 'proctoring/js/views/proctored_exam_info.js',\n 'proctoring/js/views/proctored_exam_instructor_launch.js',\n 'proctoring/js/proctored_app.js',\n 'proctoring/js/exam_action_handler.js',\n 'proctoring/js/dropdown.js']},\n 'reverify': {'output_filename': 'js/reverify.js',\n 'source_filenames': ['js/verify_student/views/error_view.js',\n 'js/verify_student/views/image_input_view.js',\n 'js/verify_student/views/webcam_photo_view.js',\n 'js/verify_student/views/step_view.js',\n 'js/verify_student/views/face_photo_step_view.js',\n 'js/verify_student/views/id_photo_step_view.js',\n 'js/verify_student/views/review_photos_step_view.js',\n 'js/verify_student/views/reverify_success_step_view.js',\n 'js/verify_student/models/verification_model.js',\n 'js/verify_student/views/reverify_view.js',\n 'js/verify_student/reverify.js']},\n 'verify_student': {'output_filename': 'js/verify_student.js',\n 'source_filenames': ['js/sticky_filter.js',\n 'js/query-params.js',\n 'js/verify_student/models/verification_model.js',\n 'js/verify_student/views/error_view.js',\n 'js/verify_student/views/image_input_view.js',\n 'js/verify_student/views/webcam_photo_view.js',\n 'js/verify_student/views/step_view.js',\n 'js/verify_student/views/intro_step_view.js',\n 'js/verify_student/views/make_payment_step_view.js',\n 'js/verify_student/views/face_photo_step_view.js',\n 'js/verify_student/views/id_photo_step_view.js',\n 'js/verify_student/views/review_photos_step_view.js',\n 'js/verify_student/views/enrollment_confirmation_step_view.js',\n 'js/verify_student/views/pay_and_verify_view.js',\n 'js/verify_student/pay_and_verify.js']}},\n 'JS_COMPRESSOR': None,\n 'PIPELINE_ENABLED': False,\n 'SASS_ARGUMENTS': '--debug-info',\n 'STYLESHEETS': {'style-certificates': {'output_filename': 'css/certificates-style.css',\n 'source_filenames': ['certificates/css/main-ltr.css',\n 'css/vendor/font-awesome.css']},\n 'style-certificates-rtl': {'output_filename': 'css/certificates-style-rtl.css',\n 'source_filenames': ['certificates/css/main-rtl.css',\n 'css/vendor/font-awesome.css']},\n 'style-course': {'output_filename': 'css/lms-course.css',\n 'source_filenames': ['css/lms-course.css']},\n 'style-course-rtl': {'output_filename': 'css/lms-course-rtl.css',\n 'source_filenames': ['css/lms-course-rtl.css']},\n 'style-course-vendor': {'output_filename': 'css/lms-style-course-vendor.css',\n 'source_filenames': ['js/vendor/CodeMirror/codemirror.css',\n 'css/vendor/jquery.treeview.css',\n 'css/vendor/ui-lightness/jquery-ui-1.8.22.custom.css']},\n 'style-inline-discussion': {'output_filename': 'css/discussion/inline-discussion.css',\n 'source_filenames': ['css/discussion/inline-discussion.css']},\n 'style-inline-discussion-rtl': {'output_filename': 'css/discussion/inline-discussion-rtl.css',\n 'source_filenames': ['css/discussion/inline-discussion-rtl.css']},\n 'style-lms-footer': {'output_filename': 'css/lms-footer.css',\n 'source_filenames': ['css/lms-footer.css']},\n 'style-lms-footer-edx': {'output_filename': 'css/lms-footer-edx.css',\n 'source_filenames': ['css/lms-footer-edx.css']},\n 'style-lms-footer-edx-rtl': {'output_filename': 'css/lms-footer-edx-rtl.css',\n 'source_filenames': ['css/lms-footer-edx-rtl.css']},\n 'style-lms-footer-rtl': {'output_filename': 'css/lms-footer-rtl.css',\n 'source_filenames': ['css/lms-footer-rtl.css']},\n 'style-main-v1': {'output_filename': 'css/lms-main-v1.css',\n 'source_filenames': ['css/lms-main-v1.css']},\n 'style-main-v1-rtl': {'output_filename': 'css/lms-main-v1-rtl.css',\n 'source_filenames': ['css/lms-main-v1-rtl.css']},\n 'style-mobile': {'output_filename': 'css/lms-mobile.css',\n 'source_filenames': ['css/lms-mobile.css']},\n 'style-mobile-rtl': {'output_filename': 'css/lms-mobile-rtl.css',\n 'source_filenames': ['css/lms-mobile-rtl.css']},\n 'style-student-notes': {'output_filename': 'css/lms-style-student-notes.css',\n 'source_filenames': ['css/vendor/edxnotes/annotator.min.css']},\n 'style-vendor': {'output_filename': 'css/lms-style-vendor.css',\n 'source_filenames': ['css/vendor/font-awesome.css',\n 'css/vendor/jquery.qtip.min.css']},\n 'style-vendor-tinymce-content': {'output_filename': 'css/lms-style-vendor-tinymce-content.css',\n 'source_filenames': ['js/vendor/tinymce/js/tinymce/skins/studio-tmce4/content.min.css']},\n 'style-vendor-tinymce-skin': {'output_filename': 'css/lms-style-vendor-tinymce-skin.css',\n 'source_filenames': ['js/vendor/tinymce/js/tinymce/skins/studio-tmce4/skin.min.css']}},\n 'UGLIFYJS_BINARY': 'node_modules/.bin/uglifyjs'}\n | \n
| PLATFORM_DESCRIPTION | \n\n 'Your Platform Description Here'\n | \n
| PLATFORM_FACEBOOK_ACCOUNT | \n\n 'http://www.facebook.com/YourPlatformFacebookAccount'\n | \n
| PLATFORM_NAME | \n\n 'Your Platform Name Here'\n | \n
| PLATFORM_TWITTER_ACCOUNT | \n\n '@YourPlatformTwitterAccount'\n | \n
| POLICY_CHANGE_GRADES_ROUTING_KEY | \n\n '********************'\n | \n
| POLICY_CHANGE_TASK_RATE_LIMIT | \n\n '300/h'\n | \n
| PREPEND_WWW | \n\n False\n | \n
| PRESS_EMAIL | \n\n 'press@example.com'\n | \n
| PREVIEW_DOMAIN | \n\n 'preview.localhost'\n | \n
| PROCTORED_EXAM_VIEWABLE_PAST_DUE | \n\n False\n | \n
| PROCTORING_BACKENDS | \n\n {'DEFAULT': 'null', 'null': {}}\n | \n
| PROCTORING_SETTINGS | \n\n {}\n | \n
| PROCTORING_USER_OBFUSCATION_KEY | \n\n '********************'\n | \n
| PROFILE_IMAGE_BACKEND | \n\n {'class': 'openedx.core.storage.OverwriteStorage',\n 'options': {'base_url': '/media/profile-images/',\n 'location': '/edx/var/edxapp/media/profile-images/'}}\n | \n
| PROFILE_IMAGE_DEFAULT_FILENAME | \n\n 'images/profiles/default'\n | \n
| PROFILE_IMAGE_DEFAULT_FILE_EXTENSION | \n\n 'png'\n | \n
| PROFILE_IMAGE_HASH_SEED | \n\n 'placeholder_secret_key'\n | \n
| PROFILE_IMAGE_MAX_BYTES | \n\n 1048576\n | \n
| PROFILE_IMAGE_MIN_BYTES | \n\n 100\n | \n
| PROFILE_IMAGE_SIZES_MAP | \n\n {'full': 500, 'large': 120, 'medium': 50, 'small': 30}\n | \n
| PROFILE_MICROFRONTEND_URL | \n\n None\n | \n
| PROGRAM_CERTIFICATES_ROUTING_KEY | \n\n '********************'\n | \n
| PROGRAM_CONSOLE_MICROFRONTEND_URL | \n\n None\n | \n
| PROGRESS_HELP_URL | \n\n 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_assets/pages.html?highlight=progress#hiding-or-showing-the-wiki-or-progress-pages'\n | \n
| PROJECT_ROOT | \n\n Path('/edx/app/edxapp/edx-platform/lms')\n | \n
| PYTHON_LIB_FILENAME | \n\n 'python_lib.zip'\n | \n
| QUEUE_VARIANT | \n\n 'lms.'\n | \n
| RATELIMIT_ENABLE | \n\n True\n | \n
| RATELIMIT_RATE | \n\n '120/m'\n | \n
| RATE_LIMIT_FOR_VIDEO_METADATA_API | \n\n '********************'\n | \n
| RECALCULATE_GRADES_ROUTING_KEY | \n\n '********************'\n | \n
| REDIRECT_CACHE_KEY_PREFIX | \n\n '********************'\n | \n
| REDIRECT_CACHE_TIMEOUT | \n\n None\n | \n
| REGISTRATION_CODE_LENGTH | \n\n 8\n | \n
| REGISTRATION_EMAIL_PATTERNS_ALLOWED | \n\n None\n | \n
| REGISTRATION_EXTENSION_FORM | \n\n None\n | \n
| REGISTRATION_EXTRA_FIELDS | \n\n {'city': 'hidden',\n 'confirm_email': 'hidden',\n 'country': 'required',\n 'gender': 'optional',\n 'goals': 'optional',\n 'honor_code': 'required',\n 'level_of_education': 'optional',\n 'mailing_address': 'hidden',\n 'terms_of_service': 'hidden',\n 'year_of_birth': 'optional'}\n | \n
| REGISTRATION_FIELD_ORDER | \n\n ['name',\n 'first_name',\n 'last_name',\n 'username',\n 'email',\n 'confirm_email',\n 'password',\n 'city',\n 'state',\n 'country',\n 'gender',\n 'year_of_birth',\n 'level_of_education',\n 'specialty',\n 'professioncompany',\n 'title',\n 'mailing_address',\n 'goals',\n 'honor_code',\n 'terms_of_service']\n | \n
| REGISTRATION_RATELIMIT | \n\n '60/7d'\n | \n
| REGISTRATION_VALIDATION_RATELIMIT | \n\n '30/7d'\n | \n
| REPO_ROOT | \n\n Path('/edx/app/edxapp/edx-platform')\n | \n
| REQUIRE_BASE_URL | \n\n './'\n | \n
| REQUIRE_BUILD_PROFILE | \n\n 'lms/js/build.js'\n | \n
| REQUIRE_DEBUG | \n\n True\n | \n
| REQUIRE_JS | \n\n 'common/js/vendor/require.js'\n | \n
| REQUIRE_JS_PATH_OVERRIDES | \n\n {'course_bookmarks/js/views/bookmark_button': 'course_bookmarks/js/views/bookmark_button.js',\n 'draggabilly': 'js/vendor/draggabilly.js',\n 'hls': 'common/js/vendor/hls.js',\n 'js/courseware/accordion_events': 'js/courseware/accordion_events.js',\n 'js/courseware/course_info_events': 'js/courseware/course_info_events.js',\n 'js/courseware/courseware_factory': 'js/courseware/courseware_factory.js',\n 'js/courseware/link_clicked_events': 'js/courseware/link_clicked_events.js',\n 'js/courseware/toggle_element_visibility': 'js/courseware/toggle_element_visibility.js',\n 'js/dateutil_factory': 'js/dateutil_factory.js',\n 'js/groups/discussions_management/discussions_dashboard_factory': 'js/discussions_management/views/discussions_dashboard_factory.js',\n 'js/groups/views/cohorts_dashboard_factory': 'js/groups/views/cohorts_dashboard_factory.js',\n 'js/student_account/logistration_factory': 'js/student_account/logistration_factory.js',\n 'js/views/message_banner': 'js/views/message_banner.js',\n 'moment': 'common/js/vendor/moment-with-locales.js',\n 'moment-timezone': 'common/js/vendor/moment-timezone-with-data.js'}\n | \n
| RESET_PASSWORD_API_RATELIMIT | \n\n '********************'\n | \n
| RESET_PASSWORD_TOKEN_VALIDATE_API_RATELIMIT | \n\n '********************'\n | \n
| REST_FRAMEWORK | \n\n {'DEFAULT_PAGINATION_CLASS': 'edx_rest_framework_extensions.paginators.DefaultPagination',\n 'DEFAULT_RENDERER_CLASSES': ('rest_framework.renderers.JSONRenderer',\n 'rest_framework.renderers.BrowsableAPIRenderer'),\n 'DEFAULT_THROTTLE_RATES': {'registration_validation': '30/minute',\n 'service_user': '800/minute',\n 'user': '60/minute'},\n 'EXCEPTION_HANDLER': 'openedx.core.lib.request_utils.expected_error_exception_handler',\n 'PAGE_SIZE': 10,\n 'URL_FORMAT_OVERRIDE': None}\n | \n
| RETIRED_EMAIL_DOMAIN | \n\n 'retired.invalid'\n | \n
| RETIRED_EMAIL_FMT | \n\n 'retired__user_{}@retired.invalid'\n | \n
| RETIRED_EMAIL_PREFIX | \n\n 'retired__user_'\n | \n
| RETIRED_USERNAME_FMT | \n\n 'retired__user_{}'\n | \n
| RETIRED_USERNAME_PREFIX | \n\n 'retired__user_'\n | \n
| RETIRED_USER_SALTS | \n\n ['OVERRIDE ME WITH A RANDOM VALUE', 'ROTATE SALTS BY APPENDING NEW VALUES']\n | \n
| RETIREMENT_SERVICE_WORKER_USERNAME | \n\n 'retirement_worker'\n | \n
| RETIREMENT_STATES | \n\n ['PENDING', 'ERRORED', 'ABORTED', 'COMPLETE']\n | \n
| RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS | \n\n 5\n | \n
| RETRY_ACTIVATION_EMAIL_TIMEOUT | \n\n 0.5\n | \n
| RETRY_CALENDAR_SYNC_EMAIL_MAX_ATTEMPTS | \n\n 5\n | \n
| REVISION_CONFIG | \n\n {'EDX_PLATFORM_REVISION': 'master'}\n | \n
| REVISION_CONFIG_FILE | \n\n '/edx/etc/revisions.yml'\n | \n
| ROOT_URLCONF | \n\n 'lms.urls'\n | \n
| RSS_PROXY_CACHE_TIMEOUT | \n\n 3600\n | \n
| SAVE_FOR_LATER_EMAIL_RATE_LIMIT | \n\n '5/h'\n | \n
| SAVE_FOR_LATER_IP_RATE_LIMIT | \n\n '100/d'\n | \n
| SEARCH_ENGINE | \n\n 'search.elastic.ElasticSearchEngine'\n | \n
| SEARCH_FILTER_GENERATOR | \n\n 'lms.lib.courseware_search.lms_filter_generator.LmsSearchFilterGenerator'\n | \n
| SEARCH_INITIALIZER | \n\n 'lms.lib.courseware_search.lms_search_initializer.LmsSearchInitializer'\n | \n
| SEARCH_RESULT_PROCESSOR | \n\n 'lms.lib.courseware_search.lms_result_processor.LmsSearchResultProcessor'\n | \n
| SEARCH_SKIP_ENROLLMENT_START_DATE_FILTERING | \n\n True\n | \n
| SECRET_KEY | \n\n '********************'\n | \n
| SECURE_BROWSER_XSS_FILTER | \n\n False\n | \n
| SECURE_CONTENT_TYPE_NOSNIFF | \n\n True\n | \n
| SECURE_HSTS_INCLUDE_SUBDOMAINS | \n\n False\n | \n
| SECURE_HSTS_PRELOAD | \n\n False\n | \n
| SECURE_HSTS_SECONDS | \n\n 0\n | \n
| SECURE_PROXY_SSL_HEADER | \n\n ('HTTP_X_FORWARDED_PROTO', 'https')\n | \n
| SECURE_REDIRECT_EXEMPT | \n\n []\n | \n
| SECURE_REFERRER_POLICY | \n\n 'same-origin'\n | \n
| SECURE_SSL_HOST | \n\n None\n | \n
| SECURE_SSL_REDIRECT | \n\n False\n | \n
| SEGMENT_KEY | \n\n '********************'\n | \n
| SERVER_EMAIL | \n\n 'sre@example.com'\n | \n
| SERVICE_VARIANT | \n\n 'lms'\n | \n
| SESSION_CACHE_ALIAS | \n\n 'default'\n | \n
| SESSION_COOKIE_AGE | \n\n 1209600\n | \n
| SESSION_COOKIE_DOMAIN | \n\n ''\n | \n
| SESSION_COOKIE_HTTPONLY | \n\n True\n | \n
| SESSION_COOKIE_NAME | \n\n 'lms_sessionid'\n | \n
| SESSION_COOKIE_PATH | \n\n '/'\n | \n
| SESSION_COOKIE_SAMESITE | \n\n 'Lax'\n | \n
| SESSION_COOKIE_SECURE | \n\n False\n | \n
| SESSION_ENGINE | \n\n 'django.contrib.sessions.backends.cache'\n | \n
| SESSION_EXPIRE_AT_BROWSER_CLOSE | \n\n False\n | \n
| SESSION_FILE_PATH | \n\n None\n | \n
| SESSION_INACTIVITY_TIMEOUT_IN_SECONDS | \n\n None\n | \n
| SESSION_SAVE_EVERY_REQUEST | \n\n False\n | \n
| SESSION_SERIALIZER | \n\n 'openedx.core.lib.session_serializers.PickleSerializer'\n | \n
| SETTINGS_MODULE | \n\n 'lms.envs.devstack_docker'\n | \n
| SHARED_COOKIE_DOMAIN | \n\n ''\n | \n
| SHIBBOLETH_DOMAIN_PREFIX | \n\n 'shib:'\n | \n
| SHORT_DATETIME_FORMAT | \n\n 'm/d/Y P'\n | \n
| SHORT_DATE_FORMAT | \n\n 'm/d/Y'\n | \n
| SHOW_ACCOUNT_ACTIVATION_CTA | \n\n False\n | \n
| SHOW_ACTIVATE_CTA_POPUP_COOKIE_NAME | \n\n 'show-account-activation-popup'\n | \n
| SIGNING_BACKEND | \n\n 'django.core.signing.TimestampSigner'\n | \n
| SILENCED_SYSTEM_CHECKS | \n\n []\n | \n
| SIMPLE_WIKI_REQUIRE_LOGIN_EDIT | \n\n True\n | \n
| SIMPLE_WIKI_REQUIRE_LOGIN_VIEW | \n\n False\n | \n
| SITE_ID | \n\n 1\n | \n
| SITE_NAME | \n\n 'localhost:18000'\n | \n
| SOCIAL_AUTH_AZUREAD_OAUTH2_AUTH_EXTRA_ARGUMENTS | \n\n {'msafed': 0}\n | \n
| SOCIAL_AUTH_CLEAN_USERNAMES | \n\n True\n | \n
| SOCIAL_AUTH_CLEAN_USERNAME_FUNCTION | \n\n 'common.djangoapps.third_party_auth.models.clean_username'\n | \n
| SOCIAL_AUTH_INACTIVE_USER_LOGIN | \n\n True\n | \n
| SOCIAL_AUTH_INACTIVE_USER_URL | \n\n '/auth/inactive'\n | \n
| SOCIAL_AUTH_LOGIN_ERROR_URL | \n\n '/'\n | \n
| SOCIAL_AUTH_LOGIN_REDIRECT_URL | \n\n '/dashboard'\n | \n
| SOCIAL_AUTH_LTI_CONSUMER_SECRETS | \n\n '********************'\n | \n
| SOCIAL_AUTH_OAUTH_SECRETS | \n\n '********************'\n | \n
| SOCIAL_AUTH_PIPELINE | \n\n ['common.djangoapps.third_party_auth.pipeline.parse_query_params',\n 'social_core.pipeline.social_auth.social_details',\n 'social_core.pipeline.social_auth.social_uid',\n 'social_core.pipeline.social_auth.auth_allowed',\n 'social_core.pipeline.social_auth.social_user',\n 'common.djangoapps.third_party_auth.pipeline.associate_by_email_if_login_api',\n 'common.djangoapps.third_party_auth.pipeline.associate_by_email_if_saml',\n 'common.djangoapps.third_party_auth.pipeline.associate_by_email_if_oauth',\n 'common.djangoapps.third_party_auth.pipeline.get_username',\n 'common.djangoapps.third_party_auth.pipeline.set_pipeline_timeout',\n 'common.djangoapps.third_party_auth.pipeline.ensure_user_information',\n 'social_core.pipeline.user.create_user',\n 'social_core.pipeline.social_auth.associate_user',\n 'enterprise.tpa_pipeline.handle_enterprise_logistration',\n 'social_core.pipeline.social_auth.load_extra_data',\n 'social_core.pipeline.user.user_details',\n 'common.djangoapps.third_party_auth.pipeline.user_details_force_sync',\n 'common.djangoapps.third_party_auth.pipeline.set_id_verification_status',\n 'common.djangoapps.third_party_auth.pipeline.set_logged_in_cookies',\n 'common.djangoapps.third_party_auth.pipeline.login_analytics']\n | \n
| SOCIAL_AUTH_PIPELINE_TIMEOUT | \n\n 600\n | \n
| SOCIAL_AUTH_PROTECTED_USER_FIELDS | \n\n ['email']\n | \n
| SOCIAL_AUTH_RAISE_EXCEPTIONS | \n\n False\n | \n
| SOCIAL_AUTH_SAML_SP_PRIVATE_KEY | \n\n '********************'\n | \n
| SOCIAL_AUTH_SAML_SP_PRIVATE_KEY_DICT | \n\n '********************'\n | \n
| SOCIAL_AUTH_SAML_SP_PUBLIC_CERT | \n\n ''\n | \n
| SOCIAL_AUTH_SAML_SP_PUBLIC_CERT_DICT | \n\n {}\n | \n
| SOCIAL_AUTH_SANITIZE_REDIRECTS | \n\n False\n | \n
| SOCIAL_AUTH_STRATEGY | \n\n 'common.djangoapps.third_party_auth.strategy.ConfigurationModelStrategy'\n | \n
| SOCIAL_AUTH_UUID_LENGTH | \n\n 4\n | \n
| SOCIAL_MEDIA_FOOTER_DISPLAY | \n\n {'facebook': {'action': 'Like {platform_name} on Facebook',\n 'icon': 'fa-facebook-square',\n 'title': 'Facebook'},\n 'instagram': {'action': 'Follow {platform_name} on Instagram',\n 'icon': 'fa-instagram',\n 'title': 'Instagram'},\n 'linkedin': {'action': 'Follow {platform_name} on LinkedIn',\n 'icon': 'fa-linkedin-square',\n 'title': 'LinkedIn'},\n 'meetup': {'icon': 'fa-calendar', 'title': 'Meetup'},\n 'reddit': {'action': 'Subscribe to the {platform_name} subreddit',\n 'icon': 'fa-reddit-square',\n 'title': 'Reddit'},\n 'tumblr': {'icon': 'fa-tumblr', 'title': 'Tumblr'},\n 'twitter': {'action': 'Follow {platform_name} on Twitter',\n 'icon': 'fa-twitter-square',\n 'title': 'Twitter'},\n 'vk': {'icon': 'fa-vk', 'title': 'VK'},\n 'weibo': {'icon': 'fa-weibo', 'title': 'Weibo'},\n 'youtube': {'action': 'Subscribe to the {platform_name} YouTube channel',\n 'icon': 'fa-youtube-square',\n 'title': 'Youtube'}}\n | \n
| SOCIAL_MEDIA_FOOTER_NAMES | \n\n ['facebook', 'twitter', 'linkedin', 'instagram', 'reddit']\n | \n
| SOCIAL_MEDIA_FOOTER_URLS | \n\n {}\n | \n
| SOCIAL_PLATFORMS | \n\n {'facebook': {'display_name': 'Facebook',\n 'example': 'https://www.facebook.com/username',\n 'url_stub': 'facebook.com/'},\n 'linkedin': {'display_name': 'LinkedIn',\n 'example': 'www.linkedin.com/in/username',\n 'url_stub': 'linkedin.com/in/'},\n 'twitter': {'display_name': 'Twitter',\n 'example': 'https://www.twitter.com/username',\n 'url_stub': 'twitter.com/'}}\n | \n
| SOCIAL_SHARING_SETTINGS | \n\n {'CERTIFICATE_FACEBOOK': False,\n 'CERTIFICATE_TWITTER': False,\n 'CUSTOM_COURSE_URLS': False,\n 'DASHBOARD_FACEBOOK': False,\n 'DASHBOARD_TWITTER': False}\n | \n
| SOFTWARE_SECURE_REQUEST_RETRY_DELAY | \n\n 3600\n | \n
| SOFTWARE_SECURE_RETRY_MAX_ATTEMPTS | \n\n 6\n | \n
| SOFTWARE_SECURE_VERIFICATION_ROUTING_KEY | \n\n '********************'\n | \n
| SSL_AUTH_DN_FORMAT_STRING | \n\n ('/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client CA '\n 'v1/CN={0}/emailAddress={1}')\n | \n
| SSL_AUTH_EMAIL_DOMAIN | \n\n 'MIT.EDU'\n | \n
| STATICFILES_DIRS | \n\n [Path('/edx/app/edxapp/edx-platform/common/static'),\n Path('/edx/app/edxapp/edx-platform/lms/static'),\n Path('/edx/app/edxapp/edx-platform/node_modules/@edx')]\n | \n
| STATICFILES_FINDERS | \n\n ['openedx.core.djangoapps.theming.finders.ThemeFilesFinder',\n 'django.contrib.staticfiles.finders.FileSystemFinder',\n 'django.contrib.staticfiles.finders.AppDirectoriesFinder']\n | \n
| STATICFILES_IGNORE_PATTERNS | \n\n ('*.py',\n '*.pyc',\n 'sass/*.scss',\n 'sass/*/*.scss',\n 'sass/*/*/*.scss',\n 'sass/*/*/*/*.scss',\n 'spec',\n 'spec_helpers',\n 'xmodule_js')\n | \n
| STATICFILES_STORAGE | \n\n 'openedx.core.storage.DevelopmentStorage'\n | \n
| STATICFILES_STORAGE_KWARGS | \n\n {}\n | \n
| STATICI18N_DOMAIN | \n\n 'djangojs'\n | \n
| STATICI18N_FILENAME_FUNCTION | \n\n 'statici18n.utils.legacy_filename'\n | \n
| STATICI18N_NAMESPACE | \n\n None\n | \n
| STATICI18N_OUTPUT_DIR | \n\n 'js/i18n'\n | \n
| STATICI18N_PACKAGES | \n\n 'django.conf'\n | \n
| STATICI18N_ROOT | \n\n Path('/edx/app/edxapp/edx-platform/lms/static')\n | \n
| STATIC_GRAB | \n\n False\n | \n
| STATIC_ROOT | \n\n Path('/edx/var/edxapp/staticfiles')\n | \n
| STATIC_ROOT_BASE | \n\n '/edx/var/edxapp/staticfiles'\n | \n
| STATIC_TEMPLATE_VIEW_DEFAULT_FILE_EXTENSION | \n\n 'html'\n | \n
| STATIC_URL | \n\n '/static/'\n | \n
| STATIC_URL_BASE | \n\n '/static/'\n | \n
| STATUS_MESSAGE_PATH | \n\n Path('/edx/app/edxapp/status_message.json')\n | \n
| STUDENTMODULEHISTORYEXTENDED_OFFSET | \n\n 10000\n | \n
| STUDENT_FILEUPLOAD_MAX_SIZE | \n\n 4000000\n | \n
| STUDIO_NAME | \n\n 'Studio'\n | \n
| STUDIO_SHORT_NAME | \n\n 'Studio'\n | \n
| SUPPORT_HOW_TO_UNENROLL_LINK | \n\n ''\n | \n
| SUPPORT_SITE_LINK | \n\n ''\n | \n
| SWAGGER_SETTINGS | \n\n {'DEFAULT_INFO': 'openedx.core.apidocs.api_info'}\n | \n
| SWIFT_AUTH_URL | \n\n None\n | \n
| SWIFT_AUTH_VERSION | \n\n None\n | \n
| SWIFT_KEY | \n\n '********************'\n | \n
| SWIFT_REGION_NAME | \n\n None\n | \n
| SWIFT_TEMP_URL_DURATION | \n\n 1800\n | \n
| SWIFT_TEMP_URL_KEY | \n\n '********************'\n | \n
| SWIFT_TENANT_ID | \n\n None\n | \n
| SWIFT_TENANT_NAME | \n\n None\n | \n
| SWIFT_USERNAME | \n\n None\n | \n
| SWIFT_USE_TEMP_URLS | \n\n False\n | \n
| SYSLOG_SERVER | \n\n ''\n | \n
| SYSTEM_TO_FEATURE_ROLE_MAPPING | \n\n {'enterprise_admin': ['dashboard_admin',\n 'catalog_admin',\n 'enrollment_api_admin',\n 'reporting_config_admin'],\n 'enterprise_openedx_operator': ['dashboard_admin',\n 'catalog_admin',\n 'enrollment_api_admin',\n 'reporting_config_admin']}\n | \n
| SYSTEM_WIDE_ROLE_CLASSES | \n\n ['system_wide_roles.SystemWideRoleAssignment',\n 'enterprise.SystemWideEnterpriseUserRoleAssignment']\n | \n
| TEAMS_HELP_URL | \n\n 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_features/teams/teams_setup.html'\n | \n
| TECH_SUPPORT_EMAIL | \n\n 'technical@example.com'\n | \n
| TEMPLATES | \n\n [{'APP_DIRS': False,\n 'BACKEND': 'django.template.backends.django.DjangoTemplates',\n 'DIRS': [Path('/edx/app/edxapp/edx-platform/lms/templates'),\n Path('/edx/app/edxapp/edx-platform/common/templates'),\n Path('/edx/app/edxapp/edx-platform/common/lib/capa/capa/templates'),\n Path('/edx/app/edxapp/edx-platform/common/djangoapps/pipeline_mako/templates'),\n Path('/edx/app/edxapp/edx-platform/common/static')],\n 'NAME': 'django',\n 'OPTIONS': {'context_processors': ['django.template.context_processors.request',\n 'django.template.context_processors.static',\n 'django.template.context_processors.i18n',\n 'django.contrib.auth.context_processors.auth',\n 'django.template.context_processors.csrf',\n 'django.template.context_processors.media',\n 'django.template.context_processors.tz',\n 'django.contrib.messages.context_processors.messages',\n 'sekizai.context_processors.sekizai',\n 'common.djangoapps.edxmako.shortcuts.marketing_link_context_processor',\n 'lms.djangoapps.courseware.context_processor.user_timezone_locale_prefs',\n 'help_tokens.context_processor',\n 'openedx.core.djangoapps.site_configuration.context_processors.configuration_context',\n 'lms.djangoapps.mobile_api.context_processor.is_from_mobile_app',\n 'social_django.context_processors.backends',\n 'social_django.context_processors.login_redirect'],\n 'debug': True,\n 'loaders': ['openedx.core.djangoapps.theming.template_loaders.ThemeTemplateLoader',\n 'common.djangoapps.edxmako.makoloader.MakoFilesystemLoader',\n 'common.djangoapps.edxmako.makoloader.MakoAppDirectoriesLoader']}},\n {'APP_DIRS': False,\n 'BACKEND': 'common.djangoapps.edxmako.backend.Mako',\n 'DIRS': [Path('/edx/app/edxapp/edx-platform/lms/templates'),\n Path('/edx/app/edxapp/edx-platform/common/templates'),\n Path('/edx/app/edxapp/edx-platform/common/lib/capa/capa/templates'),\n Path('/edx/app/edxapp/edx-platform/common/djangoapps/pipeline_mako/templates'),\n Path('/edx/app/edxapp/edx-platform/openedx/core/djangoapps/cors_csrf/templates'),\n Path('/edx/app/edxapp/edx-platform/openedx/core/djangoapps/dark_lang/templates'),\n Path('/edx/app/edxapp/edx-platform/openedx/core/lib/license/templates'),\n Path('/edx/app/edxapp/edx-platform/openedx/features/course_experience/templates')],\n 'NAME': 'mako',\n 'OPTIONS': {'context_processors': ['django.template.context_processors.request',\n 'django.template.context_processors.static',\n 'django.template.context_processors.i18n',\n 'django.contrib.auth.context_processors.auth',\n 'django.template.context_processors.csrf',\n 'django.template.context_processors.media',\n 'django.template.context_processors.tz',\n 'django.contrib.messages.context_processors.messages',\n 'sekizai.context_processors.sekizai',\n 'common.djangoapps.edxmako.shortcuts.marketing_link_context_processor',\n 'lms.djangoapps.courseware.context_processor.user_timezone_locale_prefs',\n 'help_tokens.context_processor',\n 'openedx.core.djangoapps.site_configuration.context_processors.configuration_context',\n 'lms.djangoapps.mobile_api.context_processor.is_from_mobile_app',\n 'social_django.context_processors.backends',\n 'social_django.context_processors.login_redirect'],\n 'debug': False}}]\n | \n
| TEST_NON_SERIALIZED_APPS | \n\n []\n | \n
| TEST_RUNNER | \n\n 'django.test.runner.DiscoverRunner'\n | \n
| TEXTBOOKS_HELP_URL | \n\n 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_assets/textbooks.html'\n | \n
| THIRD_PARTY_AUTH_BACKENDS | \n\n ['social_core.backends.google.GoogleOAuth2',\n 'social_core.backends.linkedin.LinkedinOAuth2',\n 'social_core.backends.facebook.FacebookOAuth2',\n 'social_core.backends.azuread.AzureADOAuth2',\n 'common.djangoapps.third_party_auth.appleid.AppleIdAuth',\n 'common.djangoapps.third_party_auth.identityserver3.IdentityServer3',\n 'common.djangoapps.third_party_auth.saml.SAMLAuthBackend',\n 'common.djangoapps.third_party_auth.lti.LTIAuthBackend']\n | \n
| THIRD_PARTY_AUTH_CUSTOM_AUTH_FORMS | \n\n {}\n | \n
| THIRD_PARTY_AUTH_OLD_CONFIG | \n\n None\n | \n
| THOUSAND_SEPARATOR | \n\n ','\n | \n
| TIME_FORMAT | \n\n 'P'\n | \n
| TIME_INPUT_FORMATS | \n\n ['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']\n | \n
| TIME_ZONE | \n\n 'UTC'\n | \n
| TIME_ZONE_DISPLAYED_FOR_DEADLINES | \n\n 'UTC'\n | \n
| TPA_PROVIDER_BURST_THROTTLE | \n\n '10/min'\n | \n
| TPA_PROVIDER_SUSTAINED_THROTTLE | \n\n '50/hr'\n | \n
| TRACKING_BACKENDS | \n\n {'logger': {'ENGINE': 'common.djangoapps.track.backends.logger.LoggerBackend',\n 'OPTIONS': {'name': 'tracking'}}}\n | \n
| TRACKING_IGNORE_URL_PATTERNS | \n\n ['^/event', '^/login', '^/heartbeat', '^/segmentio/event', '^/performance']\n | \n
| TRACKING_SEGMENTIO_ALLOWED_TYPES | \n\n ['track']\n | \n
| TRACKING_SEGMENTIO_DISALLOWED_SUBSTRING_NAMES | \n\n []\n | \n
| TRACKING_SEGMENTIO_SOURCE_MAP | \n\n {'analytics-android': 'mobile', 'analytics-ios': 'mobile'}\n | \n
| TRACKING_SEGMENTIO_WEBHOOK_SECRET | \n\n '********************'\n | \n
| TRACK_MAX_EVENT | \n\n 50000\n | \n
| TRANSLATORS_GUIDE | \n\n 'https://edx.readthedocs.org/projects/edx-developer-guide/en/latest/conventions/internationalization/i18n_translators_guide.html'\n | \n
| UNIVERSITY_EMAIL | \n\n 'university@example.com'\n | \n
| USAGE_ID_PATTERN | \n\n '(?P<usage_id>(?:i4x://?[^/]+/[^/]+/[^/]+/[^@]+(?:@[^/]+)?)|(?:[^/]+))'\n | \n
| USAGE_KEY_PATTERN | \n\n '********************'\n | \n
| USERNAME_PATTERN | \n\n '(?P<username>[\\\\w .@_+-]+)'\n | \n
| USERNAME_REGEX_PARTIAL | \n\n '[\\\\w .@_+-]+'\n | \n
| USERNAME_REPLACEMENT_WORKER | \n\n 'OVERRIDE THIS WITH A VALID USERNAME'\n | \n
| USER_STATE_BATCH_SIZE | \n\n 5000\n | \n
| USE_I18N | \n\n True\n | \n
| USE_L10N | \n\n True\n | \n
| USE_THOUSAND_SEPARATOR | \n\n False\n | \n
| USE_TZ | \n\n True\n | \n
| USE_X_FORWARDED_HOST | \n\n False\n | \n
| USE_X_FORWARDED_PORT | \n\n False\n | \n
| VERIFICATION_EXPIRY_EMAIL | \n\n {'DAYS_RANGE': 1, 'DEFAULT_EMAILS': 2, 'RESEND_DAYS': 15}\n | \n
| VERIFY_STUDENT | \n\n {'DAYS_GOOD_FOR': 365,\n 'EXPIRING_SOON_WINDOW': 28,\n 'SOFTWARE_SECURE': {'API_ACCESS_KEY': '********************',\n 'API_SECRET_KEY': '********************'}}\n | \n
| VIDEO_CDN_URL | \n\n {'EXAMPLE_COUNTRY_CODE': 'http://example.com/edx/video?s3_url='}\n | \n
| VIDEO_IMAGE_MAX_AGE | \n\n 31536000\n | \n
| VIDEO_IMAGE_SETTINGS | \n\n {'DIRECTORY_PREFIX': 'video-images/',\n 'STORAGE_KWARGS': {'base_url': '/media/',\n 'location': '/edx/var/edxapp/media//'},\n 'VIDEO_IMAGE_MAX_BYTES': 2097152,\n 'VIDEO_IMAGE_MIN_BYTES': 2048}\n | \n
| VIDEO_TRANSCRIPTS_MAX_AGE | \n\n 31536000\n | \n
| VIDEO_TRANSCRIPTS_SETTINGS | \n\n {'DIRECTORY_PREFIX': 'video-transcripts/',\n 'STORAGE_KWARGS': {'base_url': '/media/',\n 'location': '/edx/var/edxapp/media//'},\n 'VIDEO_TRANSCRIPTS_MAX_BYTES': 3145728}\n | \n
| VIDEO_UPLOAD_PIPELINE | \n\n {'BUCKET': '', 'ROOT_PATH': ''}\n | \n
| WEBPACK_CONFIG_PATH | \n\n 'webpack.dev.config.js'\n | \n
| WEBPACK_LOADER | \n\n {'DEFAULT': {'BUNDLE_DIR_NAME': 'bundles/',\n 'STATS_FILE': Path('/edx/var/edxapp/staticfiles/webpack-stats.json'),\n 'TIMEOUT': 5},\n 'WORKERS': {'BUNDLE_DIR_NAME': 'bundles/',\n 'STATS_FILE': Path('/edx/var/edxapp/staticfiles/webpack-worker-stats.json')}}\n | \n
| WIKI_ACCOUNT_HANDLING | \n\n False\n | \n
| WIKI_ANONYMOUS | \n\n False\n | \n
| WIKI_CAN_ASSIGN | \n\n <function CAN_ASSIGN at 0x7f0032c428b0>\n | \n
| WIKI_CAN_CHANGE_PERMISSIONS | \n\n <function CAN_CHANGE_PERMISSIONS at 0x7f0032c42820>\n | \n
| WIKI_CAN_DELETE | \n\n <function CAN_DELETE at 0x7f0032c3d700>\n | \n
| WIKI_CAN_MODERATE | \n\n <function CAN_MODERATE at 0x7f0032c42790>\n | \n
| WIKI_EDITOR | \n\n 'lms.djangoapps.course_wiki.editors.CodeMirror'\n | \n
| WIKI_ENABLED | \n\n True\n | \n
| WIKI_HELP_URL | \n\n 'https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/course_assets/course_wiki.html'\n | \n
| WIKI_LINK_DEFAULT_LEVEL | \n\n 2\n | \n
| WIKI_LINK_LIVE_LOOKUPS | \n\n False\n | \n
| WIKI_SHOW_MAX_CHILDREN | \n\n 0\n | \n
| WIKI_USE_BOOTSTRAP_SELECT_WIDGET | \n\n False\n | \n
| WRITABLE_GRADEBOOK_URL | \n\n 'http://localhost:1994'\n | \n
| WSGI_APPLICATION | \n\n None\n | \n
| XBLOCK_EXTRA_MIXINS | \n\n ()\n | \n
| XBLOCK_FIELD_DATA_WRAPPERS | \n\n ('lms.djangoapps.courseware.field_overrides:OverrideModulestoreFieldData.wrap',)\n | \n
| XBLOCK_FS_STORAGE_BUCKET | \n\n None\n | \n
| XBLOCK_FS_STORAGE_PREFIX | \n\n None\n | \n
| XBLOCK_MIXINS | \n\n (<class 'lms.djangoapps.lms_xblock.mixin.LmsBlockMixin'>,\n <class 'xmodule.modulestore.inheritance.InheritanceMixin'>,\n <class 'xmodule.x_module.XModuleMixin'>,\n <class 'xmodule.modulestore.edit_info.EditInfoMixin'>)\n | \n
| XBLOCK_RUNTIME_V2_EPHEMERAL_DATA_CACHE | \n\n 'default'\n | \n
| XBLOCK_SELECT_FUNCTION | \n\n <function prefer_xmodules at 0x7f00333229d0>\n | \n
| XBLOCK_SETTINGS | \n\n {'VideoBlock': {'YOUTUBE_API_KEY': '********************',\n 'licensing_enabled': False}}\n | \n
| XDOMAIN_PROXY_CACHE_TIMEOUT | \n\n 900\n | \n
| XQUEUE_INTERFACE | \n\n {'basic_auth': ['edx', 'edx'],\n 'django_auth': {'password': '********************', 'username': 'lms'},\n 'url': 'http://edx.devstack.xqueue:18040'}\n | \n
| XQUEUE_WAITTIME_BETWEEN_REQUESTS | \n\n 5\n | \n
| X_FRAME_OPTIONS | \n\n 'DENY'\n | \n
| YEAR_MONTH_FORMAT | \n\n 'F Y'\n | \n
| YOUTUBE | \n\n {'API': '********************',\n 'IMAGE_API': '********************',\n 'METADATA_URL': 'https://www.googleapis.com/youtube/v3/videos/',\n 'TEST_TIMEOUT': 1500,\n 'TEXT_API': '********************'}\n | \n
| YOUTUBE_API_KEY | \n\n '********************'\n | \n
| ZENDESK_API_KEY | \n\n '********************'\n | \n
| ZENDESK_CUSTOM_FIELDS | \n\n {}\n | \n
| ZENDESK_GROUP_ID_MAPPING | \n\n {}\n | \n
| ZENDESK_OAUTH_ACCESS_TOKEN | \n\n '********************'\n | \n
| ZENDESK_URL | \n\n ''\n | \n
| ZENDESK_USER | \n\n ''\n | \n
\n You’re seeing this error because you have \n DEBUG = True in your\n Django settings file. Change that to\n False, and Django will\n display a standard page generated by the handler for this status code.\n
In your own words, explain a famous time travel paradox
" + ], + "teams_enabled": False, + "text_response": None, + "file_upload_response": None, + **test_data.example_rubric, + } + + self.mock_ora_instance = Mock(name="openassessment-block", **self.ora_data) + + def test_individual_ora(self): + # An ORA with teams disabled should have type "individual" + data = OpenResponseMetadataSerializer(self.mock_ora_instance).data + + assert data == { + "name": self.ora_data["display_name"], + "prompts": self.ora_data["prompts"], + "type": "individual", + "textResponseConfig": "none", + "fileUploadResponseConfig": "none", + "rubricConfig": { + "feedbackPrompt": "How did this student do?", + "criteria": [ + { + "orderNum": 0, + "name": "potions", + "label": "Potions", + "prompt": "How did this student perform in the Potions exam", + "feedback": "optional", + "options": test_data.example_rubric_options_serialized, + }, + { + "orderNum": 1, + "name": "charms", + "label": "Charms", + "prompt": "How did this student perform in the Charms exam", + "feedback": "disabled", + "options": test_data.example_rubric_options_serialized, + }, + ], + }, + } + + def test_team_ora(self): + # An ORA with teams enabled should have type "team" + self.mock_ora_instance.teams_enabled = True + data = OpenResponseMetadataSerializer(self.mock_ora_instance).data + + assert data == { + "name": self.ora_data["display_name"], + "prompts": self.ora_data["prompts"], + "type": "team", + "textResponseConfig": "none", + "fileUploadResponseConfig": "none", + "rubricConfig": { + "feedbackPrompt": "How did this student do?", + "criteria": [ + { + "orderNum": 0, + "name": "potions", + "label": "Potions", + "prompt": "How did this student perform in the Potions exam", + "feedback": "optional", + "options": test_data.example_rubric_options_serialized, + }, + { + "orderNum": 1, + "name": "charms", + "label": "Charms", + "prompt": "How did this student perform in the Charms exam", + "feedback": "disabled", + "options": test_data.example_rubric_options_serialized, + }, + ], + }, + } + + @ddt.unpack + @ddt.data(("optional", "optional"), ("required", "required")) + def test_response_config(self, text_response, file_upload_response): + self.mock_ora_instance.text_response = text_response + self.mock_ora_instance.file_upload_response = file_upload_response + + data = OpenResponseMetadataSerializer(self.mock_ora_instance).data + + assert data["textResponseConfig"] == text_response + assert data["fileUploadResponseConfig"] == file_upload_response + + def test_response_config_none(self): + self.mock_ora_instance.text_response = None + self.mock_ora_instance.file_upload_response = None + + data = OpenResponseMetadataSerializer(self.mock_ora_instance).data + + assert data["textResponseConfig"] == "none" + assert data["fileUploadResponseConfig"] == "none" + + +class TestSubmissionMetadataSerializer(TestCase): + """ + Tests for SubmissionMetadataSerializer. Implicitly, this also exercises ScoreSerializer. + SubmissionMetadata comes from the ORA list_staff_workflows XBlock.json_handler and has the shape: + "In your own words, explain a famous time travel paradox
" + ], + "teams_enabled": False, + } + + # Add rubric data here for succinctness + ora_data.update(test_data.example_rubric) + return Mock(name="openassessment-block", **ora_data) + + def set_up_course_metadata(self): + """Create mock course metadata for serialization""" + course_org = "Oxford" + course_name = "Introduction to Time Travel" + course_number = "TT101" + course_run = "2054" + + return CourseOverviewFactory.create( + org=course_org, + display_name=course_name, + display_number_with_default=course_number, + run=course_run, + ) + + def setUp(self): + super().setUp() + + self.mock_ora_instance = self.set_up_ora() + self.mock_course_metadata = self.set_up_course_metadata() + self.mock_submissions_data = test_data.example_submission_list.copy() + + def test_serializer_output(self): + input_data = { + "courseMetadata": self.mock_course_metadata, + "oraMetadata": self.mock_ora_instance, + "submissions": self.mock_submissions_data, + } + + output_data = InitializeSerializer(input_data).data + + expected_course_data = CourseMetadataSerializer(self.mock_course_metadata).data + expected_ora_data = OpenResponseMetadataSerializer(self.mock_ora_instance).data + expected_submissions_data = {} + + # There's a level of unpacking that happens in the serializer, perform that here + for submission_id, submission_data in self.mock_submissions_data.items(): + serialized_data = SubmissionMetadataSerializer(submission_data).data + expected_submissions_data[submission_id] = serialized_data + + # Check that each of the sub-serializers assembles data correctly + assert output_data["courseMetadata"] == expected_course_data + assert output_data["oraMetadata"] == expected_ora_data + assert output_data["submissions"] == expected_submissions_data + + +class TestRubricConfigSerializer(TestCase): + """Tests for RubricConfigSerializer""" + + def basic_test_case(self): + """Basic test for complex rubric""" + assert RubricConfigSerializer(test_data.example_rubric).data == { + "feedbackPrompt": "How did this student do?", + "criteria": [ + { + "orderNum": 0, + "name": "potions", + "label": "Potions", + "prompt": "How did this student perform in the Potions exam", + "feedback": "optional", + "options": test_data.example_rubric_options_serialized, + }, + { + "order_num": 1, + "name": "charms", + "label": "Charms", + "prompt": "How did this student perform in the Charms exam", + "feedback": "disabled", + "options": test_data.example_rubric_options_serialized, + }, + ], + } + + +@ddt.ddt +class TestScoreFieldAndSerializer(TestCase): + """Tests for ScoreField and ScoreSerializer""" + + def test_field_no_values(self): + """An empty dict passed to the field should return None""" + assert ScoreField().to_representation({}) is None + + @ddt.data("pointsEarned", "pointsPossible") + def test_field_missing(self, missing_field): + """Missing fields should just be ignored""" + value = {"pointsEarned": 30, "pointsPossible": 50} + del value[missing_field] + + assert ScoreField().to_representation(value) == value + + def test_field(self): + """Base serialization behavior for ScoreField""" + data = {"pointsEarned": 20, "pointsPossible": 40} + representation = ScoreField().to_representation(data) + assert representation == data + + def test_serializer_no_values(self): + """Passing the ScoreSerializer an empty dict should result in an empty serializer""" + # pylint: disable=use-implicit-booleaness-not-comparison + assert ScoreSerializer({}).data == {} + + def test_serialier(self): + """Base serialization behavior for ScoreSerializer""" + input_data = {"pointsEarned": 10, "pointsPossible": 200} + data = ScoreSerializer(input_data).data + assert data == input_data + + @ddt.data("pointsEarned", "pointsPossible") + def test_serializer_missing_field(self, missing_field): + """Missing fields should just be ignored""" + value = {"pointsEarned": 30, "pointsPossible": 50} + del value[missing_field] + + assert ScoreSerializer(value).data == value + + +class TestUploadedFileSerializer(TestCase): + """Tests for UploadedFileSerializer""" + + def test_uploaded_file_serializer(self): + """Base serialization behavior""" + input_data = MagicMock(size=89794) + data = UploadedFileSerializer(input_data).data + + expected_value = { + "downloadUrl": str(input_data.download_url), + "description": str(input_data.description), + "name": str(input_data.name), + "size": input_data.size, + } + assert data == expected_value + + +@ddt.ddt +class TestResponseSerializer(TestCase): + """Tests for ResponseSerializer""" + + def test_response_serializer__empty(self): + """Empty fields should be allowed""" + input_data = {"files": [], "text": []} + assert ResponseSerializer(input_data).data == input_data + + @ddt.unpack + @ddt.data((True, True), (True, False), (False, True), (False, False)) + def test_response_serializer(self, has_text, has_files): + """Base serialization behavior""" + input_data = MagicMock() + if has_files: + input_data.files = [Mock(size=111), Mock(size=222), Mock(size=333)] + if has_text: + input_data.text = [Mock(), Mock(), Mock()] + + data = ResponseSerializer(input_data).data + expected_value = { + "files": [ + UploadedFileSerializer(mock_file).data for mock_file in input_data.files + ] + if has_files + else [], + "text": [str(mock_text) for mock_text in input_data.text] + if has_text + else [], + } + assert data == expected_value + + +class TestAssessmentCriteriaSerializer(TestCase): + """Tests for AssessmentCriteriaSerializer""" + + def test_assessment_criteria_serializer(self): + """Base serialization behavior""" + input_data = Mock(points=595) + data = AssessmentCriteriaSerializer(input_data).data + + expected_value = { + "name": str(input_data.name), + "feedback": str(input_data.feedback), + "points": input_data.points, + "selectedOption": str(input_data.option), + } + assert data == expected_value + + def test_assessment_criteria_serializer__feedback_only(self): + """Test for serialization behavior of a feedback-only criterion""" + input_data = { + "name": "SomeCriterioOn", + "feedback": "Pathetic Effort", + "points": None, + "option": None, + } + data = AssessmentCriteriaSerializer(input_data).data + + expected_value = dict(input_data) + expected_value["selectedOption"] = expected_value["option"] + del expected_value["option"] + + assert data == expected_value + + +@ddt.ddt +class TestGradeDataSerializer(TestCase): + """Tests for GradeDataSerializer""" + + def test_grade_data_serializer__no_assessment(self): + """Passing an empty dict should result in an empty dict""" + # pylint: disable=use-implicit-booleaness-not-comparison + assert GradeDataSerializer({}).data == {} + + @ddt.data(True, False) + def test_grade_data_serializer__assessment(self, has_criteria): + """Base serialization behavior, with and without criteria""" + input_data = MagicMock() + if has_criteria: + input_data.criteria = [Mock(points=123), Mock(points=11), Mock(points=22)] + data = GradeDataSerializer(input_data).data + + expected_value = { + "score": ScoreField().to_representation(input_data.score), + "overallFeedback": str(input_data.feedback), + } + if has_criteria: + expected_value["criteria"] = [ + AssessmentCriteriaSerializer(criterion).data + for criterion in input_data.criteria + ] + else: + expected_value["criteria"] = [] + assert data == expected_value + + +@ddt.ddt +class TestSubmissionStatusFetchSerializer(TestCase): + """Tests for SubmissionStatusFetchSerializer""" + + def test_submission_status_fetch_serializer(self): + """Base serialization behavior""" + input_data = MagicMock() + serializer = SubmissionStatusFetchSerializer(input_data) + with patch.object(serializer, "get_gradeStatus") as mock_get_grade_status: + data = serializer.data + + expected_value = { + "gradeData": GradeDataSerializer(input_data.assessment_info).data, + "gradeStatus": mock_get_grade_status.return_value, + "lockStatus": LockStatusField().to_representation( + input_data.lock_info.lock_status + ), + } + mock_get_grade_status.assert_called_once_with(input_data) + assert data == expected_value + + @ddt.data(True, False) + def test_get__gradeStatus(self, has_assessment): + """Unit test for get_gradeStatus""" + assessment = {"somekey": "somevalue"} if has_assessment else {} + input_data = {"assessment_info": assessment} + value = SubmissionStatusFetchSerializer().get_gradeStatus(input_data) + expected = "graded" if has_assessment else "ungraded" + assert value == expected + + +class TestSubmissionFetchSerializer(TestCase): + """Tests for the SubmissionFetchSerializer""" + + def test_submission_fetch_serializer(self): + """Base serialization behavior""" + input_data = MagicMock() + serializer = SubmissionFetchSerializer(input_data) + with patch.object(serializer, "get_gradeStatus") as mock_get_grade_status: + data = serializer.data + + expected_value = { + "gradeData": GradeDataSerializer(input_data.assessment_info).data, + "gradeStatus": mock_get_grade_status.return_value, + "lockStatus": LockStatusField().to_representation( + input_data.lock_info.lock_status + ), + "response": ResponseSerializer(input_data.submission_info).data, + } + mock_get_grade_status.assert_called_once_with(input_data) + assert data == expected_value + + +class TestLockStatusSerializer(SharedModuleStoreTestCase): + """ + Tests for LockStatusSerializer + """ + + lock_in_progress = { + "submission_uuid": "e34ef789-a4b1-48cf-b1bc-b3edacfd4eb2", + "owner_id": "10ab03f1b75b4f9d9ab13a1fd1dccca1", + "created_at": "2021-09-21T21:54:09.901221Z", + "lock_status": "in-progress", + } + + lock_in_progress_expected = {"lockStatus": "in-progress"} + + lock_owned_by_other_user = { + "submission_uuid": "e34ef789-a4b1-48cf-b1bc-b3edacfd4eb2", + "owner_id": "10ab03f1b75b4f9d9ab13a1fd1dccca1", + "created_at": "2021-09-21T21:54:09.901221Z", + "lock_status": "locked", + } + + lock_owned_by_other_user_expected = {"lockStatus": "locked"} + + course_id = "course-v1:Oxford+TT101+2054" + + def test_happy_path(self): + """For simple cases, lock status is passed through directly""" + data = LockStatusSerializer(self.lock_in_progress).data + assert data == self.lock_in_progress_expected + + data = LockStatusSerializer(self.lock_owned_by_other_user).data + assert data == self.lock_owned_by_other_user_expected + + +class TestStaffAssessSerializer(TestCase): + """Tests for StaffAssessSerializer""" + + grade_data = { + "overallFeedback": "was pretty good", + "criteria": [ + { + "name": "firstCriterion", + "feedback": "did alright", + "selectedOption": "good", + }, + {"name": "secondCriterion", "selectedOption": "fair"}, + ], + } + + grade_data_no_feedback = { + "overallFeedback": "", + "criteria": [ + {"name": "firstCriterion", "selectedOption": "good"}, + {"name": "secondCriterion", "selectedOption": "fair"}, + ], + } + + submission_uuid = "foo" + + def test_staff_assess_serializer(self): + """Base serialization behavior""" + context = {"submission_uuid": self.submission_uuid} + serializer = StaffAssessSerializer(self.grade_data, context=context) + + expected_value = { + "options_selected": { + "firstCriterion": "good", + "secondCriterion": "fair", + }, + "criterion_feedback": { + "firstCriterion": "did alright", + }, + "overall_feedback": "was pretty good", + "submission_uuid": self.submission_uuid, + "assess_type": "full-grade", + } + + assert serializer.data == expected_value + + def test_staff_assess_no_feedback(self): + """Verify that empty feedback returns a reasonable shape""" + context = {"submission_uuid": self.submission_uuid} + serializer = StaffAssessSerializer(self.grade_data_no_feedback, context=context) + + expected_value = { + "options_selected": { + "firstCriterion": "good", + "secondCriterion": "fair", + }, + "criterion_feedback": {}, + "overall_feedback": "", + "submission_uuid": self.submission_uuid, + "assess_type": "full-grade", + } + + assert serializer.data == expected_value diff --git a/lms/djangoapps/ora_staff_grader/tests/test_views.py b/lms/djangoapps/ora_staff_grader/tests/test_views.py new file mode 100644 index 0000000000..1f05580800 --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/tests/test_views.py @@ -0,0 +1,697 @@ +""" +Tests for ESG views +""" +import json +from unittest.mock import Mock, patch +from uuid import uuid4 + +import ddt +from django.http import QueryDict +from django.urls import reverse +from rest_framework.test import APITestCase +from xmodule.modulestore.tests.django_utils import ( + TEST_DATA_SPLIT_MODULESTORE, + SharedModuleStoreTestCase, +) +from xmodule.modulestore.tests.factories import CourseFactory +from xmodule.modulestore.tests.factories import ItemFactory + +from common.djangoapps.student.tests.factories import StaffFactory +from lms.djangoapps.ora_staff_grader.constants import ( + ERR_BAD_ORA_LOCATION, + ERR_GRADE_CONTESTED, + ERR_INTERNAL, + ERR_LOCK_CONTESTED, + ERR_MISSING_PARAM, + ERR_UNKNOWN, + PARAM_ORA_LOCATION, + PARAM_SUBMISSION_ID, +) +from lms.djangoapps.ora_staff_grader.errors import ( + LockContestedError, + XBlockInternalError, +) +import lms.djangoapps.ora_staff_grader.tests.test_data as test_data +from openedx.core.djangoapps.content.course_overviews.tests.factories import ( + CourseOverviewFactory, +) + + +class BaseViewTest(SharedModuleStoreTestCase, APITestCase): + """Base class for shared test utils and setup""" + + MODULESTORE = TEST_DATA_SPLIT_MODULESTORE + + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.api_url = reverse(cls.view_name) + + cls.course = CourseFactory.create() + cls.course_key = cls.course.location.course_key + + cls.ora_block = ItemFactory.create( + category="openassessment", + parent_location=cls.course.location, + display_name="test", + ) + cls.ora_usage_key = str(cls.ora_block.location) + + cls.password = "password" + cls.staff = StaffFactory(course_key=cls.course_key, password=cls.password) + + def log_in(self): + """Log in as staff""" + self.client.login(username=self.staff.username, password=self.password) + + def url_with_params(self, params): + """For DRF client.posts, you can't add query params easily. This helper adds it to the request URL""" + query_dictionary = QueryDict("", mutable=True) + query_dictionary.update(params) + + return "{base_url}?{querystring}".format( + base_url=reverse(self.view_name), querystring=query_dictionary.urlencode() + ) + + +@ddt.ddt +class TestInitializeView(BaseViewTest): + """ + Tests for the /initialize view, creating setup data for ESG + """ + + view_name = "ora-staff-grader:initialize" + + def setUp(self): + super().setUp() + self.log_in() + + @ddt.data({}, {PARAM_ORA_LOCATION: ""}) + def test_missing_param(self, query_params): + """Missing ORA location param should return 400 and error message""" + response = self.client.get(self.api_url, query_params) + + assert response.status_code == 400 + assert json.loads(response.content) == {"error": ERR_MISSING_PARAM} + + def test_bad_ora_location(self): + """Bad ORA location should return a 400 and error message""" + response = self.client.get( + self.api_url, {PARAM_ORA_LOCATION: "not_a_real_location"} + ) + + assert response.status_code == 400 + assert json.loads(response.content) == {"error": ERR_BAD_ORA_LOCATION} + + @patch("lms.djangoapps.ora_staff_grader.views.get_submissions") + @patch("lms.djangoapps.ora_staff_grader.views.get_course_overview_or_none") + def test_init(self, mock_get_course_overview, mock_get_submissions): + """Any failure to fetch info returns an unknown error response""" + mock_course_overview = CourseOverviewFactory.create() + mock_get_course_overview.return_value = mock_course_overview + mock_get_submissions.return_value = test_data.example_submission_list + + response = self.client.get( + self.api_url, {PARAM_ORA_LOCATION: self.ora_usage_key} + ) + + expected_keys = set(["courseMetadata", "oraMetadata", "submissions"]) + assert response.status_code == 200 + assert response.data.keys() == expected_keys + + @patch("lms.djangoapps.ora_staff_grader.views.get_submissions") + @patch("lms.djangoapps.ora_staff_grader.views.get_course_overview_or_none") + def test_init_xblock_exception( + self, mock_get_course_overview, mock_get_submissions + ): + """If one of the XBlock handlers fails, the exception should be caught""" + mock_course_overview = CourseOverviewFactory.create() + mock_get_course_overview.return_value = mock_course_overview + # Mock an error getting submissions + mock_get_submissions.side_effect = XBlockInternalError( + context={"handler": "list_staff_workflows"} + ) + + response = self.client.get( + self.api_url, {PARAM_ORA_LOCATION: self.ora_usage_key} + ) + + assert response.status_code == 500 + assert json.loads(response.content) == { + "error": ERR_INTERNAL, + "handler": "list_staff_workflows", + } + + @patch("lms.djangoapps.ora_staff_grader.views.get_submissions") + @patch("lms.djangoapps.ora_staff_grader.views.get_course_overview_or_none") + def test_init_generic_exception( + self, mock_get_course_overview, mock_get_submissions + ): + """If something else strange fails (e.g. bad data shape), an "unknown" error should be surfaced""" + mock_course_overview = CourseOverviewFactory.create() + mock_get_course_overview.return_value = mock_course_overview + # Mock a bad returned data shape which would break serialization + mock_get_submissions.return_value = {"bad": "wolf"} + + response = self.client.get( + self.api_url, {PARAM_ORA_LOCATION: self.ora_usage_key} + ) + + assert response.status_code == 500 + assert json.loads(response.content) == {"error": ERR_UNKNOWN} + + +@ddt.ddt +class TestFetchSubmissionView(BaseViewTest): + """ + Tests for the submission fetch view + """ + + view_name = "ora-staff-grader:fetch-submission" + + def setUp(self): + super().setUp() + self.log_in() + + @ddt.data({}, {PARAM_ORA_LOCATION: "", PARAM_SUBMISSION_ID: ""}) + def test_missing_params(self, query_params): + """Missing or blank params should return 400 and error message""" + response = self.client.get(self.api_url, query_params) + + assert response.status_code == 400 + assert json.loads(response.content) == {"error": ERR_MISSING_PARAM} + + @ddt.data(True, False) + @patch("lms.djangoapps.ora_staff_grader.views.get_submission_info") + @patch("lms.djangoapps.ora_staff_grader.views.get_assessment_info") + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + def test_fetch_submission( + self, + has_assessment, + mock_check_submission_lock, + mock_get_assessment_info, + mock_get_submission_info, + ): + """Successfull submission fetch status returns submission, lock, and grade data""" + mock_get_submission_info.return_value = test_data.example_submission + mock_get_assessment_info.return_value = ( + {} if not has_assessment else test_data.example_assessment + ) + mock_check_submission_lock.return_value = {"lock_status": "unlocked"} + + ora_location, submission_uuid = Mock(), Mock() + response = self.client.get( + self.api_url, + {PARAM_ORA_LOCATION: ora_location, PARAM_SUBMISSION_ID: submission_uuid}, + ) + + assert response.status_code == 200 + assert response.data.keys() == set( + ["gradeData", "response", "gradeStatus", "lockStatus"] + ) + assert response.data["response"].keys() == set(["files", "text"]) + expected_assessment_keys = ( + set(["score", "overallFeedback", "criteria"]) if has_assessment else set() + ) + assert response.data["gradeData"].keys() == expected_assessment_keys + + @patch("lms.djangoapps.ora_staff_grader.views.get_submission_info") + @patch("lms.djangoapps.ora_staff_grader.views.get_assessment_info") + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + def test_fetch_submission_generic_exception( + self, + mock_check_submission_lock, + mock_get_assessment_info, + mock_get_submission_info, + ): + """Other generic exceptions should return the "unknown" error response""" + mock_get_submission_info.return_value = test_data.example_submission + # Mock an error in getting the assessment info + mock_get_assessment_info.side_effect = XBlockInternalError( + context={"handler": "get_assessment_info"} + ) + mock_check_submission_lock.return_value = {"lock_status": "unlocked"} + + ora_location, submission_uuid = Mock(), Mock() + response = self.client.get( + self.api_url, + {PARAM_ORA_LOCATION: ora_location, PARAM_SUBMISSION_ID: submission_uuid}, + ) + + assert response.status_code == 500 + assert json.loads(response.content) == { + "error": ERR_INTERNAL, + "handler": "get_assessment_info", + } + + @patch("lms.djangoapps.ora_staff_grader.views.get_submission_info") + @patch("lms.djangoapps.ora_staff_grader.views.get_assessment_info") + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + def test_fetch_submission_xblock_exception( + self, + mock_check_submission_lock, + mock_get_assessment_info, + mock_get_submission_info, + ): + """An exception in any XBlock handler returns an error response""" + mock_get_submission_info.return_value = test_data.example_submission + mock_get_assessment_info.return_value = test_data.example_assessment + # Mock a bad data shape to break serialization + mock_check_submission_lock.return_value = {"mad": "hatter"} + + ora_location, submission_uuid = Mock(), Mock() + response = self.client.get( + self.api_url, + {PARAM_ORA_LOCATION: ora_location, PARAM_SUBMISSION_ID: submission_uuid}, + ) + + assert response.status_code == 500 + assert json.loads(response.content) == {"error": ERR_UNKNOWN} + + +@ddt.ddt +class TestFetchSubmissionStatusView(BaseViewTest): + """ + Tests for the submission fetch view + """ + + view_name = "ora-staff-grader:fetch-submission-status" + + def setUp(self): + super().setUp() + self.log_in() + + @ddt.data( + {}, + {PARAM_ORA_LOCATION: "", PARAM_SUBMISSION_ID: Mock()}, + {PARAM_ORA_LOCATION: Mock(), PARAM_SUBMISSION_ID: ""}, + ) + def test_missing_param(self, query_params): + """Missing ORA location or submission ID param should return 400 and error message""" + response = self.client.get(self.api_url, query_params) + + assert response.status_code == 400 + assert json.loads(response.content) == {"error": ERR_MISSING_PARAM} + + @ddt.data(True, False) + @patch("lms.djangoapps.ora_staff_grader.views.get_assessment_info") + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + def test_fetch_submission_status( + self, + has_assessment, + mock_check_submission_lock, + mock_get_assessment_info, + ): + """Successful fetch submission returns submission and related lock/assessment info""" + mock_get_assessment_info.return_value = ( + {} if not has_assessment else test_data.example_assessment + ) + mock_check_submission_lock.return_value = {"lock_status": "in-progress"} + + ora_location, submission_uuid = Mock(), Mock() + response = self.client.get( + self.api_url, + {PARAM_ORA_LOCATION: ora_location, PARAM_SUBMISSION_ID: submission_uuid}, + ) + + assert response.status_code == 200 + actual = response.json() + expected = { + "gradeStatus": "graded" if has_assessment else "ungraded", + "lockStatus": mock_check_submission_lock.return_value["lock_status"], + "gradeData": {} + if not has_assessment + else { + "score": test_data.example_assessment["score"], + "overallFeedback": test_data.example_assessment["feedback"], + "criteria": [ + { + "name": "Criterion 1", + "selectedOption": "Three", + "points": 3, + "feedback": "Feedback 1", + }, + ], + }, + } + assert actual == expected + + @patch("lms.djangoapps.ora_staff_grader.views.get_assessment_info") + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + def test_fetch_submission_status_xblock_exception( + self, mock_check_submission_lock, mock_get_assessment_info + ): + """Exceptions within an XBlock return an internal error response""" + # Mock a bad data shape to throw a serializer exception + mock_get_assessment_info.return_value = {} + mock_check_submission_lock.side_effect = XBlockInternalError( + context={"handler": "claim_submission_lock"} + ) + + ora_location, submission_uuid = Mock(), Mock() + response = self.client.get( + self.api_url, + {PARAM_ORA_LOCATION: ora_location, PARAM_SUBMISSION_ID: submission_uuid}, + ) + + assert response.status_code == 500 + assert json.loads(response.content) == { + "error": ERR_INTERNAL, + "handler": "claim_submission_lock", + } + + @patch("lms.djangoapps.ora_staff_grader.views.get_assessment_info") + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + def test_fetch_submission_status_generic_exception( + self, mock_check_submission_lock, mock_get_assessment_info + ): + """Exceptions outside of an XBlock return a generic error response""" + mock_get_assessment_info.return_value = {} + # Mock a bad data shape to throw a serializer exception + mock_check_submission_lock.return_value = {"jekyll", "hyde"} + + ora_location, submission_uuid = Mock(), Mock() + response = self.client.get( + self.api_url, + {PARAM_ORA_LOCATION: ora_location, PARAM_SUBMISSION_ID: submission_uuid}, + ) + + assert response.status_code == 500 + assert json.loads(response.content) == {"error": ERR_UNKNOWN} + + +class TestSubmissionLockView(BaseViewTest): + """ + Tests for the /lock view, locking or unlocking a submission for grading + """ + + view_name = "ora-staff-grader:lock" + + test_submission_uuid = str(uuid4()) + test_anon_user_id = "anon-user-id" + test_other_anon_user_id = "anon-user-id-2" + test_timestamp = "2020-08-29T02:14:00-04:00" + + def setUp(self): + super().setUp() + + # Lock requests must include ORA location and submission UUID + self.test_lock_params = { + PARAM_ORA_LOCATION: self.ora_usage_key, + PARAM_SUBMISSION_ID: self.test_submission_uuid, + } + + self.log_in() + + def claim_lock(self, params): + """Wrapper for easier calling of 'claim_submission_lock'""" + return self.client.post(self.url_with_params(params)) + + def delete_lock(self, params): + """Wrapper for easier calling of 'delete_submission_lock'""" + return self.client.delete(self.url_with_params(params)) + + # Tests for claiming a lock (POST) + + def test_claim_lock_invalid_ora(self): + """An invalid ORA returns a 400""" + self.test_lock_params[PARAM_ORA_LOCATION] = "not_a_real_location" + + response = self.claim_lock(self.test_lock_params) + + assert response.status_code == 400 + assert json.loads(response.content) == {"error": ERR_BAD_ORA_LOCATION} + + @patch("lms.djangoapps.ora_staff_grader.views.claim_submission_lock") + def test_claim_lock(self, mock_claim_lock): + """POST tries to claim a submission lock. Success returns lock status 'in-progress'.""" + mock_return_data = { + "submission_uuid": self.test_submission_uuid, + "owner_id": self.test_anon_user_id, + "created_at": self.test_timestamp, + "lock_status": "in-progress", + } + mock_claim_lock.return_value = mock_return_data + + response = self.claim_lock(self.test_lock_params) + + expected_value = {"lockStatus": "in-progress"} + assert response.status_code == 200 + assert json.loads(response.content) == expected_value + + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + @patch("lms.djangoapps.ora_staff_grader.views.claim_submission_lock") + def test_claim_lock_contested(self, mock_claim_lock, mock_check_lock): + """Attempting to claim a lock owned by another user returns a 403 - forbidden and passes error code.""" + mock_claim_lock.side_effect = LockContestedError() + mock_check_lock.return_value = { + "submission_uuid": self.test_submission_uuid, + "owner_id": self.test_other_anon_user_id, + "created_at": self.test_timestamp, + "lock_status": "locked", + } + + response = self.claim_lock(self.test_lock_params) + + expected_value = {"error": ERR_LOCK_CONTESTED, "lockStatus": "locked"} + assert response.status_code == 409 + assert json.loads(response.content) == expected_value + + @patch("lms.djangoapps.ora_staff_grader.views.claim_submission_lock") + def test_claim_lock_xblock_exception( + self, + mock_claim_lock, + ): + """In the unlikely event of an error, the exits are to your left and behind you""" + mock_claim_lock.side_effect = XBlockInternalError( + context={"handler": "claim_submission_lock"} + ) + + response = self.claim_lock(self.test_lock_params) + + assert response.status_code == 500 + assert json.loads(response.content) == { + "error": ERR_INTERNAL, + "handler": "claim_submission_lock", + } + + @patch("lms.djangoapps.ora_staff_grader.views.claim_submission_lock") + def test_claim_lock_generic_exception( + self, + mock_claim_lock, + ): + """In the even more unlikely event of an unhandled error, shrug exuberantly""" + # Mock a bad data shape to break serialiation and raise a generic exception + mock_claim_lock.return_value = {"android": "Rachel"} + + response = self.claim_lock(self.test_lock_params) + + assert response.status_code == 500 + assert json.loads(response.content) == {"error": ERR_UNKNOWN} + + # Tests for deleting a lock (DELETE) + + @patch("lms.djangoapps.ora_staff_grader.views.delete_submission_lock") + def test_delete_lock(self, mock_delete_lock): + """DELETE indicates to clear submission lock. Success returns lock status 'unlocked'.""" + mock_delete_lock.return_value = {"lock_status": "unlocked"} + + response = self.delete_lock(self.test_lock_params) + + expected_value = {"lockStatus": "unlocked"} + assert response.status_code == 200 + assert json.loads(response.content) == expected_value + + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + @patch("lms.djangoapps.ora_staff_grader.views.delete_submission_lock") + def test_delete_lock_contested(self, mock_delete_lock, mock_check_lock): + """Attempting to delete a lock owned by another user returns a 403 - forbidden and passes error code.""" + mock_delete_lock.side_effect = LockContestedError() + mock_check_lock.return_value = { + "submission_uuid": self.test_submission_uuid, + "owner_id": self.test_other_anon_user_id, + "created_at": self.test_timestamp, + "lock_status": "locked", + } + + response = self.delete_lock(self.test_lock_params) + + expected_value = {"error": ERR_LOCK_CONTESTED, "lockStatus": "locked"} + assert response.status_code == 409 + assert json.loads(response.content) == expected_value + + @patch("lms.djangoapps.ora_staff_grader.views.delete_submission_lock") + def test_delete_lock_xblock_exception(self, mock_delete_lock): + """In the unlikely event of an error, the exits are to your left and behind you""" + mock_delete_lock.side_effect = XBlockInternalError( + context={"handler": "delete_submission_lock"} + ) + + response = self.delete_lock(self.test_lock_params) + + assert response.status_code == 500 + assert json.loads(response.content) == { + "error": ERR_INTERNAL, + "handler": "delete_submission_lock", + } + + @patch("lms.djangoapps.ora_staff_grader.views.delete_submission_lock") + def test_delete_lock_generic_exception(self, mock_delete_lock): + """In the even more unlikely event of an unhandled error, shrug exuberantly""" + # Mock a bad data shape to break serialiation and raise a generic exception + mock_delete_lock.return_value = {"android": "Roy Batty"} + + response = self.delete_lock(self.test_lock_params) + + assert response.status_code == 500 + assert json.loads(response.content) == {"error": ERR_UNKNOWN} + + +class TestUpdateGradeView(BaseViewTest): + """ + Tests for updating a grade for a submission + """ + + view_name = "ora-staff-grader:update-grade" + + submission_uuid = str(uuid4()) + ora_location = Mock() + test_anon_user_id = "anon-user-id" + test_timestamp = "2020-08-29T02:14:00-04:00" + + def setUp(self): + super().setUp() + self.log_in() + + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + @patch("lms.djangoapps.ora_staff_grader.views.submit_grade") + def test_submit_grade_xblock_exception(self, mock_submit_grade, mock_check_lock): + """A handled ORA failure to submit a grade returns a server error""" + mock_check_lock.return_value = {"lock_status": "in-progress"} + mock_submit_grade.side_effect = XBlockInternalError( + context={"handler": "staff_assess", "msg": "Danger, Will Robinson!"} + ) + url = self.url_with_params( + { + PARAM_ORA_LOCATION: self.ora_location, + PARAM_SUBMISSION_ID: self.submission_uuid, + } + ) + data = test_data.example_grade_data + + response = self.client.post(url, data, format="json") + assert response.status_code == 500 + assert json.loads(response.content) == { + "error": ERR_INTERNAL, + "handler": "staff_assess", + "msg": "Danger, Will Robinson!", + } + + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + @patch("lms.djangoapps.ora_staff_grader.views.submit_grade") + def test_submit_grade_generic_exception(self, mock_submit_grade, mock_check_lock): + """A fall-through failure returns an unknown error""" + mock_check_lock.return_value = {"lock_status": "in-progress"} + mock_submit_grade.return_value = {"error": "time paradox encountered"} + url = self.url_with_params( + { + PARAM_ORA_LOCATION: self.ora_location, + PARAM_SUBMISSION_ID: self.submission_uuid, + } + ) + data = test_data.example_grade_data + + response = self.client.post(url, data, format="json") + assert response.status_code == 500 + assert json.loads(response.content) == {"error": ERR_UNKNOWN} + + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + @patch("lms.djangoapps.ora_staff_grader.views.get_assessment_info") + @patch("lms.djangoapps.ora_staff_grader.views.delete_submission_lock") + @patch("lms.djangoapps.ora_staff_grader.views.submit_grade") + def test_submit_grade_success( + self, mock_submit_grade, mock_delete_lock, mock_get_info, mock_check_lock + ): + """A grade update success should clear the submission lock and return submission meta""" + mock_check_lock.side_effect = [ + {"lock_status": "in-progress"}, + {"lock_status": "unlocked"}, + ] + mock_submit_grade.return_value = {"success": True, "msg": ""} + mock_get_info.return_value = test_data.example_assessment + + url = self.url_with_params( + { + PARAM_ORA_LOCATION: self.ora_location, + PARAM_SUBMISSION_ID: self.submission_uuid, + } + ) + data = test_data.example_grade_data + + response = self.client.post(url, data, format="json") + + expected_response = { + "gradeStatus": "graded", + "lockStatus": "unlocked", + "gradeData": { + "score": test_data.example_assessment["score"], + "overallFeedback": test_data.example_assessment["feedback"], + "criteria": [ + { + "name": "Criterion 1", + "selectedOption": "Three", + "points": 3, + "feedback": "Feedback 1", + }, + ], + }, + } + + assert response.status_code == 200 + assert json.loads(response.content) == expected_response + + # Verify that clear lock was called + mock_delete_lock.assert_called_once() + + @patch("lms.djangoapps.ora_staff_grader.views.check_submission_lock") + @patch("lms.djangoapps.ora_staff_grader.views.get_assessment_info") + @patch("lms.djangoapps.ora_staff_grader.views.submit_grade") + def test_submit_grade_contested( + self, mock_submit_grade, mock_get_info, mock_check_lock + ): + """Submitting a grade should be blocked if someone else has obtained the lock""" + mock_check_lock.side_effect = [{"lock_status": "unlocked"}] + mock_get_info.return_value = test_data.example_assessment + + url = self.url_with_params( + { + PARAM_ORA_LOCATION: self.ora_location, + PARAM_SUBMISSION_ID: self.submission_uuid, + } + ) + data = test_data.example_grade_data + + response = self.client.post(url, data, format="json") + + assert response.status_code == 409 + assert json.loads(response.content) == { + "error": ERR_GRADE_CONTESTED, + "gradeStatus": "graded", + "lockStatus": "unlocked", + "gradeData": { + "score": test_data.example_assessment["score"], + "overallFeedback": test_data.example_assessment["feedback"], + "criteria": [ + { + "name": "Criterion 1", + "selectedOption": "Three", + "points": 3, + "feedback": "Feedback 1", + }, + ], + }, + } + + # Verify that submit grade was not called + mock_submit_grade.assert_not_called() diff --git a/lms/djangoapps/ora_staff_grader/urls.py b/lms/djangoapps/ora_staff_grader/urls.py new file mode 100644 index 0000000000..ed100114e0 --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/urls.py @@ -0,0 +1,31 @@ +""" +URLs for Enhanced Staff Grader (ESG) backend-for-frontend (BFF) +""" +from django.conf.urls import include +from django.urls import path + + +from lms.djangoapps.ora_staff_grader.views import ( + InitializeView, + SubmissionFetchView, + SubmissionLockView, + SubmissionStatusFetchView, + UpdateGradeView, +) + + +urlpatterns = [] +app_name = "ora-staff-grader" + +urlpatterns += [ + path("mock/", include("lms.djangoapps.ora_staff_grader.mock.urls")), + path("initialize", InitializeView.as_view(), name="initialize"), + path( + "submission/status", + SubmissionStatusFetchView.as_view(), + name="fetch-submission-status", + ), + path("submission/lock", SubmissionLockView.as_view(), name="lock"), + path("submission/grade", UpdateGradeView.as_view(), name="update-grade"), + path("submission", SubmissionFetchView.as_view(), name="fetch-submission"), +] diff --git a/lms/djangoapps/ora_staff_grader/utils.py b/lms/djangoapps/ora_staff_grader/utils.py new file mode 100644 index 0000000000..c307f77cf3 --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/utils.py @@ -0,0 +1,76 @@ +""" +Various helpful utilities for ESG +""" +from functools import wraps +import json + +from opaque_keys.edx.keys import UsageKey +from rest_framework.request import clone_request + +from lms.djangoapps.courseware.module_render import handle_xblock_callback_noauth +from lms.djangoapps.ora_staff_grader.errors import MissingParamResponse + + +def require_params(param_names): + """ + Adds the required query params to the view function. Returns 404 if param(s) missing. + + Params: + - param_name (string): the query param to unpack + + Raises: + - MissingParamResponse (HTTP 400) + """ + + def decorator(function): + @wraps(function) + def wrapped_function( + self, request, *args, **kwargs + ): # pylint: disable=unused-argument + passed_parameters = [] + + for param_name in param_names: + param = request.query_params.get(param_name) + + if not param: + return MissingParamResponse() + + passed_parameters.append(param) + return function(self, request, *passed_parameters, *args, **kwargs) + + return wrapped_function + + return decorator + + +def call_xblock_json_handler(request, usage_id, handler_name, data): + """ + WARN: Tested only for use in ESG. Consult before use outside of ESG. + + Create an internally-routed XBlock.json_handler request. The internal auth code/param unpacking requires a POST + request with payload in the body. Ideally, we would be able to call functions on XBlocks without this sort of + hacky request proxying but this is what we have to work with right now. + + params: + request (HttpRequest): Originating web request, we're going to borrow auth headers/cookies from this + usage_id (str): Usage ID of the XBlock for running the handler + handler_name (str): the name of the XBlock handler method + data (dict): Data to be encoded and sent as the body of the POST request + returns: + response (HttpResponse): get response data with json.loads(response.content) + """ + # XBlock.json_handler operates through a POST request + proxy_request = clone_request(request, "POST") + proxy_request.META["REQUEST_METHOD"] = "POST" + + # The body is an encoded JSON blob + proxy_request.body = json.dumps(data).encode() + + # Course ID can be retrieved from the usage_id + usage_key = UsageKey.from_string(usage_id) + course_id = str(usage_key.course_key) + + # Send the request and return the HTTP response from the XBlock + return handle_xblock_callback_noauth( + proxy_request, course_id, usage_id, handler_name + ) diff --git a/lms/djangoapps/ora_staff_grader/views.py b/lms/djangoapps/ora_staff_grader/views.py new file mode 100644 index 0000000000..655dd9b995 --- /dev/null +++ b/lms/djangoapps/ora_staff_grader/views.py @@ -0,0 +1,398 @@ +""" +Views for Enhanced Staff Grader +""" +# NOTE: we intentionally do broad exception checking to return a clean error shape +# pylint: disable=broad-except + +# NOTE: we intentionally add extra args using @require_params +# pylint: disable=arguments-differ + +from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication +from edx_rest_framework_extensions.auth.session.authentication import ( + SessionAuthenticationAllowInactiveUser, +) +from opaque_keys import InvalidKeyError +from opaque_keys.edx.keys import UsageKey +from rest_framework.generics import RetrieveAPIView +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from xmodule.modulestore.django import modulestore +from xmodule.modulestore.exceptions import ItemNotFoundError + +from lms.djangoapps.ora_staff_grader.constants import ( + PARAM_ORA_LOCATION, + PARAM_SUBMISSION_ID, +) +from lms.djangoapps.ora_staff_grader.errors import ( + BadOraLocationResponse, + GradeContestedResponse, + InternalErrorResponse, + LockContestedError, + LockContestedResponse, + UnknownErrorResponse, + XBlockInternalError, +) +from lms.djangoapps.ora_staff_grader.ora_api import ( + check_submission_lock, + claim_submission_lock, + delete_submission_lock, + get_assessment_info, + get_submission_info, + get_submissions, + submit_grade, +) +from lms.djangoapps.ora_staff_grader.serializers import ( + InitializeSerializer, + LockStatusSerializer, + StaffAssessSerializer, + SubmissionFetchSerializer, + SubmissionStatusFetchSerializer, +) +from lms.djangoapps.ora_staff_grader.utils import require_params +from openedx.core.djangoapps.content.course_overviews.api import ( + get_course_overview_or_none, +) +from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser + + +class StaffGraderBaseView(RetrieveAPIView): + """ + Base view for common auth/permission setup used across ESG views. + """ + + authentication_classes = ( + JwtAuthentication, + BearerAuthenticationAllowInactiveUser, + SessionAuthenticationAllowInactiveUser, + ) + + permission_classes = (IsAuthenticated,) + + +class InitializeView(StaffGraderBaseView): + """ + GET course metadata + + Response: { + courseMetadata + oraMetadata + submissions + } + + Errors: + - MissingParamResponse (HTTP 400) for missing params + - BadOraLocationResponse (HTTP 400) for bad ORA location + - XBlockInternalError (HTTP 500) for an issue with ORA + - UnknownError (HTTP 500) for other errors + """ + + @require_params([PARAM_ORA_LOCATION]) + def get(self, request, ora_location, *args, **kwargs): + try: + init_data = {} + + # Get ORA block and config (incl. rubric) + ora_usage_key = UsageKey.from_string(ora_location) + init_data["oraMetadata"] = modulestore().get_item(ora_usage_key) + + # Get course metadata + course_id = str(ora_usage_key.course_key) + init_data["courseMetadata"] = get_course_overview_or_none(course_id) + + # Get list of submissions for this ORA + init_data["submissions"] = get_submissions(request, ora_location) + + return Response(InitializeSerializer(init_data).data) + + # Catch bad ORA location + except (InvalidKeyError, ItemNotFoundError): + return BadOraLocationResponse() + + # Issues with the XBlock handlers + except XBlockInternalError as ex: + return InternalErrorResponse(context=ex.context) + + # Blanket exception handling + except Exception: + return UnknownErrorResponse() + + +class SubmissionFetchView(StaffGraderBaseView): + """ + GET submission contents and assessment info, if any + + Response: { + gradeData: { + score: (dict or None) { + pointsEarned: (int) earned points + pointsPossible: (int) possible points + } + overallFeedback: (string) overall feedback + criteria: (list of dict) [{ + name: (str) name of criterion + feedback: (str) feedback for criterion + points: (int) points of selected option or None if feedback-only criterion + selectedOption: (str) name of selected option or None if feedback-only criterion + }] + } + response: { + text: (list of string), [the html content of text responses] + files: (list of dict) [{ + downloadUrl: (string) file download url + description: (string) file description + name: (string) filename + }] + } + } + + Errors: + - MissingParamResponse (HTTP 400) for missing params + - XBlockInternalError (HTTP 500) for an issue with ORA + - UnknownError (HTTP 500) for other errors + """ + + @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) + def get(self, request, ora_location, submission_uuid, *args, **kwargs): + try: + submission_info = get_submission_info( + request, ora_location, submission_uuid + ) + assessment_info = get_assessment_info( + request, ora_location, submission_uuid + ) + lock_info = check_submission_lock(request, ora_location, submission_uuid) + + serializer = SubmissionFetchSerializer( + { + "submission_info": submission_info, + "assessment_info": assessment_info, + "lock_info": lock_info, + } + ) + + return Response(serializer.data) + + # Issues with the XBlock handlers + except XBlockInternalError as ex: + return InternalErrorResponse(context=ex.context) + + # Blanket exception handling + except Exception: + return UnknownErrorResponse() + + +class SubmissionStatusFetchView(StaffGraderBaseView): + """ + GET submission grade status, lock status, and grade data + + Response: { + gradeStatus: (str) one of [graded, ungraded] + lockStatus: (str) one of [locked, unlocked, in-progress] + gradeData: { + score: (dict or None) { + pointsEarned: (int) earned points + pointsPossible: (int) possible points + } + overallFeedback: (string) overall feedback + criteria: (list of dict) [{ + name: (str) name of criterion + feedback: (str) feedback for criterion + points: (int) points of selected option or None if feedback-only criterion + selectedOption: (str) name of selected option or None if feedback-only criterion + }] + } + } + + Errors: + - MissingParamResponse (HTTP 400) for missing params + - XBlockInternalError (HTTP 500) for an issue with ORA + - UnknownError (HTTP 500) for other errors + """ + + @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) + def get(self, request, ora_location, submission_uuid, *args, **kwargs): + try: + assessment_info = get_assessment_info( + request, ora_location, submission_uuid + ) + lock_info = check_submission_lock(request, ora_location, submission_uuid) + + serializer = SubmissionStatusFetchSerializer( + { + "assessment_info": assessment_info, + "lock_info": lock_info, + } + ) + + return Response(serializer.data) + + # Issues with the XBlock handlers + except XBlockInternalError as ex: + return InternalErrorResponse(context=ex.context) + + # Blanket exception handling + except Exception: + return UnknownErrorResponse() + + +class UpdateGradeView(StaffGraderBaseView): + """ + POST submit a grade for a submission + + Body: { + overallFeedback: (string) overall feedback + criteria: [ + { + name: (string) name of criterion + feedback: (string, optional) feedback for criterion + selectedOption: (string) name of selected option or None if feedback-only criterion + }, + ... (one per criteria) + ] + } + + Response: { + gradeStatus: (string) - One of ['graded', 'ungraded'] + lockStatus: (string) - One of ['unlocked', 'locked', 'in-progress'] + gradeData: { + score: (dict or None) { + pointsEarned: (int) earned points + pointsPossible: (int) possible points + } + overallFeedback: (string) overall feedback + criteria: (list of dict) [{ + name: (str) name of criterion + feedback: (str) feedback for criterion + selectedOption: (str) name of selected option or None if feedback-only criterion + }] + } + } + + Errors: + - MissingParamResponse (HTTP 400) for missing params + - GradeContestedResponse (HTTP 409) for trying to submit a grade for a submission you don't have an active lock for + - XBlockInternalError (HTTP 500) for an issue with ORA + - UnknownError (HTTP 500) for other errors + """ + + @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) + def post(self, request, ora_location, submission_uuid, *args, **kwargs): + """Update a grade""" + try: + # Reassert that we have ownership of the submission lock + lock_info = check_submission_lock(request, ora_location, submission_uuid) + if not lock_info.get("lock_status") == "in-progress": + assessment_info = get_assessment_info( + request, ora_location, submission_uuid + ) + submission_status = SubmissionStatusFetchSerializer( + { + "assessment_info": assessment_info, + "lock_info": lock_info, + } + ).data + return GradeContestedResponse(context=submission_status) + + # Transform grade data and submit assessment, rasies on failure + context = {"submission_uuid": submission_uuid} + grade_data = StaffAssessSerializer(request.data, context=context).data + submit_grade(request, ora_location, grade_data) + + # Clear the lock on the graded submission + delete_submission_lock(request, ora_location, submission_uuid) + + # Return submission status info to frontend + assessment_info = get_assessment_info( + request, ora_location, submission_uuid + ) + lock_info = check_submission_lock(request, ora_location, submission_uuid) + serializer = SubmissionStatusFetchSerializer( + { + "assessment_info": assessment_info, + "lock_info": lock_info, + } + ) + return Response(serializer.data) + + # Issues with the XBlock handlers + except XBlockInternalError as ex: + return InternalErrorResponse(context=ex.context) + + # Blanket exception handling + except Exception: + return UnknownErrorResponse() + + +class SubmissionLockView(StaffGraderBaseView): + """ + POST claim a submission lock for grading + DELETE release a submission lock + + Params: + - ora_location (str/UsageID): ORA location for XBlock handling + - submissionUUID (UUID): A submission to lock/unlock + + Response: { + lockStatus + } + + Errors: + - MissingParamResponse (HTTP 400) for missing params + - LockContestedResponse (HTTP 409) for contested lock + - XBlockInternalError (HTTP 500) for an issue with ORA + - UnknownError (HTTP 500) for other errors + """ + + @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) + def post(self, request, ora_location, submission_uuid, *args, **kwargs): + """Claim a submission lock""" + try: + # Validate ORA location + UsageKey.from_string(ora_location) + lock_info = claim_submission_lock(request, ora_location, submission_uuid) + return Response(LockStatusSerializer(lock_info).data) + + # Catch bad ORA location + except (InvalidKeyError, ItemNotFoundError): + return BadOraLocationResponse() + + # Return updated lock info on error + except LockContestedError: + lock_info = check_submission_lock(request, ora_location, submission_uuid) + lock_status = LockStatusSerializer(lock_info).data + return LockContestedResponse(context=lock_status) + + # Issues with the XBlock handlers + except XBlockInternalError as ex: + return InternalErrorResponse(context=ex.context) + + # Blanket exception handling + except Exception: + return UnknownErrorResponse() + + @require_params([PARAM_ORA_LOCATION, PARAM_SUBMISSION_ID]) + def delete(self, request, ora_location, submission_uuid, *args, **kwargs): + """Clear a submission lock""" + try: + # Validate ORA location + UsageKey.from_string(ora_location) + lock_info = delete_submission_lock(request, ora_location, submission_uuid) + return Response(LockStatusSerializer(lock_info).data) + + # Catch bad ORA location + except (InvalidKeyError, ItemNotFoundError): + return BadOraLocationResponse() + + # Return updated lock info on error + except LockContestedError: + lock_info = check_submission_lock(request, ora_location, submission_uuid) + lock_status = LockStatusSerializer(lock_info).data + return LockContestedResponse(context=lock_status) + + # Issues with the XBlock handlers + except XBlockInternalError as ex: + return InternalErrorResponse(context=ex.context) + + # Blanket exception handling + except Exception: + return UnknownErrorResponse() diff --git a/lms/djangoapps/teams/api.py b/lms/djangoapps/teams/api.py index fabceba379..a5d2da67d9 100644 --- a/lms/djangoapps/teams/api.py +++ b/lms/djangoapps/teams/api.py @@ -375,6 +375,22 @@ def get_team_for_user_course_topic(user, course_id, topic_id): ).first() +def get_teams_in_teamset(course_id, topic_id): + """ + Given a course_id and topic_id, return all CourseTeams in the course and topic + """ + try: + course_key = CourseKey.from_string(course_id) + except InvalidKeyError as exc: + raise ValueError("The supplied course id {course_id} is not valid.".format( + course_id=course_id + )) from exc + return CourseTeam.objects.filter( + course_id=course_key, + topic_id=topic_id, + ).all() + + def anonymous_user_ids_for_team(user, team): """ Get the anonymous user IDs for members of a team, used in team submissions Requesting user must be a member of the team or course staff diff --git a/lms/djangoapps/teams/services.py b/lms/djangoapps/teams/services.py index 5c5b5c6bf0..dce876fc6b 100644 --- a/lms/djangoapps/teams/services.py +++ b/lms/djangoapps/teams/services.py @@ -11,6 +11,15 @@ class TeamsService: from . import api return api.get_team_for_user_course_topic(user, course_id, topic_id) + def get_team_names(self, course_id, topic_id): + """ + Given a course and topic id, return a dict mapping from team id to team name for teams in that topic + """ + from . import api + teams = api.get_teams_in_teamset(course_id, topic_id) + name_mapping = {team.team_id: team.name for team in teams} + return name_mapping + def get_team_by_team_id(self, team_id): from . import api return api.get_team_by_team_id(team_id) diff --git a/lms/djangoapps/teams/tests/test_api.py b/lms/djangoapps/teams/tests/test_api.py index 0632bc832f..bc9679a8f6 100644 --- a/lms/djangoapps/teams/tests/test_api.py +++ b/lms/djangoapps/teams/tests/test_api.py @@ -222,6 +222,32 @@ class PythonAPITests(SharedModuleStoreTestCase): team_anonymous_user_ids = teams_api.anonymous_user_ids_for_team(user_staff, self.team1) assert len(self.team1.users.all()) == len(team_anonymous_user_ids) + def test_get_teams_in_teamset__bad_course_id(self): + bad_course_id = 'badcourseid' + with self.assertRaisesMessage(ValueError, f'The supplied course id {bad_course_id} is not valid'): + teams_api.get_teams_in_teamset(bad_course_id, 'teamset-id') + + def test_get_teams_in_teamset_1_1(self): + result = teams_api.get_teams_in_teamset(str(COURSE_KEY1), TOPIC1) + assert len(result) == 2 + assert self.team1 in result + assert self.team1a in result + + def test_get_teams_in_teamset_1_2(self): + result = teams_api.get_teams_in_teamset(str(COURSE_KEY1), TOPIC2) + assert len(result) == 0 + + def test_get_teams_in_teamset_2_2(self): + result = teams_api.get_teams_in_teamset(str(COURSE_KEY2), TOPIC2) + assert len(result) == 2 + assert self.team2 in result + assert self.team2a in result + + def test_get_teams_in_teamset_2_3(self): + result = teams_api.get_teams_in_teamset(str(COURSE_KEY2), TOPIC3) + assert len(result) == 1 + assert self.team3 in result + @ddt.ddt class TeamAccessTests(SharedModuleStoreTestCase): diff --git a/lms/djangoapps/teams/tests/test_services.py b/lms/djangoapps/teams/tests/test_services.py index a9a5c1642f..22baeefadf 100644 --- a/lms/djangoapps/teams/tests/test_services.py +++ b/lms/djangoapps/teams/tests/test_services.py @@ -44,3 +44,28 @@ class TeamsServiceTests(ModuleStoreTestCase): split_url = team_detail_url.split('/') assert split_url[1:] ==\ ['courses', str(self.course_run['key']), 'teams', '#teams', self.team.topic_id, self.team.team_id] + + def test_get_team_names(self): + """ + get_team_names will return a dict mapping the team id to the team name for all teams in the given teamset + """ + additional_teams = [ + CourseTeamFactory.create(course_id=self.course_key, topic_id=self.team.topic_id) + for _ in range(3) + ] + + result = self.service.get_team_names(self.course_key, self.team.topic_id) + + assert result == { + self.team.team_id: self.team.name, + additional_teams[0].team_id: additional_teams[0].name, + additional_teams[1].team_id: additional_teams[1].name, + additional_teams[2].team_id: additional_teams[2].name, + } + + def test_get_team_names__none(self): + """ If there are no teams in the teamset, the function will return an empty list""" + course_run = CourseRunFactory.create() + course_key = course_run['key'] + result = self.service.get_team_names(course_key, "some-topic-id") + assert result == {} diff --git a/lms/envs/common.py b/lms/envs/common.py index 693a0cf40e..5969d7abb0 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -3895,6 +3895,7 @@ OPTIONAL_APPS = [ ('openassessment', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), ('openassessment.assessment', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), ('openassessment.fileupload', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + ('openassessment.staffgrader', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), ('openassessment.workflow', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), ('openassessment.xblock', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), @@ -4731,6 +4732,13 @@ PROGRAM_CONSOLE_MICROFRONTEND_URL = None # .. setting_default: None # .. setting_description: Base URL of the micro-frontend-based courseware page. LEARNING_MICROFRONTEND_URL = None +# .. setting_name: ORA_GRADING_MICROFRONTEND_URL +# .. setting_default: None +# .. setting_description: Base URL of the micro-frontend-based openassessment grading page. +# This is will be show in the open response tab list data. +# .. setting_warning: Also set site's openresponseassessment.enhanced_staff_grader +# waffle flag. +ORA_GRADING_MICROFRONTEND_URL = None # .. setting_name: DISCUSSIONS_MICROFRONTEND_URL # .. setting_default: None # .. setting_description: Base URL of the micro-frontend-based discussions page. diff --git a/lms/envs/devstack.py b/lms/envs/devstack.py index fe422a1cef..be26f49719 100644 --- a/lms/envs/devstack.py +++ b/lms/envs/devstack.py @@ -277,6 +277,7 @@ LOGIN_REDIRECT_WHITELIST.extend([ 'localhost:2001', # frontend-app-course-authoring 'localhost:3001', # frontend-app-library-authoring 'localhost:18400', # frontend-app-publisher + 'localhost:1993', # frontend-app-ora-grading ENTERPRISE_LEARNER_PORTAL_NETLOC, # frontend-app-learner-portal-enterprise ENTERPRISE_ADMIN_PORTAL_NETLOC, # frontend-app-admin-portal ]) diff --git a/lms/urls.py b/lms/urls.py index 350e7f2b10..53e5ee2df3 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -1026,3 +1026,8 @@ if settings.ENABLE_SAVE_FOR_LATER: urlpatterns += [ path('', include('lms.djangoapps.save_for_later.urls')), ] + +# Enhanced Staff Grader (ESG) URLs +urlpatterns += [ + path('api/ora_staff_grader/', include('lms.djangoapps.ora_staff_grader.urls', 'ora-staff-grader')), +] diff --git a/openedx/core/djangoapps/content/course_overviews/tests/factories.py b/openedx/core/djangoapps/content/course_overviews/tests/factories.py index 0604ac9831..b7c1ca1b15 100644 --- a/openedx/core/djangoapps/content/course_overviews/tests/factories.py +++ b/openedx/core/djangoapps/content/course_overviews/tests/factories.py @@ -20,6 +20,7 @@ class CourseOverviewFactory(DjangoModelFactory): # lint-amnesty, pylint: disabl version = CourseOverview.VERSION pre_requisite_courses = [] org = 'edX' + display_number_with_default = 'toy' run = factory.Sequence('2012_Fall_{}'.format) @factory.lazy_attribute @@ -32,7 +33,11 @@ class CourseOverviewFactory(DjangoModelFactory): # lint-amnesty, pylint: disabl @factory.lazy_attribute def id(self): - return CourseLocator(self.org, 'toy', self.run) + return CourseLocator(self.org, self.display_number_with_default, self.run) + + @factory.lazy_attribute + def display_org_with_default(self): + return self.org @factory.lazy_attribute def display_name(self): diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 7fa677a89a..ebb5e8aa83 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -704,7 +704,7 @@ openedx-events==0.7.1 # via -r requirements/edx/base.in openedx-filters==0.4.3 # via -r requirements/edx/base.in -ora2==3.7.8 +ora2==3.8.1 # via -r requirements/edx/base.in packaging==21.3 # via diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index b0a52361ac..c00ae564b0 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -939,7 +939,7 @@ openedx-events==0.7.1 # via -r requirements/edx/testing.txt openedx-filters==0.4.3 # via -r requirements/edx/testing.txt -ora2==3.7.8 +ora2==3.8.1 # via -r requirements/edx/testing.txt packaging==21.3 # via diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 6be25ac863..13b993fd0d 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -889,7 +889,7 @@ openedx-events==0.7.1 # via -r requirements/edx/base.txt openedx-filters==0.4.3 # via -r requirements/edx/base.txt -ora2==3.7.8 +ora2==3.8.1 # via -r requirements/edx/base.txt packaging==21.3 # via