diff --git a/.github/workflows/pylint-checks.yml b/.github/workflows/pylint-checks.yml index 4f95444df5..e64999afb4 100644 --- a/.github/workflows/pylint-checks.yml +++ b/.github/workflows/pylint-checks.yml @@ -21,7 +21,7 @@ jobs: - 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/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/ openedx/core/djangoapps/course_live/" - module-name: openedx-2 - path: "openedx/core/djangoapps/geoinfo/ openedx/core/djangoapps/header_control/ openedx/core/djangoapps/heartbeat/ openedx/core/djangoapps/lang_pref/ openedx/core/djangoapps/models/ openedx/core/djangoapps/monkey_patch/ openedx/core/djangoapps/oauth_dispatch/ openedx/core/djangoapps/olx_rest_api/ openedx/core/djangoapps/password_policy/ openedx/core/djangoapps/plugin_api/ openedx/core/djangoapps/plugins/ openedx/core/djangoapps/profile_images/ openedx/core/djangoapps/programs/ openedx/core/djangoapps/safe_sessions/ openedx/core/djangoapps/schedules/ openedx/core/djangoapps/self_paced/ openedx/core/djangoapps/service_status/ openedx/core/djangoapps/session_inactivity_timeout/ openedx/core/djangoapps/signals/ openedx/core/djangoapps/site_configuration/ openedx/core/djangoapps/system_wide_roles/ openedx/core/djangoapps/theming/ openedx/core/djangoapps/user_api/ openedx/core/djangoapps/user_authn/ openedx/core/djangoapps/util/ openedx/core/djangoapps/verified_track_content/ openedx/core/djangoapps/video_config/ openedx/core/djangoapps/video_pipeline/ openedx/core/djangoapps/waffle_utils/ openedx/core/djangoapps/xblock/ openedx/core/djangoapps/xmodule_django/ openedx/core/tests/ openedx/features/ openedx/testing/ openedx/tests/ openedx/core/djangoapps/learner_pathway/" + path: "openedx/core/djangoapps/geoinfo/ openedx/core/djangoapps/header_control/ openedx/core/djangoapps/heartbeat/ openedx/core/djangoapps/lang_pref/ openedx/core/djangoapps/models/ openedx/core/djangoapps/monkey_patch/ openedx/core/djangoapps/oauth_dispatch/ openedx/core/djangoapps/olx_rest_api/ openedx/core/djangoapps/password_policy/ openedx/core/djangoapps/plugin_api/ openedx/core/djangoapps/plugins/ openedx/core/djangoapps/profile_images/ openedx/core/djangoapps/programs/ openedx/core/djangoapps/safe_sessions/ openedx/core/djangoapps/schedules/ openedx/core/djangoapps/service_status/ openedx/core/djangoapps/session_inactivity_timeout/ openedx/core/djangoapps/signals/ openedx/core/djangoapps/site_configuration/ openedx/core/djangoapps/system_wide_roles/ openedx/core/djangoapps/theming/ openedx/core/djangoapps/user_api/ openedx/core/djangoapps/user_authn/ openedx/core/djangoapps/util/ openedx/core/djangoapps/verified_track_content/ openedx/core/djangoapps/video_config/ openedx/core/djangoapps/video_pipeline/ openedx/core/djangoapps/waffle_utils/ openedx/core/djangoapps/xblock/ openedx/core/djangoapps/xmodule_django/ openedx/core/tests/ openedx/features/ openedx/testing/ openedx/tests/ openedx/core/djangoapps/learner_pathway/" - module-name: common path: "common" - module-name: cms diff --git a/.github/workflows/unit-test-shards.json b/.github/workflows/unit-test-shards.json index 838d0fe261..d106c71e04 100644 --- a/.github/workflows/unit-test-shards.json +++ b/.github/workflows/unit-test-shards.json @@ -132,7 +132,6 @@ "openedx/core/djangoapps/programs/", "openedx/core/djangoapps/safe_sessions/", "openedx/core/djangoapps/schedules/", - "openedx/core/djangoapps/self_paced/", "openedx/core/djangoapps/service_status/", "openedx/core/djangoapps/session_inactivity_timeout/", "openedx/core/djangoapps/signals/", @@ -212,7 +211,6 @@ "openedx/core/djangoapps/programs/", "openedx/core/djangoapps/safe_sessions/", "openedx/core/djangoapps/schedules/", - "openedx/core/djangoapps/self_paced/", "openedx/core/djangoapps/service_status/", "openedx/core/djangoapps/session_inactivity_timeout/", "openedx/core/djangoapps/signals/", diff --git a/cms/djangoapps/contentstore/management/commands/edit_course_tabs.py b/cms/djangoapps/contentstore/management/commands/edit_course_tabs.py index feb624ce49..0381d638d8 100644 --- a/cms/djangoapps/contentstore/management/commands/edit_course_tabs.py +++ b/cms/djangoapps/contentstore/management/commands/edit_course_tabs.py @@ -33,7 +33,7 @@ def print_course(course): # course.tabs looks like this -# [{u'type': u'courseware'}, {u'type': u'course_info', u'name': u'Course Info'}, {u'type': u'textbooks'}, +# [{u'type': u'courseware'}, {u'type': u'textbooks'}, # {u'type': u'discussion', u'name': u'Discussion'}, {u'type': u'wiki', u'name': u'Wiki'}, # {u'type': u'progress', u'name': u'Progress'}] @@ -53,7 +53,7 @@ command again, adding --insert or --delete to edit the list. course_help = '--course required, e.g. Stanford/CS99/2013_spring' delete_help = '--delete ' - insert_help = '--insert , e.g. 4 "course_info" "Course Info"' + insert_help = '--insert , e.g. 4 "discussion" "Discussion"' def add_arguments(self, parser): parser.add_argument('--course', diff --git a/cms/djangoapps/contentstore/management/commands/tests/test_backfill_course_tabs.py b/cms/djangoapps/contentstore/management/commands/tests/test_backfill_course_tabs.py index 258f00a133..a83eb621b3 100644 --- a/cms/djangoapps/contentstore/management/commands/tests/test_backfill_course_tabs.py +++ b/cms/djangoapps/contentstore/management/commands/tests/test_backfill_course_tabs.py @@ -40,13 +40,13 @@ class BackfillCourseTabsTest(ModuleStoreTestCase): course = CourseFactory() course.tabs = [tab for tab in course.tabs if tab.type != 'dates'] self.update_course(course, ModuleStoreEnum.UserID.test) - assert len(course.tabs) == 6 + assert len(course.tabs) == 5 assert 'dates' not in {tab.type for tab in course.tabs} call_command('backfill_course_tabs') course = self.store.get_course(course.id) - assert len(course.tabs) == 7 + assert len(course.tabs) == 6 assert 'dates' in {tab.type for tab in course.tabs} mock_logger.info.assert_any_call(f'Updating tabs for {course.id}.') mock_logger.info.assert_any_call(f'Successfully updated tabs for {course.id}.') @@ -66,16 +66,16 @@ class BackfillCourseTabsTest(ModuleStoreTestCase): CourseFactory() CourseFactory() course = CourseFactory() - course.tabs = [tab for tab in course.tabs if tab.type in ('course_info', 'courseware')] + course.tabs = [tab for tab in course.tabs if tab.type == 'courseware'] self.update_course(course, ModuleStoreEnum.UserID.test) - assert len(course.tabs) == 2 + assert len(course.tabs) == 1 assert 'dates' not in {tab.type for tab in course.tabs} assert 'progress' not in {tab.type for tab in course.tabs} call_command('backfill_course_tabs') course = self.store.get_course(course.id) - assert len(course.tabs) == 7 + assert len(course.tabs) == 6 assert 'dates' in {tab.type for tab in course.tabs} assert 'progress' in {tab.type for tab in course.tabs} mock_logger.info.assert_any_call('4 courses read from modulestore. Processing 0 to 4.') @@ -99,8 +99,8 @@ class BackfillCourseTabsTest(ModuleStoreTestCase): course_2 = CourseFactory() course_2.tabs = [tab for tab in course_2.tabs if tab.type != 'progress'] self.update_course(course_2, ModuleStoreEnum.UserID.test) - assert len(course_1.tabs) == 6 - assert len(course_2.tabs) == 6 + assert len(course_1.tabs) == 5 + assert len(course_2.tabs) == 5 assert 'dates' not in {tab.type for tab in course_1.tabs} assert 'progress' not in {tab.type for tab in course_2.tabs} @@ -108,8 +108,8 @@ class BackfillCourseTabsTest(ModuleStoreTestCase): course_1 = self.store.get_course(course_1.id) course_2 = self.store.get_course(course_2.id) - assert len(course_1.tabs) == 7 - assert len(course_2.tabs) == 7 + assert len(course_1.tabs) == 6 + assert len(course_2.tabs) == 6 assert 'dates' in {tab.type for tab in course_1.tabs} assert 'progress' in {tab.type for tab in course_2.tabs} mock_logger.info.assert_any_call('2 courses read from modulestore. Processing 0 to 2.') @@ -168,13 +168,13 @@ class BackfillCourseTabsTest(ModuleStoreTestCase): def test_arguments_batching(self, start, count, expected_tabs_modified): courses = CourseFactory.create_batch(4) for course in courses: - course.tabs = [tab for tab in course.tabs if tab.type in ('course_info', 'courseware')] + course.tabs = [tab for tab in course.tabs if tab.type == 'courseware'] course = self.update_course(course, ModuleStoreEnum.UserID.test) - assert len(course.tabs) == 2 + assert len(course.tabs) == 1 BackfillCourseTabsConfig.objects.create(enabled=True, start_index=start, count=count) call_command('backfill_course_tabs') for i, course in enumerate(courses): course = self.store.get_course(course.id) - assert len(course.tabs) == (7 if expected_tabs_modified[i] else 2), f'Wrong tabs for course index {i}' + assert len(course.tabs) == (6 if expected_tabs_modified[i] else 1), f'Wrong tabs for course index {i}' diff --git a/cms/djangoapps/contentstore/tests/test_course_listing.py b/cms/djangoapps/contentstore/tests/test_course_listing.py index 3c9705fdf2..9baa1c044f 100644 --- a/cms/djangoapps/contentstore/tests/test_course_listing.py +++ b/cms/djangoapps/contentstore/tests/test_course_listing.py @@ -315,24 +315,24 @@ class TestCourseListing(ModuleStoreTestCase): all of them. """ org_course_one = self.store.make_course_key('AwesomeOrg', 'Course1', 'RunBabyRun') - CourseFactory.create( + course_1 = CourseFactory.create( org=org_course_one.org, number=org_course_one.course, run=org_course_one.run ) + CourseOverviewFactory.create(id=course_1.id, org='AwesomeOrg') org_course_two = self.store.make_course_key('AwesomeOrg', 'Course2', 'RunBabyRun') - CourseFactory.create( + course_2 = CourseFactory.create( org=org_course_two.org, number=org_course_two.course, run=org_course_two.run ) + CourseOverviewFactory.create(id=course_2.id, org='AwesomeOrg') # Two types of org-wide roles have edit permissions: staff and instructor. We test both role.add_users(self.user) - with self.assertRaises(AccessListFallback): - _accessible_courses_list_from_groups(self.request) courses_list, __ = get_courses_accessible_to_user(self.request) # Verify fetched accessible courses list is a list of CourseSummery instances and test expacted @@ -340,6 +340,17 @@ class TestCourseListing(ModuleStoreTestCase): self.assertEqual(len(list(courses_list)), 2) self.assertTrue(all(isinstance(course, CourseOverview) for course in courses_list)) + @ddt.data(OrgStaffRole(), OrgInstructorRole()) + def test_course_listing_org_permissions_exception(self, role): + """ + Create roles with no course_id neither org to make sure AccessListFallback is raised for + platform-wide permissions + """ + role.add_users(self.user) + + with self.assertRaises(AccessListFallback): + _accessible_courses_list_from_groups(self.request) + def test_course_listing_with_actions_in_progress(self): sourse_course_key = CourseLocator('source-Org', 'source-Course', 'source-Run') diff --git a/cms/djangoapps/contentstore/tests/test_import.py b/cms/djangoapps/contentstore/tests/test_import.py index 41b06729ac..4c63ecd735 100644 --- a/cms/djangoapps/contentstore/tests/test_import.py +++ b/cms/djangoapps/contentstore/tests/test_import.py @@ -173,7 +173,7 @@ class ContentStoreImportTest(ModuleStoreTestCase): def test_tab_name_imports_correctly(self): _module_store, _content_store, course = self.load_test_import_course() print(f"course tabs = {course.tabs}") - self.assertEqual(course.tabs[2]['name'], 'Syllabus') + self.assertEqual(course.tabs[1]['name'], 'Syllabus') def test_import_performance_mongo(self): store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) diff --git a/cms/djangoapps/contentstore/tests/test_utils.py b/cms/djangoapps/contentstore/tests/test_utils.py index d0c76125e9..b166de0ef9 100644 --- a/cms/djangoapps/contentstore/tests/test_utils.py +++ b/cms/djangoapps/contentstore/tests/test_utils.py @@ -738,32 +738,41 @@ class ValidateCourseOlxTests(CourseTestCase): ) -class DetermineLabelTestCase(TestCase): - """Tests for xblock Title quirks""" +class DetermineLabelTests(TestCase): + """Tests for xblock Title""" - def validate_html_replaced_with_text(self): + def test_html_replaced_with_text_for_none(self): """ - Tests that display names for "html" xblocks are repleaced with "Text" when the display name is otherwise unset. + Tests that display names for "html" xblocks are replaced with "Text" when the display name is otherwise unset. """ display_name = None block_type = "html" result = utils.determine_label(display_name, block_type) - self.assertEqual(result, display_name) + self.assertEqual(result, "Text") - def validate_set_titles_not_replaced(self): + def test_html_replaced_with_text_for_empty(self): """ - Tests that display names for "html" xblocks are not repleaced with "Text" when the display name is set. + Tests that display names for "html" xblocks are replaced with "Text" when the display name is empty. + """ + display_name = "" + block_type = "html" + result = utils.determine_label(display_name, block_type) + self.assertEqual(result, "Text") + + def test_set_titles_not_replaced(self): + """ + Tests that display names for "html" xblocks are not replaced with "Text" when the display name is set. """ display_name = "Something" block_type = "html" result = utils.determine_label(display_name, block_type) - self.assertEqual(result, display_name) + self.assertEqual(result, "Something") - def validate_non_html_blocks_titles_not_replaced(self): + def test_non_html_blocks_titles_not_replaced(self): """ - Tests that display names for non-"html" xblocks are not repleaced with "Text" when the display name is set. + Tests that display names for non-"html" xblocks are not replaced with "Text" when the display name is set. """ display_name = None block_type = "something else" result = utils.determine_label(display_name, block_type) - self.assertEqual(result, display_name) + self.assertEqual(result, "something else") diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index bb5a53fb94..3aa171c922 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -708,10 +708,13 @@ def determine_label(display_name, block_type): Returns the name of the xblock to display in studio. Please see TNL-9838. """ - label = display_name - if block_type == 'html': - label = _("Text") - return label + if display_name in {"", None}: + if block_type == 'html': + return _("Text") + else: + return block_type + else: + return display_name @contextmanager diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 10b78596ae..b8db950d74 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -483,10 +483,20 @@ def _accessible_courses_list_from_groups(request): courses_list = [] course_keys = {} + user_global_orgs = set() for course_access in all_courses: - if course_access.course_id is None: + if course_access.course_id is not None: + course_keys[course_access.course_id] = course_access.course_id + elif course_access.org: + user_global_orgs.add(course_access.org) + else: raise AccessListFallback - course_keys[course_access.course_id] = course_access.course_id + + if user_global_orgs: + # Getting courses from user global orgs + overviews = CourseOverview.get_all_courses(orgs=list(user_global_orgs)) + overviews_course_keys = {overview.id: overview.id for overview in overviews} + course_keys.update(overviews_course_keys) course_keys = list(course_keys.values()) diff --git a/cms/djangoapps/contentstore/views/preview.py b/cms/djangoapps/contentstore/views/preview.py index b64a77ddb2..0d7e8786b4 100644 --- a/cms/djangoapps/contentstore/views/preview.py +++ b/cms/djangoapps/contentstore/views/preview.py @@ -212,7 +212,6 @@ def _preview_module_system(request, descriptor, field_data): # TODO (cpennington): Do we want to track how instructors are using the preview problems? track_function=lambda event_type, event: None, get_module=partial(_load_preview_module, request), - debug=True, mixins=settings.XBLOCK_MIXINS, course_id=course_id, diff --git a/cms/djangoapps/contentstore/views/tabs.py b/cms/djangoapps/contentstore/views/tabs.py index 5482be63dc..85f6b89850 100644 --- a/cms/djangoapps/contentstore/views/tabs.py +++ b/cms/djangoapps/contentstore/views/tabs.py @@ -220,8 +220,8 @@ def get_tab_by_locator(tab_list: List[CourseTab], tab_location: Union[str, Usage def validate_args(num, tab_type): "Throws for the disallowed cases." - if num <= 1: - raise ValueError('Tabs 1 and 2 cannot be edited') + if num < 1: + raise ValueError('Tab 1 cannot be edited') if tab_type == 'static_tab': raise ValueError('Tabs of type static_tab cannot be edited here (use Studio)') diff --git a/cms/djangoapps/contentstore/views/tests/test_tabs.py b/cms/djangoapps/contentstore/views/tests/test_tabs.py index e366420d22..58a6393585 100644 --- a/cms/djangoapps/contentstore/views/tests/test_tabs.py +++ b/cms/djangoapps/contentstore/views/tests/test_tabs.py @@ -113,7 +113,7 @@ class TabsPageTests(CourseTestCase): def test_reorder_tabs_invalid_tab(self): """Test re-ordering of tabs with invalid tab""" - invalid_tab_ids = ['courseware', 'info', 'invalid_tab_id'] + invalid_tab_ids = ['courseware', 'invalid_tab_id'] # post the request resp = self.client.ajax_post( @@ -189,16 +189,14 @@ class PrimitiveTabEdit(ModuleStoreTestCase): course = CourseFactory.create() with self.assertRaises(ValueError): tabs.primitive_delete(course, 0) - with self.assertRaises(ValueError): - tabs.primitive_delete(course, 1) with self.assertRaises(IndexError): - tabs.primitive_delete(course, 7) + tabs.primitive_delete(course, 6) - assert course.tabs[2] != {'type': 'dates', 'name': 'Dates'} - tabs.primitive_delete(course, 2) + assert course.tabs[1] != {'type': 'dates', 'name': 'Dates'} + tabs.primitive_delete(course, 1) assert {'type': 'progress'} not in course.tabs # Check that dates has shifted up - assert course.tabs[2] == {'type': 'dates', 'name': 'Dates'} + assert course.tabs[1] == {'type': 'dates', 'name': 'Dates'} def test_insert(self): """Test primitive tab insertion.""" diff --git a/cms/envs/common.py b/cms/envs/common.py index 876fe559aa..d932f61c47 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -1112,10 +1112,10 @@ COURSES_WITH_UNSAFE_CODE = [] # Cojail REST service ENABLE_CODEJAIL_REST_SERVICE = False # .. setting_name: CODE_JAIL_REST_SERVICE_REMOTE_EXEC -# .. setting_default: 'common.lib.capa.capa.safe_exec.remote_exec.send_safe_exec_request_v0' +# .. setting_default: 'capa.safe_exec.remote_exec.send_safe_exec_request_v0' # .. setting_description: Set the python package.module.function that is reponsible of # calling the remote service in charge of jailed code execution -CODE_JAIL_REST_SERVICE_REMOTE_EXEC = 'common.lib.capa.capa.safe_exec.remote_exec.send_safe_exec_request_v0' +CODE_JAIL_REST_SERVICE_REMOTE_EXEC = 'capa.safe_exec.remote_exec.send_safe_exec_request_v0' # .. setting_name: CODE_JAIL_REST_SERVICE_HOST # .. setting_default: 'http://127.0.0.1:8550' # .. setting_description: Set the codejail remote service host @@ -1661,9 +1661,6 @@ INSTALLED_APPS = [ # edx-milestones service 'milestones', - # Self-paced course configuration - 'openedx.core.djangoapps.self_paced', - # Coursegraph 'cms.djangoapps.coursegraph.apps.CoursegraphConfig', diff --git a/cms/envs/production.py b/cms/envs/production.py index 82244acbde..8a20a6ba12 100644 --- a/cms/envs/production.py +++ b/cms/envs/production.py @@ -110,8 +110,8 @@ EDX_PLATFORM_REVISION = REVISION_CONFIG.get('EDX_PLATFORM_REVISION', EDX_PLATFOR BROKER_POOL_LIMIT = 0 BROKER_CONNECTION_TIMEOUT = 1 -# For the Result Store, use the django cache named 'celery' -CELERY_RESULT_BACKEND = 'django-cache' +# Allow env to configure celery result backend with default set to django-cache +CELERY_RESULT_BACKEND = ENV_TOKENS.get('CELERY_RESULT_BACKEND', 'django-cache') # When the broker is behind an ELB, use a heartbeat to refresh the # connection and to detect if it has been dropped. diff --git a/cms/lib/xblock/test/test_authoring_mixin.py b/cms/lib/xblock/test/test_authoring_mixin.py index 9d848b06ba..6028f39614 100644 --- a/cms/lib/xblock/test/test_authoring_mixin.py +++ b/cms/lib/xblock/test/test_authoring_mixin.py @@ -14,9 +14,9 @@ from xmodule.partitions.partitions import ( Group, UserPartition ) +from xmodule.tests.test_export import PureXBlock from common.djangoapps.course_modes.tests.factories import CourseModeFactory -from common.lib.xmodule.xmodule.tests.test_export import PureXBlock class AuthoringMixinTestCase(ModuleStoreTestCase): diff --git a/cms/static/js/i18n/ar/djangojs.js b/cms/static/js/i18n/ar/djangojs.js index 4c5b0cc8a8..89faf805af 100644 --- a/cms/static/js/i18n/ar/djangojs.js +++ b/cms/static/js/i18n/ar/djangojs.js @@ -168,7 +168,6 @@ "(Caption will be displayed when you start playing the video.)": "(\u0633\u062a\u064f\u0639\u0631\u0636 \u0627\u0644\u0639\u0646\u0627\u0648\u064a\u0646 \u0627\u0644\u0641\u0631\u0639\u064a\u0651\u0629 \u0645\u0639 \u0628\u062f\u0621 \u0639\u0631\u0636 \u0645\u0642\u0637\u0639 \u0627\u0644\u0641\u064a\u062f\u064a\u0648.)", "(Community TA)": "(\u0645\u062c\u0645\u0648\u0639\u0629 \u0645\u0633\u0627\u0639\u062f\u064a \u0627\u0644\u0645\u0639\u0644\u0645\u064a\u0646)", "(Optional)": "(\u0627\u062e\u062a\u064a\u0627\u0631\u064a)", - "(Read-only)": "(\u0644\u0644\u0642\u0631\u0627\u0621\u0629 \u0641\u0642\u0637)", "(Required Field)": "(\u062d\u0642\u0644 \u0645\u0637\u0644\u0648\u0628)", "(Self-paced) Ended {end}": "(\u062a\u062f\u0631\u064a\u0628 \u0630\u0627\u062a\u064a) \u0627\u0646\u062a\u0647\u0649 {end}", "(Self-paced) Ends {end}": "(\u062a\u062f\u0631\u064a\u0628 \u0630\u0627\u062a\u064a) \u064a\u0646\u062a\u0647\u064a \u0628\u062a\u0627\u0631\u064a\u062e {end}", @@ -322,7 +321,6 @@ "An error has occurred. Wait a few minutes, and then try again.": "\u062d\u062f\u062b \u062e\u0637\u0623. \u0627\u0646\u062a\u0638\u0631 \u0628\u0636\u0639 \u062f\u0642\u0627\u0626\u0642 \u060c \u062b\u0645 \u062d\u0627\u0648\u0644 \u0645\u0631\u0629 \u0623\u062e\u0631\u0649.", "An error occurred retrieving your email. Please try again later, and contact technical support if the problem persists.": "\u0646\u0623\u0633\u0641 \u0644\u062d\u062f\u0648\u062b \u062e\u0637\u0623 \u0641\u064a \u0627\u0633\u062a\u0631\u062f\u0627\u062f \u0631\u0633\u0627\u0644\u0629 \u0627\u0644\u0628\u0631\u064a\u062f \u0627\u0644\u0625\u0644\u0643\u062a\u0631\u0648\u0646\u064a. \u064a\u064f\u0631\u062c\u0649 \u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0645\u062d\u0627\u0648\u0644\u0629 \u0644\u0627\u062d\u0642\u064b\u0627\u060c \u0648\u0627\u0644\u0627\u062a\u0651\u0635\u0627\u0644 \u0628\u0641\u0631\u064a\u0642 \u0627\u0644\u062f\u0639\u0645 \u0627\u0644\u0641\u0646\u0651\u064a \u0641\u064a \u062d\u0627\u0644 \u0627\u0633\u062a\u0645\u0631\u0651\u062a \u0627\u0644\u0645\u0634\u0643\u0644\u0629.", "An error occurred when signing you in to %s.": "\u062d\u062f\u062b \u062e\u0637\u0623 \u0623\u062b\u0646\u0627\u0621 \u0645\u062d\u0627\u0648\u0644\u0629 \u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644 \u0625\u0644\u0649 %s.", - "An error occurred while attempting to retrieve or save the information below. Please try again later.": "\u062d\u062f\u062b \u062e\u0637\u0623 \u0623\u062b\u0646\u0627\u0621 \u0645\u062d\u0627\u0648\u0644\u0629 \u0627\u0633\u062a\u0631\u062f\u0627\u062f \u0623\u0648 \u062d\u0641\u0638 \u0627\u0644\u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0623\u062f\u0646\u0627\u0647\u060c \u064a\u0631\u062c\u0649 \u0627\u0644\u0645\u062d\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062e\u0631\u0649 \u0644\u0627\u062d\u0642\u064b\u0627.", "An error occurred while looking up team membership. Try refreshing the page.": "\u062d\u062f\u062b \u062e\u0637\u0623 \u0623\u062b\u0646\u0627\u0621 \u0627\u0644\u0628\u062d\u062b \u0639\u0646 \u0639\u0636\u0648\u064a\u0629 \u0627\u0644\u0641\u0631\u064a\u0642. \u062d\u0627\u0648\u0644 \u062a\u062d\u062f\u064a\u062b \u0627\u0644\u0635\u0641\u062d\u0629.", "An error occurred while removing the member from the team. Try again.": "\u0646\u0623\u0633\u0641 \u0644\u062d\u062f\u0648\u062b \u062e\u0637\u0623 \u0623\u062b\u0646\u0627\u0621 \u0625\u0633\u062a\u0628\u0639\u0627\u062f \u0639\u0636\u0648 \u0645\u0646 \u0627\u0644\u0641\u0631\u064a\u0642. \u064a\u064f\u0631\u062c\u0649 \u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0645\u062d\u0627\u0648\u0644\u0629.", "An error occurred.": "\u0646\u0623\u0633\u0641 \u0644\u062d\u062f\u0648\u062b \u062e\u0637\u0623. ", @@ -409,7 +407,6 @@ "Bottom": "\u0627\u0644\u0623\u0633\u0641\u0644", "Browse": "\u062a\u0635\u0641\u0651\u062d", "Browse recently launched courses and see what\\'s new in your favorite subjects": "\u062a\u0635\u0641\u062d \u0627\u0644\u0641\u0635\u0648\u0644 \u062d\u062f\u064a\u062b\u0629 \u0627\u0644\u0625\u0646\u0634\u0627\u0621 \u0648\u0634\u0627\u0647\u062f \u0627\u0644\u062c\u062f\u064a\u062f \u0641\u064a \u0645\u0648\u0627\u0636\u064a\u0639\u0643 \u0627\u0644\u0645\u0641\u0636\u0644\u0629.", - "Browsing": "\u0627\u0644\u062a\u0635\u0641\u062d", "Bulk Exceptions": "\u0645\u062c\u0645\u0648\u0639\u0629 \u0645\u0646 \u0627\u0644\u0627\u0633\u062a\u062b\u0646\u0627\u0621\u0627\u062a", "Bullet list": "\u0642\u0627\u0626\u0645\u0629 \u062a\u0639\u062f\u0627\u062f \u0646\u0642\u0637\u064a", "Bulleted List (Ctrl+U)": "\u0642\u0627\u0626\u0645\u0629 \u0645\u0646\u0642\u0651\u0637\u0629 (Ctrl+U)", @@ -594,9 +591,7 @@ "Course Name": "\u0627\u0633\u0645 \u0627\u0644\u0645\u0633\u0627\u0642", "Course Number": "\u0631\u0642\u0645 \u0627\u0644\u0645\u0633\u0627\u0642 ", "Course Number Override": "\u062a\u062c\u0627\u0648\u0632 \u0631\u0642\u0645 \u0627\u0644\u0645\u0633\u0627\u0642", - "Course Number:": "\u0631\u0642\u0645 \u0627\u0644\u0645\u0633\u0627\u0642:", "Course Outline": "\u0645\u062e\u0637\u0651\u0637 \u0627\u0644\u0645\u0633\u0627\u0642", - "Course Run:": "\u062a\u0634\u063a\u064a\u0644 \u0627\u0644\u0645\u0633\u0627\u0642:", "Course Start": "\u062a\u0627\u0631\u064a\u062e \u0627\u0628\u062a\u062f\u0627\u0621 \u0627\u0644\u0645\u0633\u0627\u0642", "Course Title": "\u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0645\u0633\u0627\u0642", "Course Title Override": "\u062a\u062c\u0627\u0648\u0632 \u0639\u0646\u0648\u0627\u0646 \u0627\u0644\u0645\u0633\u0627\u0642", @@ -608,7 +603,6 @@ "Create": "\u0625\u0646\u0634\u0627\u0621 ", "Create Account": "\u0625\u0646\u0634\u0627\u0621 \u062d\u0633\u0627\u0628 ", "Create Re-run": "\u062e\u0644\u0642 \u0648\u062d\u062f\u0629 \u0645\u064f\u0639\u0627\u062f \u062a\u0646\u0641\u064a\u0630\u0647\u0627", - "Create Support Ticket": "\u0625\u0646\u0634\u0627\u0621 \u062a\u0630\u0643\u0631\u0629 \u062f\u0639\u0645", "Create a New Team": "\u0625\u0646\u0634\u0627\u0621 \u0641\u0631\u064a\u0642 \u062c\u062f\u064a\u062f", "Create a content group": "\u0625\u0646\u0634\u0627\u0621 \u0645\u062c\u0645\u0648\u0639\u0629 \u0645\u062d\u062a\u0648\u0649", "Create a new team if you can't find an existing team to join, or if you would like to learn with friends you know.": "\u0623\u0646\u0634\u0626 \u0641\u0631\u064a\u0642\u064b\u0627 \u062c\u062f\u064a\u062f\u064b\u0627 \u0641\u064a \u062d\u0627\u0644 \u0644\u0645 \u062a\u062c\u062f \u0641\u0631\u064a\u0642\u064b\u0627 \u0645\u0648\u062c\u0648\u062f\u064b\u0627 \u0644\u0644\u0627\u0646\u0636\u0645\u0627\u0645 \u0625\u0644\u064a\u0647 \u0623\u0648 \u0643\u0646\u062a \u062a\u0631\u063a\u0628 \u0628\u0627\u0644\u062a\u0639\u0644\u0651\u0645 \u0628\u0631\u0641\u0642\u0629 \u0623\u0635\u062f\u0642\u0627\u0621 \u062a\u0639\u0631\u0641\u0647\u0645.", @@ -676,7 +670,6 @@ "Describe ": "\u0635\u0641", "Description": "\u0627\u0644\u0648\u0635\u0641", "Description of the certificate": "\u0648\u0635\u0641 \u0627\u0644\u0634\u0647\u0627\u062f\u0629", - "Details": "\u0627\u0644\u062a\u0641\u0627\u0635\u064a\u0644", "Device with Camera": "\u062c\u0647\u0627\u0632 \u0645\u0632\u0648\u062f \u0628\u0643\u0627\u0645\u064a\u0631\u0627", "Dimensions": "\u0627\u0644\u0623\u0628\u0639\u0627\u062f", "Disc": "\u0627\u0644\u0642\u0631\u0635", @@ -886,7 +879,6 @@ "Find next": "\u0625\u064a\u062c\u0627\u062f \u0627\u0644\u062a\u0627\u0644\u064a", "Find previous": "\u0625\u064a\u062c\u0627\u062f \u0627\u0644\u0633\u0627\u0628\u0642", "Finish": "\u0625\u0646\u0647\u0627\u0621", - "Finish later": "\u0627\u0644\u0625\u0646\u0647\u0627\u0621 \u0644\u0627\u062d\u0642\u064b\u0627", "First time here?": "\u0644\u0623\u0648\u0644 \u0645\u0631\u0629 \u0647\u0646\u0627\u061f", "Follow": "\u0645\u062a\u0627\u0628\u0639\u0629", "Follow or unfollow posts": "\u062a\u0627\u0628\u0639 \u0623\u0648 \u0623\u0644\u063a\u064a \u0645\u062a\u0627\u0628\u0639\u0629 \u0645\u0646\u0634\u0648\u0631\u0627\u062a", @@ -940,7 +932,6 @@ "H Align": "\u0645\u062d\u0627\u0630\u0627\u0629 \u0623\u0641\u0642\u064a\u0629", "HTML preview of post": "\u0627\u0633\u062a\u0639\u0631\u0627\u0636 \u0627\u0644\u0645\u0646\u0634\u0648\u0631 \u0641\u064a HTML", "HTML source code": "\u0631\u0645\u0632 \u0645\u0635\u062f\u0631 HTML", - "Have you ever served on active duty in the U.S. Armed Forces, Reserves, or National Guard?": "\u0647\u0644 \u0633\u0628\u0642 \u0623\u0646 \u0642\u0645\u062a \u0628\u0627\u0644\u062e\u062f\u0645\u0629 \u0627\u0644\u0639\u0633\u0643\u0631\u064a\u0629 \u0641\u064a \u0627\u0644\u0642\u0648\u0627\u062a \u0627\u0644\u0645\u0633\u0644\u062d\u0629 \u0623\u0648 \u0627\u0644\u062c\u064a\u0634 \u0627\u0644\u0648\u0637\u0646\u064a \u0623\u0648 \u0627\u0644\u0642\u0648\u0629 \u0627\u0644\u0627\u062d\u062a\u064a\u0627\u0637\u064a\u0629\u061f", "Header": "\u062a\u0631\u0648\u064a\u0633\u0629", "Header 1": "\u0627\u0644\u062a\u0631\u0648\u064a\u0633\u0629 1", "Header 2": "\u0627\u0644\u062a\u0631\u0648\u064a\u0633\u0629 2", @@ -1144,7 +1135,6 @@ "Loading more threads": "\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0645\u0632\u064a\u062f \u0645\u0646 \u0627\u0644\u0645\u0648\u0627\u0636\u064a\u0639", "Loading posts list": "\u062a\u062d\u0645\u064a\u0644 \u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0645\u0634\u0627\u0631\u0643\u0627\u062a", "Loading your courses": "\u062c\u0627\u0631\u064a \u062a\u062d\u0645\u064a\u0644 \u0645\u0633\u0627\u0642\u0627\u062a\u0643", - "Loading...": "\u062c\u0627\u0631\u064a \u0627\u0644\u062a\u062d\u0645\u064a\u0644...", "Location": "\u0627\u0644\u0645\u0648\u0642\u0639", "Location in Course": "\u0627\u0644\u0645\u0648\u0642\u0639 \u0641\u064a \u0627\u0644\u0645\u0633\u0627\u0642", "Lock this asset": "\u0642\u0641\u0644 \u0647\u0630\u0647 \u0627\u0644\u0645\u0627\u062f\u0629 \u0627\u0644\u0645\u0644\u062d\u0642\u0629", @@ -1205,7 +1195,6 @@ "Name of the signatory": "\u0627\u0633\u0645 \u0627\u0644\u0645\u0648\u0642\u0651\u0650\u0639", "Name or short description of the configuration": "\u0627\u0633\u0645 \u0627\u0644\u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0623\u0648 \u0648\u0635\u0641 \u0645\u0648\u062c\u064e\u0632 \u0639\u0646\u0647\u0627", "Navigate up": "\u0627\u0646\u062a\u0642\u0644 \u0644\u0623\u0639\u0644\u0649", - "Need help logging in?": "\u0647\u0644 \u062a\u062d\u062a\u0627\u062c \u0645\u0633\u0627\u0639\u062f\u0629 \u0641\u064a \u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644\u061f", "Need help signing in?": "\u0647\u0644 \u062a\u062d\u062a\u0627\u062c \u0625\u0644\u0649 \u0645\u0633\u0627\u0639\u062f\u0629 \u0641\u064a \u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644\u061f", "Need other help signing in?": "\u0647\u0644 \u062a\u062d\u062a\u0627\u062c \u0625\u0644\u0649 \u0645\u0633\u0627\u0639\u062f\u0629 \u0623\u062e\u0631\u0649 \u0641\u064a \u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644\u061f", "Needs verified certificate ": "\u0628\u062d\u0627\u062c\u0629 \u0625\u0644\u0649 \u0634\u0647\u0627\u062f\u0629 \u0645\u0648\u062b\u0642\u0629", @@ -1316,7 +1305,6 @@ "Organization ": "\u0627\u0644\u0645\u0624\u0633\u0633\u0629", "Organization Name": "\u0627\u0633\u0645 \u0627\u0644\u0645\u0624\u0633\u0633\u0629", "Organization of the signatory": "\u0645\u0624\u0633\u0651\u0633\u0629 \u0627\u0644\u0645\u0648\u0642\u0651\u0650\u0639", - "Organization:": "\u0627\u0644\u0645\u0624\u0633\u0651\u0633\u0629:", "Other": "\u0622\u062e\u0631", "Other sign-in issues": "\u0645\u0634\u0627\u0643\u0644 \u0623\u062e\u0631\u0649 \u0645\u0631\u062a\u0628\u0637\u0629 \u0628\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644", "Overall Score": "\u0627\u0644\u0646\u062a\u064a\u062c\u0629 \u0627\u0644\u0643\u0644\u064a\u0629", @@ -1455,7 +1443,6 @@ "Questions raise issues that need answers. Discussions share ideas and start conversations. (Required)": "\u0627\u0644\u0623\u0633\u0626\u0644\u0629 \u0627\u0644\u062a\u064a \u062a\u062b\u064a\u0631 \u0642\u0636\u0627\u064a\u0627 \u062a\u062d\u062a\u0627\u062c \u0625\u0644\u0649 \u0625\u062c\u0627\u0628\u0627\u062a. \u0645\u0646\u0627\u0642\u0634\u0627\u062a \u062a\u0628\u0627\u062f\u0644 \u0627\u0644\u0623\u0641\u0643\u0627\u0631 \u0648\u0628\u062f\u0621 \u0627\u0644\u0645\u062d\u0627\u062f\u062b\u0627\u062a. (\u0645\u0637\u0644\u0648\u0628)", "Queued": "\u062a\u062d\u0645\u064a\u0644 \u0642\u064a\u062f \u0627\u0644\u0627\u0646\u062a\u0638\u0627\u0631", "REMAINING COURSES": "\u0645\u0633\u0627\u0642\u0627\u062a \u0645\u062a\u0628\u0642\u064a\u0629", - "Re-run Course": "\u0645\u0633\u0627\u0642 \u0645\u0634\u063a\u0651\u064e\u0644 \u0645\u0631\u0651\u0629 \u0623\u062e\u0631\u0649", "Read More": "\u0644\u0642\u0631\u0627\u0621\u0629 \u0627\u0644\u0645\u0632\u064a\u062f", "Read more": "\u0627\u0642\u0631\u0623 \u0627\u0644\u0645\u0632\u064a\u062f", "Ready To Start": "\u062c\u0627\u0647\u0632 \u0644\u0644\u0628\u062f\u0621", @@ -1529,7 +1516,6 @@ "Return to Export": "\u0627\u0644\u0639\u0648\u062f\u0629 \u0625\u0644\u0649 \u0627\u0644\u062a\u0635\u062f\u064a\u0631", "Return to Your Dashboard": "\u0627\u0644\u0639\u0648\u062f\u0629 \u0625\u0644\u0649 \u0644\u0648\u062d\u0629 \u0627\u0644\u0645\u0639\u0644\u0648\u0645\u0627\u062a", "Return to all posts": "\u0627\u0644\u0639\u0648\u062f\u0629 \u0625\u0644\u0649 \u062c\u0645\u064a\u0639 \u0627\u0644\u0645\u0634\u0627\u0631\u0643\u0627\u062a", - "Return to my dashboard": "\u0627\u0644\u0639\u0648\u062f\u0629 \u0625\u0644\u0649 \u0644\u0648\u062d\u0629 \u0645\u0639\u0644\u0648\u0645\u0627\u062a\u064a", "Return to team listing": "\u0627\u0644\u0639\u0648\u062f\u0629 \u0625\u0644\u0649 \u0625\u062f\u0631\u0627\u062c \u0627\u0644\u0641\u0631\u064a\u0642", "Review Policy Exception": "\u0645\u0631\u0627\u062c\u0639\u0629 \u0627\u0633\u062a\u062b\u0646\u0627\u0621 \u0627\u0644\u0633\u064a\u0627\u0633\u0629", "Review Rules": "\u0642\u0648\u0627\u0639\u062f \u0627\u0644\u0645\u0631\u0627\u062c\u0639\u0629", @@ -1582,14 +1568,12 @@ "Select a subject for your support request.": "\u0627\u062e\u062a\u0631 \u0645\u0648\u0636\u0648\u0639\u064b\u0627 \u0644\u0637\u0644\u0628 \u0627\u0644\u062f\u0639\u0645.", "Select a time allotment for the exam. If it is over 24 hours, type in the amount of time. You can grant individual learners extra time to complete the exam through the Instructor Dashboard.": "\u0627\u062e\u062a\u0631 \u0641\u062a\u0631\u0629 \u0632\u0645\u0646\u064a\u0629 \u0645\u062e\u0635\u0651\u0635\u0629 \u0644\u0625\u062c\u0631\u0627\u0621 \u0627\u0644\u0627\u0645\u062a\u062d\u0627\u0646. \u0625\u0630\u0627 \u0632\u0627\u062f\u062a \u0627\u0644\u0642\u064a\u0645\u0629 \u0639\u0646 24 \u0633\u0627\u0639\u0629\u060c\u0623\u062f\u062e\u0644 \u0645\u0642\u062f\u0627\u0631\u064b\u0627 \u0645\u0646 \u0627\u0644\u0648\u0642\u062a. \u064a\u0645\u0643\u0646\u0643 \u0643\u0645\u062c \u0645\u062a\u0639\u0644\u0651\u0645\u064a\u0646 \u0645\u062d\u062f\u062f\u064a\u0646 \u0632\u0645\u0646\u064b\u0627 \u0625\u0636\u0627\u0641\u064a\u064b\u0627 \u0644\u0625\u062c\u0631\u0627\u0621 \u0627\u0644\u0627\u0645\u062a\u062d\u0627\u0646 \u0645\u0646 \u062e\u0644\u0627\u0644 \u0644\u0648\u062d\u0629 \u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0627\u0644\u0623\u0633\u062a\u0627\u0630.", "Select all": "\u0627\u062e\u062a\u064a\u0627\u0631 \u0627\u0644\u0643\u0644", - "Select employment status": "\u0641\u0636\u0644\u064b\u0627 \u062d\u062f\u062f \u062d\u0627\u0644\u062a\u0643 \u0627\u0644\u0648\u0638\u064a\u0641\u064a\u0629", "Select fidelity": "\u062d\u062f\u062f \u0627\u0644\u062f\u0642\u0629", "Select language": "\u062d\u062f\u062f \u0627\u0644\u0644\u063a\u0629", "Select one or more groups:": "\u062d\u062f\u062f \u0645\u062c\u0645\u0648\u0639\u0629 \u0648\u0627\u062d\u062f\u0629 \u0623\u0648 \u0623\u0643\u062b\u0631:", "Select the course-wide discussion topics that you want to divide.": "\u062d\u062f\u062f \u0645\u0648\u0627\u0636\u064a\u0639 \u0627\u0644\u0645\u062d\u0627\u062f\u062b\u0629 \u0641\u064a \u0627\u0644\u0645\u0633\u0627\u0642 \u0627\u0644\u062a\u064a \u062a\u0648\u062f \u062a\u0642\u0633\u064a\u0645\u0647\u0627.", "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browser's local time zone.": "\u062d\u062f\u062f \u0627\u0644\u0645\u0646\u0637\u0642\u0629 \u0627\u0644\u0632\u0645\u0646\u064a\u0629 \u0644\u0639\u0631\u0636 \u0645\u0648\u0627\u0639\u064a\u062f \u0627\u0644\u062f\u0648\u0631\u0629. \u0625\u0630\u0627 \u0644\u0645 \u062a\u0642\u0645 \u0628\u062a\u062d\u062f\u064a\u062f \u0645\u0646\u0637\u0642\u0629 \u0632\u0645\u0646\u064a\u0629\u060c \u0633\u064a\u062a\u0645 \u0639\u0631\u0636 \u0645\u0648\u0627\u0639\u064a\u062f \u0627\u0644\u062f\u0648\u0631\u0629\u060c \u0628\u0645\u0627 \u0641\u064a \u0630\u0644\u0643 \u0645\u0648\u0627\u0639\u064a\u062f \u062a\u0642\u062f\u064a\u0645 \u0627\u0644\u0648\u0627\u062c\u0628\u0627\u062a\u060c \u0648\u0641\u0642\u0627\u064b \u0644\u0645\u0646\u0637\u0642\u0629 \u0627\u0644\u062a\u0648\u0642\u064a\u062a \u0627\u0644\u0645\u062d\u0644\u064a \u0641\u064a \u0627\u0644\u0645\u062a\u0635\u0641\u062d.", "Select turnaround": "\u0627\u062e\u062a\u0631 \u0645\u062e\u0637\u0637 \u0627\u0644\u0627\u0646\u062c\u0627\u0632", - "Selected blocks": "\u0627\u0644\u0643\u062a\u0644 \u0627\u0644\u0645\u062d\u062f\u062f\u0629", "Selected tab": "\u0627\u0644\u062a\u0628\u0648\u064a\u0628\u0629 \u0627\u0644\u0645\u0646\u062a\u0642\u0627\u0629", "Self": "\u0645\u064f\u0642\u064a\u0651\u064e\u0645 \u0630\u0627\u062a\u064a\u0651\u064b\u0627", "Send to:": "\u0623\u0631\u0633\u0644 \u0625\u0644\u0649: ", @@ -2213,17 +2197,11 @@ "Weight of Total Grade": "\u0648\u0632\u0646 \u0627\u0644\u062f\u0631\u062c\u0629 \u0627\u0644\u0625\u062c\u0645\u0627\u0644\u064a\u0629", "We\u2019re sorry to see you go!": "\u0646\u0623\u0633\u0641 \u0644\u0630\u0647\u0627\u0628\u0643!", "What You Need for Verification": "\u0645\u0627 \u0627\u0644\u0630\u064a \u062a\u062d\u062a\u0627\u062c\u0647 \u0625\u0644\u064a\u0647 \u0644\u0625\u062c\u0631\u0627\u0621 \u0627\u0644\u062a\u062d\u0642\u0651\u0642", - "What can we help you with, {username}?": "\u0643\u064a\u0641 \u064a\u0645\u0643\u0646\u0646\u0627 \u0645\u0633\u0627\u0639\u062f\u062a\u0643\u060c {username}\u061f", "What does %(platformName)s do with this photo?": "\u0645\u0627 \u0627\u0644\u0630\u064a \u062a\u0641\u0639\u0644\u0647 %(platformName)s \u0628\u0647\u0630\u0647 \u0627\u0644\u0635\u0648\u0631\u0629\u061f", "What does this mean?": "\u0645\u0627\u0630\u0627 \u064a\u0639\u0646\u064a \u0647\u0630\u0627\u061f", "What if I can't see the camera image, or if I can't see my photo do determine which side is visible?": "\u0645\u0627\u0630\u0627 \u0644\u0648 \u0644\u0645 \u0623\u062a\u0645\u0643\u0646 \u0645\u0646 \u0631\u0624\u064a\u0629 \u0635\u0648\u0631\u0629 \u0627\u0644\u0643\u0627\u0645\u064a\u0631\u0627\u060c \u0623\u0648 \u0625\u0630\u0627 \u0644\u0645 \u0623\u062a\u0645\u0643\u0646 \u0645\u0646 \u0631\u0624\u064a\u0629 \u0635\u0648\u0631\u062a\u064a \u0644\u062a\u062d\u062f\u064a\u062f \u0627\u0644\u062c\u0627\u0646\u0628 \u0627\u0644\u0645\u0631\u0626\u064a\u061f", "What if I have difficulty holding my ID in position relative to the camera?": "\u0645\u0627\u0630\u0627 \u0644\u0648 \u0648\u0627\u062c\u0647\u062a \u0635\u0639\u0648\u0628\u0629 \u0641\u064a \u062a\u062b\u0628\u064a\u062a \u0628\u0637\u0627\u0642\u0629 \u0647\u0648\u064a\u062a\u064a \u0641\u064a \u0627\u0644\u0645\u0648\u0636\u0639 \u0627\u0644\u0645\u0646\u0627\u0633\u0628 \u0644\u0644\u0643\u0627\u0645\u064a\u0631\u0627\u061f", "What if I have difficulty holding my head in position relative to the camera?": "\u0645\u0627\u0630\u0627 \u0644\u0648 \u0648\u0627\u062c\u0647\u062a \u0635\u0639\u0648\u0628\u0629 \u0641\u064a \u062a\u062b\u0628\u064a\u062a \u0631\u0623\u0633\u064a \u0641\u064a \u0627\u0644\u0645\u0648\u0636\u0639 \u0627\u0644\u0645\u0646\u0627\u0633\u0628 \u0644\u0644\u0643\u0627\u0645\u064a\u0631\u0627\u061f", - "What industry do you currently work in?": "\u0645\u0627\u0647\u0648 \u0645\u062c\u0627\u0644 \u0639\u0645\u0644\u0643 \u062d\u0627\u0644\u064a\u064b\u0627\u061f", - "What industry do you want to work in?": "\u0645\u0627 \u0627\u0644\u0645\u062c\u0627\u0644 \u0627\u0644\u0630\u064a \u062a\u0631\u063a\u0628 \u0627\u0644\u0639\u0645\u0644 \u0628\u0647\u061f", - "What is the highest level of education that any of your parents or guardians have achieved?": "\u0645\u0627 \u0627\u0644\u0645\u0633\u062a\u0648\u0649 \u0627\u0644\u062a\u0639\u0644\u064a\u0645\u064a \u0627\u0644\u0623\u0639\u0644\u0649 \u0627\u0644\u0630\u064a \u062d\u0635\u0644 \u0639\u0644\u064a\u0647 \u0623\u062d\u062f \u0648\u0627\u0644\u062f\u064a\u0643 / \u0627\u0644\u0648\u0635\u064a \u0639\u0644\u064a\u0643\u061f", - "What is the highest level of education that you have achieved so far?": "\u0645\u0627 \u0627\u0644\u0645\u0633\u062a\u0648\u0649 \u0627\u0644\u062a\u0639\u0644\u064a\u0645\u064a \u0627\u0644\u0623\u0639\u0644\u0649 \u0627\u0644\u0630\u064a \u062d\u0635\u0644\u062a \u0639\u0644\u064a\u0647\u061f", - "What was the total combined income, during the last 12 months, of all members of your family? ": "\u0645\u0627 \u0645\u062c\u0645\u0648\u0639 \u0627\u0644\u062f\u062e\u0644 \u0627\u0644\u0639\u0627\u0645 \u062e\u0644\u0627\u0644 \u0627\u0644\u0627\u062b\u0646\u0627 \u0639\u0634\u0631 \u0634\u0647\u0631\u064b\u0627 \u0627\u0644\u0645\u0627\u0636\u064a\u0629 \u0644\u062c\u0645\u064a\u0639 \u0623\u0641\u0631\u0627\u062f \u0639\u0627\u0626\u0644\u062a\u0643\u061f", "What's Your Next Accomplishment?": "\u0645\u0627\u0647\u0648 \u0625\u0646\u062c\u0627\u0632\u0643 \u0627\u0644\u0642\u0627\u062f\u0645\u061f", "When learners submit an answer to an assessment, they immediately see whether the answer is correct or incorrect, and the score received.": "\u0639\u0646\u062f\u0645\u0627 \u06cc\u0642\u062f\u0645 \u0627\u0644\u0645\u062a\u0639\u0644\u0645\u0648\u0646 \u0625\u062c\u0627\u0628\u0629 \u0639\u0644\u06cc \u0646\u0645\u0648\u0630\u062c \u062a\u0642\u06cc\u06cc\u0645\u060c \u0641\u0633\u06cc\u0638\u0647\u0631 \u0645\u0627 \u0625\u0630\u0627 \u06a9\u0627\u0646 \u0627\u0644\u062c\u0648\u0627\u0628 \u0635\u062d\u06cc\u062d\u0627\u064b \u0623\u0645 \u0644\u0627\u060c \u0648\u064a\u0633\u062a\u0644\u0645\u0648\u0646 \u0627\u0644\u0646\u062a\u06cc\u062c\u0629 \u0641\u0648\u0631\u064b\u0627.", "When your face is in position, use the Take Photo button {icon} below to take your photo.": "\u0639\u0646\u062f\u0645\u0627 \u064a\u0643\u0648\u0646 \u0648\u062c\u0647\u0643 \u0641\u064a \u0645\u0648\u0636\u0639\u0647 \u060c \u0627\u0633\u062a\u062e\u062f\u0645 \u0627\u0644\u0632\u0631 \"\u0627\u0644\u062a\u0642\u0627\u0637 \u0635\u0648\u0631\u0629\" {icon} \u0623\u062f\u0646\u0627\u0647 \u0644\u0627\u0644\u062a\u0642\u0627\u0637 \u0635\u0648\u0631\u062a\u0643.", @@ -2232,7 +2210,6 @@ "Whole words": "\u0643\u0644\u0645\u0627\u062a \u0643\u0627\u0645\u0644\u0629 ", "Why activate?": "\u0644\u0645 \u0646\u0639\u062a\u0628\u0631 \u062a\u0641\u0639\u064a\u0644 \u0627\u0644\u062d\u0633\u0627\u0628 \u0636\u0631\u0648\u0631\u064a\u0627\u064b\u061f", "Why does %(platformName)s need my photo?": "\u0644\u0645\u0627\u0630\u0627 \u062a\u062d\u062a\u0627\u062c %(platformName)s \u0625\u0644\u0649 \u0635\u0648\u0631\u062a\u064a\u061f", - "Why does edX collect this information?": "\u0644\u0645\u0627\u0630\u0627 \u062a\u062c\u0645\u0639 edX \u0647\u0630\u0647 \u0627\u0644\u0645\u0639\u0644\u0648\u0645\u0627\u062a\u061f", "Width": "\u0639\u0631\u0636", "Will Be Visible To:": "\u0633\u064a\u0635\u0628\u062d \u0645\u0631\u0626\u064a\u0651 \u0645\u0646: ", "Words: {0}": "\u0627\u0644\u0643\u0644\u0645\u0627\u062a: {0}", @@ -2421,7 +2398,6 @@ "endorsed %(time_ago)s by %(user)s": "\u0645\u0635\u0627\u062f\u0642\u0629 \u0639\u0644\u064a\u0647\u0627 %(time_ago)s \u0645\u0646 \u0642\u0650\u0628\u0644 %(user)s", "enter code here": "\u0623\u062f\u062e\u0644 \u0627\u0644\u062a\u0639\u0644\u064a\u0645\u0627\u062a \u0627\u0644\u0628\u0631\u0645\u062c\u064a\u0629 \u0647\u0646\u0627", "enter link description here": "\u0623\u062f\u062e\u0644 \u0648\u0635\u0641 \u0627\u0644\u0631\u0627\u0628\u0637 \u0647\u0646\u0627", - "finish later": "\u0627\u0644\u0625\u0646\u0647\u0627\u0621 \u0644\u0627\u062d\u0642\u064b\u0627", "follow this post": "\u062a\u0627\u0628\u0650\u0639 \u0647\u0630\u0627 \u0627\u0644\u0645\u0646\u0634\u0648\u0631 ", "for": "\u0645\u0646 \u0623\u062c\u0644", "group configuration": "\u0636\u0628\u0637 \u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u0645\u062c\u0645\u0648\u0639\u0629", @@ -2436,7 +2412,6 @@ "minute": "\u062f\u0642\u064a\u0642\u0629", "minutes": "\u062f\u0642\u0627\u0626\u0642", "name": "\u0627\u0644\u0627\u0633\u0645 ", - "next page": "\u0627\u0644\u0635\u0641\u062d\u0629 \u0627\u0644\u062a\u0627\u0644\u064a\u0629", "off": "\u063a\u064a\u0631 \u0645\u0641\u0639\u0651\u0644", "on": "\u0645\u0641\u0639\u0651\u0644", "one letter Friday\u0004F": "\u062c\u0645\u0639\u0629", @@ -2468,7 +2443,6 @@ "team count": "\u0639\u062f\u062f \u0623\u0639\u0636\u0627\u0621 \u0627\u0644\u0641\u0631\u064a\u0642", "text_word_{uniqueId}": "title_word_{uniqueId}", "text_word_{uniqueId} title_word_{uniqueId}": "text_word_{uniqueId} title_word_{uniqueId}", - "the more quickly and helpfully we can respond!": "\u0628\u0623\u0633\u0631\u0639 \u0648\u0642\u062a \u0645\u0645\u0643\u0646 \u0644\u0644\u0631\u062f \u0648\u0627\u0644\u0645\u0633\u0627\u0639\u062f\u0629!", "there is currently {numVotes} vote": [ "\u0647\u0646\u0627\u0643 {numVotes} \u0635\u0648\u062a", "\u0647\u0646\u0627\u0643 {numVotes} \u0635\u0648\u062a", diff --git a/cms/static/js/i18n/de-de/djangojs.js b/cms/static/js/i18n/de-de/djangojs.js index 9cf07036ec..42ca2214e7 100644 --- a/cms/static/js/i18n/de-de/djangojs.js +++ b/cms/static/js/i18n/de-de/djangojs.js @@ -114,7 +114,6 @@ "(Caption will be displayed when you start playing the video.)": "(Beschriftung wird angezeigt, sobald das Video gestartet wird.)", "(Community TA)": "(Gemeinschaftslehrkraft)", "(Optional)": "(Optional)", - "(Read-only)": "(Nur Lesezugriff)", "(Required Field)": "(Erforderliches Feld)", "(Self-paced) Ended {end}": "(Eigenes Tempo) Ist beendet {end}", "(Self-paced) Ends {end}": "(Eigenes Tempo) Endet {end}", @@ -261,7 +260,6 @@ "An error has occurred. Wait a few minutes, and then try again.": "Ein Fehler ist aufgetreten. Warten Sie und versuchen Sie es sp\u00e4ter noch einmal.", "An error occurred retrieving your email. Please try again later, and contact technical support if the problem persists.": "Es gab einen Fehler beim Abholen deiner E-Mail. Bitte versuche es sp\u00e4ter noch einmal, und benachrichtige die technische \u00dcnterst\u00fctzung, wenn das Problem weiterhin besteht.", "An error occurred when signing you in to %s.": "Es ist ein Fehler aufgetreten als Sie sich f\u00fcr %s angemeldet haben.", - "An error occurred while attempting to retrieve or save the information below. Please try again later.": "Beim Versuch, die nachstehenden Informationen abzurufen oder zu speichern, ist ein Fehler aufgetreten. Bitte versuchen Sie es sp\u00e4ter noch einmal.", "An error occurred while looking up team membership. Try refreshing the page.": "Beim Pr\u00fcfen der Teammitgliedschaft ist ein Fehler aufgetreten. Versuchen Sie, die Seite zu aktualisieren.", "An error occurred while removing the member from the team. Try again.": "Beim entfernen des Teammitgliedes ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", "An error occurred.": "Ein Fehler ist aufgetreten.", @@ -348,7 +346,6 @@ "Bottom": "Grundlinie", "Browse": "Durchsuchen", "Browse recently launched courses and see what\\'s new in your favorite subjects": "Neue Kurse durchsuchen", - "Browsing": "Durchsuchen", "Bulk Exceptions": "Sammelausnahmen", "Bullet list": "Aufz\u00e4hlung", "Bulleted List (Ctrl+U)": "Aufz\u00e4hlende Liste (STRG+U)", @@ -524,9 +521,7 @@ "Course Name": "Kursbezeichnung", "Course Number": "Kursnummer", "Course Number Override": "Kursnummer \u00dcberschreibung", - "Course Number:": "Kursnummer:", "Course Outline": "Kurs\u00fcbersicht", - "Course Run:": "Kurslaufzeit:", "Course Start": "Kursbeginn", "Course Title": "Kurs Titel", "Course Title Override": "Kurstitel \u00fcberschreiben", @@ -538,7 +533,6 @@ "Create": "Erstellen", "Create Account": "Konto erstellen", "Create Re-run": "Erstelle Wiederholungslauf", - "Create Support Ticket": "Erstellen Sie ein Supportticket", "Create a New Team": "Ein neues Team erstellen", "Create a content group": "Erstelle eine Inhaltsgruppe", "Create a new team if you can't find an existing team to join, or if you would like to learn with friends you know.": "Erstelle ein neues Team, wenn Du keinem bestehenden Team beitreten kannst, oder wenn Du mit Freunden lernen m\u00f6chtest, die Du kennst.", @@ -608,7 +602,6 @@ "Describe ": "Beschreibe", "Description": "Beschreibung", "Description of the certificate": "Beschreibung des Zertifikats", - "Details": "Details", "Device with Camera": "Ger\u00e4t mit Kamera", "Dimensions": "Dimensionen", "Disc": "Scheibe", @@ -817,7 +810,6 @@ "Find next": "Finde n\u00e4chstes", "Find previous": "Finde vorheriges", "Finish": "Beende", - "Finish later": "Sp\u00e4ter beenden", "First time here?": "Zum ersten Mal hier?", "Follow": "Folgen", "Follow or unfollow posts": "Beitr\u00e4gen folgen / entfolgen", @@ -869,7 +861,6 @@ "H Align": "H Ausrichtung", "HTML preview of post": "HTML Vorschau des Beitrags", "HTML source code": "HTML-Quellcode", - "Have you ever served on active duty in the U.S. Armed Forces, Reserves, or National Guard?": "Haben Sie jemals im aktiven Dienst in den US-Streitkr\u00e4ften, Reserven oder der Nationalgarde gedient?", "Header": "Kopfzeile", "Header 1": "Kopfzeile 1", "Header 2": "Kopfzeile 2", @@ -1068,7 +1059,6 @@ "Loading more threads": "Lade weitere Diskussionsstr\u00e4nge", "Loading posts list": "Lade Beitrags\u00fcbersicht", "Loading your courses": "Lade ihren Kurs", - "Loading...": "Lade...", "Location": "Ort", "Location in Course": "Position im Kurs", "Lock this asset": "Objekt sperren", @@ -1129,7 +1119,6 @@ "Name of the signatory": "Name des Unterzeichners", "Name or short description of the configuration": "Name oder Kurzbeschreibung des Aufbaus", "Navigate up": "Aufw\u00e4rts navigieren", - "Need help logging in?": "Brauchen Sie Hilfe beim einloggen?", "Need help signing in?": "Brauchen Sie Hilfe bei der Anmeldung?", "Need other help signing in?": "Brauchen Sie weitere Hilfe bei der Anmeldung?", "Needs verified certificate ": "Ben\u00f6tigt ein verifiziertes Zertifikat", @@ -1233,7 +1222,6 @@ "Organization ": "Organsation", "Organization Name": "Name der Organisation", "Organization of the signatory": "Organisation des Unterzeichners", - "Organization:": "Organisation:", "Other": "Anderer Abschluss", "Other sign-in issues": "Andere Probleme beim Anmelden", "Overall Score": "Gesamtpunkte", @@ -1372,7 +1360,6 @@ "Questions raise issues that need answers. Discussions share ideas and start conversations. (Required)": "Fragen ben\u00f6tigen Antworten. Diskussionen l\u00f6sen eine Konversation aus. (Erforderlich)", "Queued": "Eingereiht", "REMAINING COURSES": "VERBLEIBENDE KURSE", - "Re-run Course": "Neue Kurslaufzeit", "Read More": "Weiterlesen", "Read more": "Mehr erfahren", "Ready To Start": "Bereit um zu starten", @@ -1445,7 +1432,6 @@ "Return to Export": "Zum Export zur\u00fcckkehren", "Return to Your Dashboard": "Zur\u00fcck zu \"Meine Kurse\"", "Return to all posts": "Zu allen Beitr\u00e4gen", - "Return to my dashboard": "Zur\u00fcck zu meinem Dashboard", "Return to team listing": "Zur\u00fcck zur Teamliste", "Review Policy Exception": "Ausnahme bei der Begutachtungsrichtlinine", "Review Rules": "\u00dcberpr\u00fcfungsregeln", @@ -1496,14 +1482,12 @@ "Select a subject for your support request.": "W\u00e4hlen Sie ein Betreff f\u00fcr Ihre Supportanfrage.", "Select a time allotment for the exam. If it is over 24 hours, type in the amount of time. You can grant individual learners extra time to complete the exam through the Instructor Dashboard.": "W\u00e4hlen Sie eine Zeitzuteilung f\u00fcr die Pr\u00fcfung aus. Wenn es mehr als 24 Stunden sind, geben Sie die Zeitspanne ein. Sie k\u00f6nnen einzelnen Lernenden \u00fcber das Lehrer-Dashboard zus\u00e4tzliche Zeit geben, um die Pr\u00fcfung abzuschlie\u00dfen.", "Select all": "Alles ausw\u00e4hlen", - "Select employment status": "W\u00e4hlen Sie Ihren Besch\u00e4ftigungsstatus", "Select fidelity": "Treue ausw\u00e4hlen", "Select language": "W\u00e4hlen Sie eine Sprache", "Select one or more groups:": "W\u00e4hlen Sie eine oder mehrere Gruppen aus:", "Select the course-wide discussion topics that you want to divide.": "W\u00e4hlen Sie die kursweiten Diskussionsthemen aus, die Sie aufteilen m\u00f6chten.", "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browser's local time zone.": "W\u00e4hlen Sie die Zeitzone des angebotenen Kurses aus. Wenn Sie keine bestimmte Zeitzone ausw\u00e4hlen, werden Kursdaten, inklusive Abgabetermine f\u00fcr Aufgaben, in der lokalen Zeit ihres Browsers dargestellt.", "Select turnaround": "Wendepunkt ausw\u00e4hlen", - "Selected blocks": "Ausgew\u00e4hlte Bl\u00f6cke", "Selected tab": "Ausgew\u00e4hlter Tab", "Self": "Selbst", "Send to:": "Senden an:", @@ -2067,7 +2051,7 @@ "View Cohort": "Kohort ansehen", "View Course": "Anzeigen", "View Current Team Memberships": "Aktuelle Team-Mitgliedschaft sehen ", - "View Live": "Live betrachten", + "View Live": "Seite ansehen", "View Program Record": "Programmprotokoll einsehen", "View Report": "Sehen Sie den Bericht hier", "View Teams in the {topic_name} Topic": "Teams im {topic_name} ansehen.", @@ -2112,17 +2096,11 @@ "Weight of Total Grade": "Gewichtung der Gesamtnote", "We\u2019re sorry to see you go!": "Es tut uns leid, dass Sie Ihren Account l\u00f6schen m\u00f6chten!", "What You Need for Verification": "Was du zur Verifizierung ben\u00f6tigst", - "What can we help you with, {username}?": "Wie k\u00f6nnen wir Ihnen helfen, {username}?", "What does %(platformName)s do with this photo?": "Was macht %(platformName)s mit dieses Foto?", "What does this mean?": "Was soll dies bedeuten?", "What if I can't see the camera image, or if I can't see my photo do determine which side is visible?": "Was, wenn ich mein Kamerabild oder mein Foto nicht sehen kann?", "What if I have difficulty holding my ID in position relative to the camera?": "Was, wenn ich Schwierigkeiten habe, meinen Ausweis in der richtigen Position zur Kamera zu halten?", "What if I have difficulty holding my head in position relative to the camera?": "Was, wenn ich Schwierigkeiten habe, meinen Kopf in der richtigen Position zur Kamera zu halten?", - "What industry do you currently work in?": "In welcher Branche arbeiten Sie derzeit?", - "What industry do you want to work in?": "In welcher Branchen wollen Sie arbeiten?", - "What is the highest level of education that any of your parents or guardians have achieved?": "Welches ist der h\u00f6chste Bildungsabschluss, den ein Elternteil oder Erziehungsberechtigter von Ihnen erreicht hat?", - "What is the highest level of education that you have achieved so far?": "Was ist der h\u00f6chste Bildungsabschluss, den Sie erreicht haben?", - "What was the total combined income, during the last 12 months, of all members of your family? ": "Wie hoch war das gemeinsame Gesamteinkommen aller Mitglieder Ihrer Familie in den letzten 12 Monaten? ", "What's Your Next Accomplishment?": "Was ist Ihr n\u00e4chster Erfolg?", "When learners submit an answer to an assessment, they immediately see whether the answer is correct or incorrect, and the score received.": "Nutzer sehen sofort nach einreichen ihrer Antworten, ob diese richtig oder falsch sind und welche Punkte sie erhalten.", "When your face is in position, use the Take Photo button {icon} below to take your photo.": "Wenn Ihr Gesicht in Position ist, klicken Sie auf die Schaltfl\u00e4che des Ausl\u00f6sers {icon} unten, um ein Foto aufzunehmen.", @@ -2131,7 +2109,6 @@ "Whole words": "Ganze Worte", "Why activate?": "Wieso aktivieren?", "Why does %(platformName)s need my photo?": "Wieso braucht %(platformName)s mein Foto?", - "Why does edX collect this information?": "Warum braucht edX diese Information?", "Width": "Breite", "Will Be Visible To:": "Wird sichtbar sein f\u00fcr:", "Words: {0}": "W\u00f6rter: {0}", @@ -2315,7 +2292,6 @@ "endorsed %(time_ago)s by %(user)s": "bef\u00fcrwortet %(time_ago)s von %(user)s", "enter code here": "Code hier eingeben", "enter link description here": "Linkbeschreibung hier eingeben", - "finish later": "Sp\u00e4ter beenden", "follow this post": "Beitrag folgen", "for": "f\u00fcr", "group configuration": "Gruppenkonfiguration", @@ -2329,7 +2305,6 @@ "minute": "Minute", "minutes": "Minuten", "name": "name", - "next page": "n\u00e4chste Seite", "off": "Aus", "on": "An", "one letter Friday\u0004F": "Fr", @@ -2361,7 +2336,6 @@ "team count": "Anzahl an Teams", "text_word_{uniqueId}": "text_word_{uniqueId}", "text_word_{uniqueId} title_word_{uniqueId}": "text_word_{uniqueId} title_word_{uniqueId}", - "the more quickly and helpfully we can respond!": "desto schneller und gezielter k\u00f6nnen wir Ihnen antworten!", "there is currently {numVotes} vote": [ "aktuell gibt es {numVotes} Stimme", "aktuell gibt es {numVotes} Stimmen" diff --git a/cms/static/js/i18n/el/djangojs.js b/cms/static/js/i18n/el/djangojs.js index 0290f431d5..c0e07ffc46 100644 --- a/cms/static/js/i18n/el/djangojs.js +++ b/cms/static/js/i18n/el/djangojs.js @@ -202,7 +202,6 @@ "Midnight": "\u039c\u03b5\u03c3\u03ac\u03bd\u03c5\u03c7\u03c4\u03b1", "More": "\u03a0\u03b5\u03c1\u03b9\u03c3\u03c3\u03cc\u03c4\u03b5\u03c1\u03b1", "Name": "\u038c\u03bd\u03bf\u03bc\u03b1", - "Need help logging in?": "\u039e\u03b5\u03c7\u03ac\u03c3\u03b1\u03c4\u03b5 \u03c4\u03bf\u03bd \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc \u03c3\u03b1\u03c2;", "Need help signing in?": "\u03a7\u03c1\u03b5\u03b9\u03ac\u03b6\u03b5\u03c3\u03c4\u03b5 \u03b2\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1;", "New Password": "\u039d\u03ad\u03bf\u03c2 \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2", "No description available": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b4\u03b9\u03b1\u03b8\u03ad\u03c3\u03b9\u03bc\u03b7 \u03c0\u03b5\u03c1\u03b9\u03b3\u03c1\u03b1\u03c6\u03ae", @@ -370,7 +369,6 @@ "Verified Certificate": "\u03a0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03b9\u03b7\u03c4\u03b9\u03ba\u03cc \u03b5\u03c0\u03b1\u03bb\u03b7\u03b8\u03b5\u03c5\u03bc\u03ad\u03bd\u03bf", "View": "\u03a0\u03c1\u03bf\u03b2\u03bf\u03bb\u03ae", "View Course": "\u0394\u0395\u0399\u03a4\u0395 \u03a4\u039f \u039c\u0391\u0398\u0397\u039c\u0391", - "View Live": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03c5\u03b8\u03ae\u03c3\u03c4\u03b5 \u03b6\u03c9\u03bd\u03c4\u03b1\u03bd\u03ac", "Vote for good posts and responses": "\u03a5\u03c0\u03b5\u03c1\u03c8\u03b7\u03c6\u03af\u03c3\u03c4\u03b5 \u03c4\u03b9\u03c2 \u03ba\u03b1\u03bb\u03ad\u03c2 \u03b1\u03bd\u03b1\u03c1\u03c4\u03ae\u03c3\u03b5\u03b9\u03c2 \u03ba\u03b1\u03b9 \u03b1\u03c0\u03bf\u03ba\u03c1\u03af\u03c3\u03b5\u03b9\u03c2", "Warning": "\u03a0\u03c1\u03bf\u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7", "We couldn't create your account.": "\u0394\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03ad\u03c3\u03b1\u03bc\u03b5 \u03bd\u03b1 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03c3\u03bf\u03c5\u03bc\u03b5 \u03c4\u03bf\u03bd \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc \u03c3\u03b1\u03c2", diff --git a/cms/static/js/i18n/eo/djangojs.js b/cms/static/js/i18n/eo/djangojs.js index 404d096061..6f35d08ce8 100644 --- a/cms/static/js/i18n/eo/djangojs.js +++ b/cms/static/js/i18n/eo/djangojs.js @@ -434,6 +434,7 @@ "Code block": "\u00c7\u00f6d\u00e9 \u00dfl\u00f6\u00e7k \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3#", "Cohort Assignment Method": "\u00c7\u00f6h\u00f6rt \u00c0ss\u00efgnm\u00e9nt M\u00e9th\u00f6d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7#", "Cohort Name": "\u00c7\u00f6h\u00f6rt N\u00e4m\u00e9 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f #", + "Cohort assignment not allowed: {email_or_username}": "\u00c7\u00f6h\u00f6rt \u00e4ss\u00efgnm\u00e9nt n\u00f6t \u00e4ll\u00f6w\u00e9d: {email_or_username} \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454\u0442#", "Cohorts": "\u00c7\u00f6h\u00f6rts \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c #", "Cohorts Disabled": "\u00c7\u00f6h\u00f6rts D\u00efs\u00e4\u00dfl\u00e9d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c#", "Cohorts Enabled": "\u00c7\u00f6h\u00f6rts \u00c9n\u00e4\u00dfl\u00e9d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1#", @@ -622,9 +623,7 @@ "Download Memberships": "D\u00f6wnl\u00f6\u00e4d M\u00e9m\u00df\u00e9rsh\u00efps \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, #", "Download Software Clicked": "D\u00f6wnl\u00f6\u00e4d S\u00f6ftw\u00e4r\u00e9 \u00c7l\u00ef\u00e7k\u00e9d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455#", "Download Transcript for Editing": "D\u00f6wnl\u00f6\u00e4d Tr\u00e4ns\u00e7r\u00efpt f\u00f6r \u00c9d\u00eft\u00efng \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442#", - "Download URL": "D\u00f6wnl\u00f6\u00e4d \u00dbRL \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455#", "Download available encodings (.csv)": "D\u00f6wnl\u00f6\u00e4d \u00e4v\u00e4\u00efl\u00e4\u00dfl\u00e9 \u00e9n\u00e7\u00f6d\u00efngs (.\u00e7sv) \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454\u0442#", - "Download the user's certificate": "D\u00f6wnl\u00f6\u00e4d th\u00e9 \u00fcs\u00e9r's \u00e7\u00e9rt\u00eff\u00ef\u00e7\u00e4t\u00e9 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442#", "Draft (Never published)": "Dr\u00e4ft (N\u00e9v\u00e9r p\u00fc\u00dfl\u00efsh\u00e9d) \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3#", "Draft (Unpublished changes)": "Dr\u00e4ft (\u00dbnp\u00fc\u00dfl\u00efsh\u00e9d \u00e7h\u00e4ng\u00e9s) \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454#", "Draft saved on {lastSavedStart}{editedOn}{lastSavedEnd} by {editedByStart}{editedBy}{editedByEnd}": "Dr\u00e4ft s\u00e4v\u00e9d \u00f6n {lastSavedStart}{editedOn}{lastSavedEnd} \u00df\u00fd {editedByStart}{editedBy}{editedByEnd} \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454\u0442\u03c5#", @@ -1173,7 +1172,6 @@ "Not Started": "N\u00f6t St\u00e4rt\u00e9d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f #", "Not Supported": "N\u00f6t S\u00fcpp\u00f6rt\u00e9d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9#", "Not able to set passing grade to less than %(minimum_grade_cutoff)s%.": "N\u00f6t \u00e4\u00dfl\u00e9 t\u00f6 s\u00e9t p\u00e4ss\u00efng gr\u00e4d\u00e9 t\u00f6 l\u00e9ss th\u00e4n %(minimum_grade_cutoff)s%. \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9\u0442 \u03b1\u043c\u0454\u0442, \u00a2\u03c3\u03b7\u0455\u0454\u00a2\u0442\u0454\u0442\u03c5\u044f \u03b1#", - "Not available": "N\u00f6t \u00e4v\u00e4\u00efl\u00e4\u00dfl\u00e9 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455\u03b9#", "Not divided": "N\u00f6t d\u00efv\u00efd\u00e9d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f #", "Not in Use": "N\u00f6t \u00efn \u00dbs\u00e9 \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3#", "Not selected": "N\u00f6t s\u00e9l\u00e9\u00e7t\u00e9d \u2c60'\u03c3\u044f\u0454\u043c \u03b9\u03c1\u0455\u03c5\u043c \u2202\u03c3\u0142\u03c3\u044f \u0455#", diff --git a/cms/static/js/i18n/es-419/djangojs.js b/cms/static/js/i18n/es-419/djangojs.js index 9733607a8e..4fd6f115bd 100644 --- a/cms/static/js/i18n/es-419/djangojs.js +++ b/cms/static/js/i18n/es-419/djangojs.js @@ -224,7 +224,6 @@ "(Caption will be displayed when you start playing the video.)": "(Los subt\u00edtulos ser\u00e1n mostrados al iniciar la reproducci\u00f3n del video.)", "(Community TA)": "(Profesor ayudante de la comunidad)", "(Optional)": "(Opcional)", - "(Read-only)": "(Solo-lectura)", "(Required Field)": "(Campo requerido)", "(Self-paced) Ended {end}": "(A ritmo propio) Terminado {end}", "(Self-paced) Ends {end}": "(A ritmo propio)Termina{end}", @@ -375,7 +374,6 @@ "An error has occurred. Wait a few minutes, and then try again.": "Un error ha ocurrido. Espere unos pocos minutos e intenta de nuevo.", "An error occurred retrieving your email. Please try again later, and contact technical support if the problem persists.": "Ocurri\u00f3 un error al recuperar tu correo. Por favor intenta nuevamente m\u00e1s tarde. Si el problema persiste, contacta a soporte t\u00e9cnico.", "An error occurred when signing you in to %s.": "Ocurri\u00f3 un error al iniciar tu sesi\u00f3n en %s.", - "An error occurred while attempting to retrieve or save the information below. Please try again later.": "Se produjo un error al intentar recuperar recuperar o guardar la informaci\u00f3n a continuaci\u00f3n. Por favor intenta de nuevo mas tarde. ", "An error occurred while looking up team membership. Try refreshing the page.": "Ocurrio un error al buscar la membresia del equipo. Intenta refrescar la pagina.", "An error occurred while removing the member from the team. Try again.": "Ocurri\u00f3 un error al remover al miembro del equipo. Intenta nuevamente. ", "An error occurred.": "Ha ocurrido un error.", @@ -463,7 +461,6 @@ "Bottom": "Base", "Browse": "Explorar", "Browse recently launched courses and see what\\'s new in your favorite subjects": "Echa un vistazo a nuestros cursos lanzados recientemente y qu\u00e9 hay de nuevo en tus temas favoritos.", - "Browsing": "Explorando", "Bulk Exceptions": "Excepciones en lote", "Bullet list": "Lista con vi\u00f1etas", "Bulleted List (Ctrl+U)": "Vi\u00f1etas (Ctrl+U)", @@ -650,9 +647,7 @@ "Course Name": "T\u00edtulo del curso", "Course Number": "C\u00f3digo del curso", "Course Number Override": "Reemplazo para el n\u00famero de curso", - "Course Number:": "N\u00famero del curso:", "Course Outline": "Estructura del curso", - "Course Run:": "Edici\u00f3n del Curso:", "Course Start": "Inicio del Curso", "Course Title": "T\u00edtulo del curso", "Course Title Override": "Reemplazo para el t\u00edtulo del curso", @@ -664,7 +659,6 @@ "Create": "Crear", "Create Account": "Crear cuenta", "Create Re-run": "Crear reapertura", - "Create Support Ticket": "Crear caso de soporte", "Create a New Team": "Crear un nuevo equipo", "Create a content group": "Crear contenido de grupo", "Create a new team if you can't find an existing team to join, or if you would like to learn with friends you know.": "Crea un nuevo equipo si no puedes encontrar uno existente para unirte o si deseas aprender con personas que ya conoces.", @@ -734,7 +728,6 @@ "Describe ": "Describir", "Description": "Descripci\u00f3n", "Description of the certificate": "Descripci\u00f3n del certificado", - "Details": "Detalles", "Device with Camera": "Dispositivo con c\u00e1mara", "Dimensions": "Dimensiones", "Disc": "Disco", @@ -952,7 +945,6 @@ "Find next": "Encontrar el siguiente", "Find previous": "Encontrar el anterior", "Finish": "Finalizar", - "Finish later": "Terminar despues", "First time here?": "Primera vez aqu\u00ed?", "Follow": "Seguir", "Follow or unfollow posts": "Seguir o dejar de seguir publicaciones", @@ -1007,7 +999,6 @@ "HTML preview of post": "previsiualizaci\u00f3n en HTML", "HTML source code": "C\u00f3digo fuente HTML", "Have a computer with a functioning webcam": "Contar con un computador con c\u00e1mara web funcionando ", - "Have you ever served on active duty in the U.S. Armed Forces, Reserves, or National Guard?": "\u00bfAlguna vez has servido en un deber activo en las Fuerzas Armadas de Estados Unidos de America, Reservas, o Guardia Nacional? ", "Have your valid photo ID (e.g. driver's license or passport) ready": "Tener una foto de identificaci\u00f3n valida (ejemplo: licencia de conducci\u00f3n o pasaporte) a la mano", "Header": "Encabezado", "Header 1": "Encabezado 1", @@ -1213,7 +1204,6 @@ "Loading more threads": "Cargando m\u00e1s discusiones", "Loading posts list": "Cargando lista de publicaci\u00f3nes", "Loading your courses": "Cargando tus cursos", - "Loading...": "Cargando...", "Location": "Ubicaci\u00f3n", "Location in Course": "Ubicaci\u00f3n en el curso", "Lock this asset": "Bloquear este recurso", @@ -1277,7 +1267,6 @@ "Name or short description of the configuration": "Nombre o descripci\u00f3n corta de la configuraci\u00f3n", "Navigate to onboarding exam": "Navega hacia el examen de inducci\u00f3n", "Navigate up": "Navegue hacia arriba", - "Need help logging in?": "\u00bfNecesita ayuda para iniciar sesi\u00f3n?", "Need help signing in?": "\u00bfNecesita ayuda para iniciar sesi\u00f3n?", "Need other help signing in?": "\u00bfNecesita otra ayuda para iniciar sesi\u00f3n?", "Needs verified certificate ": "Necesita certificado verificado", @@ -1393,7 +1382,6 @@ "Organization ": "Organizaci\u00f3n", "Organization Name": "Nombre de la organizaci\u00f3n", "Organization of the signatory": "Organizaci\u00f3n del signatario", - "Organization:": "Organizaci\u00f3n:", "Other": "Otros", "Other sign-in issues": "Otros problemas para iniciar sesi\u00f3n", "Overall Score": "Puntaje general", @@ -1540,7 +1528,6 @@ "Questions raise issues that need answers. Discussions share ideas and start conversations. (Required)": "Utiliza Pregunta para plantear temas que necesitan respuestas. Utiliza Discusi\u00f3n para compartir tus ideas y comenzar conversaciones. (Requerido)", "Queued": "En cola", "REMAINING COURSES": "CURSOS RESTANTES", - "Re-run Course": "Relanzar Curso", "Read More": "Leer mas", "Read more": "Leer m\u00e1s", "Ready To Start": "Listo para comenzar", @@ -1618,7 +1605,6 @@ "Return to Export": "Regresar a exportar", "Return to Your Dashboard": "Volver al panel principal", "Return to all posts": "Volver a todas las publicaciones", - "Return to my dashboard": "Volver a mi panel de control", "Return to team listing": "Volver a la lista del equipo", "Review Policy Exception": "Revisar excepci\u00f3n a la pol\u00edtica", "Review Rules": "Revisar las reglas", @@ -1671,14 +1657,12 @@ "Select a subject for your support request.": "Elige un tema para tu petici\u00f3n de ayuda.", "Select a time allotment for the exam. If it is over 24 hours, type in the amount of time. You can grant individual learners extra time to complete the exam through the Instructor Dashboard.": "Seleccione un tiempo disponible para el examen. Si es mayor a 24 horas, escriba la cantidad de tiempo. Puede otorgar a estudiantes individuales un tiempo extra para completar el examen a trav\u00e9s del panel de control de instructor.", "Select all": "Selecionar todo", - "Select employment status": "Seleccionar estatus laboral", "Select fidelity": "Seleccionar fidelidad", "Select language": "Seleccionar lenguaje", "Select one or more groups:": "Selecciones uno o mas grupos:", "Select the course-wide discussion topics that you want to divide.": "Seleccione los temas de discusi\u00f3n del curso que desea dividir.", "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browser's local time zone.": "Selecciona la zona horaria para exponer las fechas del curso. Si no especificas una zona horaria, las fechas del curso, incluidas las fechas l\u00edmites de tareas, ser\u00e1n expuestas en la zona horaria local de tu navegador.", "Select turnaround": "Seleccione tiempo de entrega.", - "Selected blocks": "Bloques seleccionados", "Selected tab": "Opci\u00f3n seleccionada", "Self": "Auto", "Send to:": "Enviar a:", @@ -2314,17 +2298,11 @@ "Weight of Total Grade": "Peso en la calificaci\u00f3n total", "We\u2019re sorry to see you go!": "\u00a1Sentimos que te vayas!", "What You Need for Verification": "Lo que necesita para la verificaci\u00f3n", - "What can we help you with, {username}?": "\u00bfEn qu\u00e9 podemos ayudarte, {username}?", "What does %(platformName)s do with this photo?": "\u00bfQu\u00e9 hace %(platformName)s con esta imagen?", "What does this mean?": "\u00bfQu\u00e9 significa esto?", "What if I can't see the camera image, or if I can't see my photo do determine which side is visible?": "\u00bfQu\u00e9 sucede si no puedo ver la imagen de la c\u00e1mara, o si no puedo ver mi foto para determinar qu\u00e9 lado es visible?", "What if I have difficulty holding my ID in position relative to the camera?": "\u00bfQu\u00e9 sucede si tengo dificultades para mantener mi identificaci\u00f3n en posici\u00f3n con respecto a la c\u00e1mara?", "What if I have difficulty holding my head in position relative to the camera?": "\u00bfQu\u00e9 sucede si tengo dificultades para mantener la cabeza en posici\u00f3n con respecto a la c\u00e1mara?", - "What industry do you currently work in?": "\u00bfEn que industria trabajas actualmente?", - "What industry do you want to work in?": "\u00bfEn que industria te gustar\u00eda trabajar?", - "What is the highest level of education that any of your parents or guardians have achieved?": "\u00bfCual es el nivel de educaci\u00f3n mas alto que alguno de tus padres o responsables ha adquirido? ", - "What is the highest level of education that you have achieved so far?": "\u00bfCual es el nivel mas alto de educaci\u00f3n que has obtenido hasta la fecha?", - "What was the total combined income, during the last 12 months, of all members of your family? ": "\u00bfCual fue el ingreso total combinado, durante los ultimo 12 meses, de todos los miembros de tu familia? ", "What's Your Next Accomplishment?": "\u00bfQu\u00e9 ser\u00e1 tu pr\u00f3ximo logro?", "When learners submit an answer to an assessment, they immediately see whether the answer is correct or incorrect, and the score received.": "Cuando un estudiante ya haya enviado su respuesta en una evaluaci\u00f3n, en seguida ver\u00e1 si esa respuesta es correcta o incorrecta y la puntuaci\u00f3n correspondiente.", "When your face is in position, use the Take Photo button {icon} below to take your photo.": "Cuando su cara est\u00e9 en posici\u00f3n, use el bot\u00f3n Tomar foto {icon} a continuaci\u00f3n para tomar su foto.", @@ -2333,7 +2311,6 @@ "Whole words": "Palabras completas", "Why activate?": "Por qu\u00e9 activar?", "Why does %(platformName)s need my photo?": "Por qu\u00e9 %(platformName)s necesita mi foto ?", - "Why does edX collect this information?": "\u00bfPor qu\u00e9 edX recoleta esta informaci\u00f3n?", "Width": "Anchura", "Will Be Visible To:": "Ser\u00e1 visible para:", "Words: {0}": "Palabras: {0}", @@ -2538,7 +2515,6 @@ "endorsed %(time_ago)s by %(user)s": "Validado hace %(time_ago)s por %(user)s", "enter code here": "ingresa el c\u00f3digo aqu\u00ed", "enter link description here": "Ingresa la descripci\u00f3n del v\u00ednculo aqu\u00ed", - "finish later": "terminar despues", "follow this post": "sigue esta publicaci\u00f3n", "for": "para", "group configuration": "Configuraci\u00f3n de Grupo", @@ -2553,7 +2529,6 @@ "minute": "minuto", "minutes": "minutos", "name": "nombre", - "next page": "Siguiente p\u00e1gina", "off": "Apagar", "on": "Encender", "one letter Friday\u0004F": "V", @@ -2585,7 +2560,6 @@ "team count": "Cantidad de equipos", "text_word_{uniqueId}": "text_word_{uniqueId}", "text_word_{uniqueId} title_word_{uniqueId}": "text_word_{uniqueId} title_word_{uniqueId}", - "the more quickly and helpfully we can respond!": "Cuantos m\u00e1s detalles, m\u00e1s r\u00e1pido y mejor podremos responder", "there is currently {numVotes} vote": [ "actualmente hay {numVotes} voto", "actualmente hay {numVotes} votos", diff --git a/cms/static/js/i18n/eu-es/djangojs.js b/cms/static/js/i18n/eu-es/djangojs.js index bd36c35cd1..0093df513b 100644 --- a/cms/static/js/i18n/eu-es/djangojs.js +++ b/cms/static/js/i18n/eu-es/djangojs.js @@ -68,7 +68,6 @@ "(Caption will be displayed when you start playing the video.)": "(Azpitituluak erakutsiko dira bideoa ikusten hasten zarenean.)", "(Community TA)": "(Eztabaida-laguntzailea)", "(Optional)": "(Aukerakoa)", - "(Read-only)": "(Irakurtzekoa baino ez)", "(Required Field)": "(Beharrezko eremua)", "(Staff)": "(Arduradunak)", "(optional)": "(aukerakoa)", @@ -349,9 +348,7 @@ "Course Name": "Ikastaroaren izena", "Course Number": "Ikastaro-zenbakia", "Course Number Override": "Aldatu Ikastaro-zenbakia", - "Course Number:": "Ikastaro-zenbakia", "Course Outline": "Ikastaroaren egitura", - "Course Run:": "Ikastaroa ematea:", "Course Start": "Ikastaroaren hasiera", "Course Title": "Ikastaroaren izenburua", "Course Title Override": "Aldatu ikastaroaren izenburua", @@ -412,7 +409,6 @@ "Describe ": "Deskribatu", "Description": "Deskribapena", "Description of the certificate": "Ziurtagiriaren deskribapena", - "Details": "Xehetasunak", "Dimensions": "Neurriak", "Disc": "Diskoa", "Discard Changes": "Utzi aldaketak", @@ -752,7 +748,6 @@ "Name of the signatory": "Sinatzailearen izena", "Name or short description of the configuration": "Ezarpenen izena edo deskribapen laburra", "Navigate up": "Joan gora", - "Need help logging in?": "Laguntza behar duzu saioa hasteko?", "New %(item_type)s": "%(item_type)s berria", "New Address": "Helbide berria", "New Password": "Pasahitz berria", @@ -825,7 +820,6 @@ "Organization ": "Erakundea", "Organization Name": "Erakundearen izena", "Organization of the signatory": "Sinatzailearen erakundea", - "Organization:": "Erakundea:", "Other": "Bestelako bat", "Overall Score": "Puntuazio orokorra", "Page break": "Orri-jauzia", @@ -894,7 +888,6 @@ "Question": "Galdera", "Queued": "Ilaran", "REMAINING COURSES": "GERATZEN DIREN IKASTAROAK", - "Re-run Course": "Errepikatu ikastaroa", "Read More": "Irakurri gehiago", "Read more": "Irakurri gehiago", "Ready To Start": "Hasteko prest", @@ -1328,7 +1321,6 @@ "We've encountered an error. Refresh your browser and then try again.": "Errore bat aurkitu dugu. Frekatu zure nabigatzailea eta saiatu berriz.", "Web:": "Web:", "Webcam": "Web-kamera", - "What can we help you with, {username}?": "Zertan lagundu ahal dizugu, {username}?", "What does this mean?": "Zer esan nahi du honek?", "What's Your Next Accomplishment?": "Zein izango da hurrengo lorpena?", "Whole words": "Hitz osoak", @@ -1446,7 +1438,6 @@ "minute": "minutu", "minutes": "minutu", "name": "izena", - "next page": "hurrengo orria", "off": "itzalita", "on": "piztuta", "one letter Friday\u0004F": "O", diff --git a/cms/static/js/i18n/fr/djangojs.js b/cms/static/js/i18n/fr/djangojs.js index 254b689959..dbba28e4d7 100644 --- a/cms/static/js/i18n/fr/djangojs.js +++ b/cms/static/js/i18n/fr/djangojs.js @@ -224,7 +224,6 @@ "(Caption will be displayed when you start playing the video.)": "(Les sous-titres s'afficheront quand vous lancerez la vid\u00e9o.)", "(Community TA)": "(Assistant technique)", "(Optional)": "(Optionnel)", - "(Read-only)": "(Lecture-seule)", "(Required Field)": "(Champ requis)", "(Self-paced) Ended {end}": "(\u00c0 votre rythme) Termin\u00e9 {end}", "(Self-paced) Ends {end}": "(\u00c0 votre rythme) Se finit \u00e0 {end}", @@ -375,7 +374,6 @@ "An error has occurred. Wait a few minutes, and then try again.": "Une erreur est survenue. Attendez quelques minutes avant de r\u00e9essayer.", "An error occurred retrieving your email. Please try again later, and contact technical support if the problem persists.": "Une erreur est survenue lors de la r\u00e9cup\u00e9ration de votre courriel. S'il vous pla\u00eet r\u00e9essayez plus tard, et contactez le support technique si le probl\u00e8me persiste.", "An error occurred when signing you in to %s.": "Une erreur s'est produite en vous connectant \u00e0 %s.", - "An error occurred while attempting to retrieve or save the information below. Please try again later.": "Une erreur s'est produite lors de la tentative de r\u00e9cup\u00e9ration ou d'enregistrement des informations ci-dessous. Veuillez r\u00e9essayer plus tard.", "An error occurred while looking up team membership. Try refreshing the page.": "Une erreur s'est produite lors de la recherche des membres de l'\u00e9quipe. Essayez d'actualiser la page.", "An error occurred while removing the member from the team. Try again.": "Une erreur est survenue lors de la suppression du membre de l'\u00e9quipe. R\u00e9essayez.", "An error occurred.": "Une erreur est survenue.", @@ -463,7 +461,6 @@ "Bottom": "Bas", "Browse": "Parcourir", "Browse recently launched courses and see what\\'s new in your favorite subjects": "Parcourez les plus r\u00e9cents cours et voyez ce qui existe de nouveau dans vos sujets favoris", - "Browsing": "Navigation", "Bulk Exceptions": "D\u00e9rogations en lot", "Bullet list": "Liste \u00e0 puces", "Bulleted List (Ctrl+U)": "List \u00e0 Puces (Ctrl+U)", @@ -650,9 +647,7 @@ "Course Name": "Nom du Cours", "Course Number": "Num\u00e9ro du cours", "Course Number Override": "Ecrasement du Num\u00e9ro du Cours", - "Course Number:": "Num\u00e9ro du cours :", "Course Outline": "Plan du Cours", - "Course Run:": "Session :", "Course Start": "D\u00e9but du Cours", "Course Title": "Titre du cours", "Course Title Override": "Surcharge du Titre du Cours", @@ -664,7 +659,6 @@ "Create": "Cr\u00e9er", "Create Account": "Cr\u00e9er un compte", "Create Re-run": "Cr\u00e9er une relance", - "Create Support Ticket": "Cr\u00e9er un billet de support", "Create a New Team": "Cr\u00e9er une nouvelle \u00e9quipe", "Create a content group": "Cr\u00e9er un groupe de contenu", "Create a new team if you can't find an existing team to join, or if you would like to learn with friends you know.": "Cr\u00e9ez une nouvelle \u00e9quipe si vous ne trouvez pas une \u00e9quipe existante \u00e0 rejoindre, ou si vous d\u00e9sirez apprendre avec des amis que vous connaissez d\u00e9j\u00e0.", @@ -734,7 +728,6 @@ "Describe ": "D\u00e9crire", "Description": "Description", "Description of the certificate": "Description du certificat", - "Details": "D\u00e9tails", "Device with Camera": "Appareil avec cam\u00e9ra", "Dimensions": "Dimensions", "Disc": "Disque", @@ -952,7 +945,6 @@ "Find next": "Trouver le suivant", "Find previous": "Trouver le pr\u00e9c\u00e9dent", "Finish": "Terminer", - "Finish later": "Terminer plus tard", "First time here?": "C'est votre premi\u00e8re visite ?", "Follow": "Suivre", "Follow or unfollow posts": "Suivre ou ne plus suivre un message", @@ -1007,7 +999,6 @@ "HTML preview of post": "Pr\u00e9visualisation HTML de l'article", "HTML source code": "Code source HTML", "Have a computer with a functioning webcam": "Ayez un ordinateur avec une webcam fonctionnelle", - "Have you ever served on active duty in the U.S. Armed Forces, Reserves, or National Guard?": "Avez-vous d\u00e9j\u00e0 servi en service actif dans les forces arm\u00e9es, les r\u00e9serves ou la garde nationale?", "Have your valid photo ID (e.g. driver's license or passport) ready": "Ayez \u00e0 port\u00e9e de main votre pi\u00e8ce d'identit\u00e9 valide avec photo (par exemple, permis de conduire ou passeport)", "Header": "En-t\u00eate", "Header 1": "En-t\u00eate 1", @@ -1213,7 +1204,6 @@ "Loading more threads": "Chargement de plus de messages", "Loading posts list": "Chargement de la liste des messages", "Loading your courses": "Chargement de vos cours", - "Loading...": "Chargement...", "Location": "Adresse", "Location in Course": "Emplacement dans le cours", "Lock this asset": "V\u00e9rouiller cet \u00e9l\u00e9ment", @@ -1277,7 +1267,6 @@ "Name or short description of the configuration": "Nom ou description br\u00e8ve de la configuration", "Navigate to onboarding exam": "Acc\u00e9dez \u00e0 l'examen d'int\u00e9gration", "Navigate up": "Naviguer vers le haut", - "Need help logging in?": "Besoin d'aide pour vous connecter ?", "Need help signing in?": "Besoin d'aide pour vous enregistrer?", "Need other help signing in?": "Encore besoin d'aide pour vous enregistrer?", "Needs verified certificate ": "N\u00e9cessite un certificat v\u00e9rifi\u00e9", @@ -1393,7 +1382,6 @@ "Organization ": "Organisation", "Organization Name": "Nom de l'organisation", "Organization of the signatory": "Organisation du signataire", - "Organization:": "Organisation:", "Other": "Autre", "Other sign-in issues": "Autres probl\u00e8mes d'enregistrement", "Overall Score": "Score total", @@ -1540,7 +1528,6 @@ "Questions raise issues that need answers. Discussions share ideas and start conversations. (Required)": "Les questions sont adapt\u00e9es pour les sujets qui n\u00e9cessitent une r\u00e9ponse. Les discussions permettent de partager des id\u00e9es et de commencer des conversations. (Requis)", "Queued": "Mis en file d'attente", "REMAINING COURSES": "RESTANT", - "Re-run Course": "Relancer le cours", "Read More": "En lire plus.", "Read more": "En lire plus", "Ready To Start": "Pr\u00eat \u00e0 Commencer", @@ -1619,7 +1606,6 @@ "Return to Export": "Retourner \u00e0 l'exportation", "Return to Your Dashboard": "Retour au Tableau de bord", "Return to all posts": "Revenir \u00e0 tous les sujets", - "Return to my dashboard": "Retour \u00e0 mon tableau de bord", "Return to team listing": "Retour \u00e0 la liste des membres de l'\u00e9quipe", "Review Policy Exception": "Exception aux R\u00e8gles de Revues", "Review Rules": "V\u00e9rification des r\u00e8gles", @@ -1672,14 +1658,12 @@ "Select a subject for your support request.": "S\u00e9lectionnez un sujet pour votre demande d'assistance.", "Select a time allotment for the exam. If it is over 24 hours, type in the amount of time. You can grant individual learners extra time to complete the exam through the Instructor Dashboard.": "Choisissez une p\u00e9riode de temps pour l'examen. Si c'est plus de 24 heures, indiquer la quantit\u00e9 de temps. Vous pouvez accorder du temps suppl\u00e9mentaire \u00e0 des apprenants individuels via le tableau de bord de l'enseignant afin de leur permettre de terminer l'examen.", "Select all": "Tout s\u00e9lectionner", - "Select employment status": "S\u00e9lectionnez le statut d'emploi", "Select fidelity": "S\u00e9lectionnez la fid\u00e9lit\u00e9", "Select language": "S\u00e9lectionnez la langue", "Select one or more groups:": "S\u00e9lectionner au moins un groupe\u00a0:", "Select the course-wide discussion topics that you want to divide.": "S\u00e9lectionnez les sujets de discussion du cours que vous souhaitez s\u00e9parer.", "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browser's local time zone.": "S\u00e9lectionnez le fuseau horaire pour l'affichage des dates de cours. Si vous n'indiquez aucun fuseau horaire, les dates de cours, y compris les \u00e9ch\u00e9ances de devoirs, seront affich\u00e9s en fonction du fuseau horaire local de votre navigateur. ", "Select turnaround": "S\u00e9lectionnez le d\u00e9lai d'ex\u00e9cution", - "Selected blocks": "Blocs s\u00e9lectionn\u00e9s", "Selected tab": "Onglet s\u00e9lectionn\u00e9", "Self": "Auto", "Send to:": "Envoyez \u00e0 :", @@ -2316,17 +2300,11 @@ "Weight of Total Grade": "Valeur de l'ensemble du grade", "We\u2019re sorry to see you go!": "Nous sommes d\u00e9sol\u00e9s de vous voir partir !", "What You Need for Verification": "Ce qui est n\u00e9c\u00e9saire pour la v\u00e9rification", - "What can we help you with, {username}?": "A quoi pouvons-nous vous \u00eatre utile, {username}?", "What does %(platformName)s do with this photo?": "Que fait %(platformName)s avec cette photo ?", "What does this mean?": "Qu'est ce que cela signifie?", "What if I can't see the camera image, or if I can't see my photo do determine which side is visible?": "Que se passe-t-il si je ne peux pas voir l'image de l'appareil photo ou si je ne peux pas voir ma photo pour d\u00e9terminer quel c\u00f4t\u00e9 est visible ?", "What if I have difficulty holding my ID in position relative to the camera?": "Que faire si j'ai des difficult\u00e9s \u00e0 tenir ma carte d'identit\u00e9 en position par rapport \u00e0 la cam\u00e9ra ?", "What if I have difficulty holding my head in position relative to the camera?": "Que faire si j'ai des difficult\u00e9s \u00e0 maintenir ma t\u00eate en position par rapport \u00e0 la cam\u00e9ra ?", - "What industry do you currently work in?": "Dans quel secteur travaillez-vous actuellement?", - "What industry do you want to work in?": "Dans quel secteur souhaitez-vous travailler?", - "What is the highest level of education that any of your parents or guardians have achieved?": "Quel est le niveau de scolarit\u00e9 le plus \u00e9lev\u00e9 que l'un de vos parents ou tuteurs ait atteint?", - "What is the highest level of education that you have achieved so far?": "Quel est le niveau d'\u00e9ducation le plus \u00e9lev\u00e9 que vous ayez atteint jusqu'\u00e0 pr\u00e9sent?", - "What was the total combined income, during the last 12 months, of all members of your family? ": "Quel a \u00e9t\u00e9 le revenu total combin\u00e9, au cours des 12 derniers mois, de tous les membres de votre famille?", "What's Your Next Accomplishment?": "Quel est votre prochain accomplissement?", "When learners submit an answer to an assessment, they immediately see whether the answer is correct or incorrect, and the score received.": "Lorsque les \u00e9tudiants r\u00e9pondent \u00e0 une \u00e9valuation, ils voient imm\u00e9diatement si la r\u00e9ponse est juste ou fausse ainsi que leur note. ", "When your face is in position, use the Take Photo button {icon} below to take your photo.": "Lorsque votre visage est en position, utilisez le bouton \"Prendre une photo\" {icon} ci-dessous pour prendre votre photo.", @@ -2335,7 +2313,6 @@ "Whole words": "Mots entiers", "Why activate?": "Pourquoi activer?", "Why does %(platformName)s need my photo?": "Pourquoi %(platformName)s a besoin de ma photo ?", - "Why does edX collect this information?": "Pourquoi edX collecte-t-il ces informations ?", "Width": "Largeur", "Will Be Visible To:": "Sera visible pour :", "Words: {0}": "Mots : {0}", @@ -2540,7 +2517,6 @@ "endorsed %(time_ago)s by %(user)s": "approuv\u00e9 %(time_ago)s par %(user)s", "enter code here": "saisir du code ici", "enter link description here": "saisir une description du lien ici", - "finish later": "terminer plus tard", "follow this post": "Suivre ce message", "for": "pour", "group configuration": "configuration des groupes", @@ -2555,7 +2531,6 @@ "minute": "minute", "minutes": "minutes", "name": "nom", - "next page": "page suivante", "off": "arr\u00eat", "on": "on", "one letter Friday\u0004F": "V", @@ -2587,7 +2562,6 @@ "team count": "total \u00e9quipe", "text_word_{uniqueId}": "text_word_{uniqueId}", "text_word_{uniqueId} title_word_{uniqueId}": "text_word_{uniqueId} title_word_{uniqueId}", - "the more quickly and helpfully we can respond!": "le plus rapidement et efficacement que nous pouvons r\u00e9pondre !", "there is currently {numVotes} vote": [ "il y a actuellement {numVotes} vote", "il y a actuellement {numVotes} votes", diff --git a/cms/static/js/i18n/id/djangojs.js b/cms/static/js/i18n/id/djangojs.js index ce4883a2e1..1ec5f0cd4e 100644 --- a/cms/static/js/i18n/id/djangojs.js +++ b/cms/static/js/i18n/id/djangojs.js @@ -233,7 +233,6 @@ "Bottom": "Bawah", "Browse": "Jelajahi", "Browse recently launched courses and see what\\'s new in your favorite subjects": "Telusuri kursus yang baru dirilis dan lihat yang baru dalam subjek favorit Anda.", - "Browsing": "Jelajah", "Bulk Exceptions": "Pengecualian Massal", "Bullet list": "Bullet list", "Bulleted List (Ctrl+U)": "Bulleted List (Ctrl+U)", @@ -374,9 +373,7 @@ "Course Name": "Nama Kursus", "Course Number": "Nomor Kursus", "Course Number Override": "Penggantian Nomor Kursus", - "Course Number:": "Nomor Kursus:", "Course Outline": "Garis Besar Kursus", - "Course Run:": "Kursus Berjalan:", "Course Title": "Judul Kursus", "Course Title Override": "Penggantian Judul Kursus", "Course Video Settings": "Pengaturan Video Kursus", @@ -428,7 +425,6 @@ "Describe ": "Deskripsikan", "Description": "Deskripsi", "Description of the certificate": "Keterangan sertifikat", - "Details": "Detil", "Dimensions": "Dimensi", "Disc": "Cakram", "Discard Changes": "Batalkan Perubahan", @@ -906,7 +902,6 @@ "Organization ": "Organisasi", "Organization Name": "Nama Organisasi", "Organization of the signatory": "Organisasi dari penandatangan", - "Organization:": "Organisasi:", "Other": "Lainnya", "Overall Score": "Nilai Keseluruhan", "PDF Chapters": "Bab PDF", @@ -1561,7 +1556,6 @@ "Weight of Total Grade": "Berat dari total grade", "We\u2019re sorry to see you go!": "Kami sedih Anda pergi ", "What You Need for Verification": "Apa yang anda perlukan untuk verifikasi", - "What can we help you with, {username}?": "Ada yang bisa kami bantu, {username}?", "What does %(platformName)s do with this photo?": "Apa yang dilakukan %(platformName)s terhadap foto ini?", "What does this mean?": "Apakah ini maksudnya?", "When learners submit an answer to an assessment, they immediately see whether the answer is correct or incorrect, and the score received.": "Saat peserta mengirimkan jawaban untuk penilaian, mereka langsung dapat melihat apakah jawaban mereka benar atau salah, dan nilai yang mereka terima.", diff --git a/cms/static/js/i18n/it-it/djangojs.js b/cms/static/js/i18n/it-it/djangojs.js index 6d7795d97e..cbf49bcad8 100644 --- a/cms/static/js/i18n/it-it/djangojs.js +++ b/cms/static/js/i18n/it-it/djangojs.js @@ -210,7 +210,6 @@ "(Caption will be displayed when you start playing the video.)": "(La trascrizione del video verr\u00e0 mostrata quando inizierai a guardare il video)", "(Community TA)": "(Community TA)", "(Optional)": "(Facoltativo)", - "(Read-only)": "(Sola lettura)", "(Required Field)": "(Campo richiesto)", "(Self-paced) Ended {end}": "(Apprendimento autonomo) Terminato {end}", "(Self-paced) Ends {end}": "(Apprendimento autonomo) Termina {end}", @@ -360,7 +359,6 @@ "An error has occurred. Wait a few minutes, and then try again.": "Si \u00e8 verificato un errore. Aspetta qualche minuto e riprova.", "An error occurred retrieving your email. Please try again later, and contact technical support if the problem persists.": "Si \u00e8 verificato un errore mentre cercavi di leggere le tue email. Riprova dopo cortesemente, e contatta il supporto tecnico se il problema persiste.", "An error occurred when signing you in to %s.": "Si \u00e8 verificato un errore durante l'accesso a %s.", - "An error occurred while attempting to retrieve or save the information below. Please try again later.": "Si \u00e8 verificato un errore durante il tentativo di recuperare o salvare le informazioni seguenti. Prova di nuovo pi\u00f9 tardi.", "An error occurred while looking up team membership. Try refreshing the page.": "Si \u00e8 verificato un errore durante la ricerca tra le adesioni ai team. Prova ad aggiornare la pagina.", "An error occurred while removing the member from the team. Try again.": "Si \u00e8 verificato un errore durante la rimozione del membro dal team. Riprova pi\u00f9 tardi", "An error occurred.": "Si \u00e8 verificato un errore. ", @@ -447,7 +445,6 @@ "Bottom": "In basso", "Browse": "Cerca", "Browse recently launched courses and see what\\'s new in your favorite subjects": "Sfoglia i corsi avviati di recente e scopri le novit\u00e0 nei tuoi soggetti preferiti ", - "Browsing": "Esplorazione", "Bulk Exceptions": "Eccezioni di massa ", "Bullet list": "Elenco puntato", "Bulleted List (Ctrl+U)": "Elenco puntato (Ctrl+U)", @@ -629,9 +626,7 @@ "Course Name": "Nome Corso", "Course Number": "Numero del Corso", "Course Number Override": "Sostituzione numero corso", - "Course Number:": "Numero Corso", "Course Outline": "Struttura del corso", - "Course Run:": "Esecuzione corso:", "Course Start": "Inizio del corso ", "Course Title": "Titolo del Corso", "Course Title Override": "Sostituzione titolo corso", @@ -643,7 +638,6 @@ "Create": "Crea", "Create Account": "Crea account", "Create Re-run": "Crea un riavvio", - "Create Support Ticket": "Crea ticket di supporto", "Create a New Team": "Crea un nuovo gruppo", "Create a content group": "Crea un gruppo di contenuti ", "Create a new team if you can't find an existing team to join, or if you would like to learn with friends you know.": "Crea un nuovo gruppo se non ne trovi uno esistente di cui far parte, o se vorresti apprendere insieme ad amici che conosci ", @@ -713,7 +707,6 @@ "Describe ": "Descrivi ", "Description": "Descrizione", "Description of the certificate": "Descrizione del certificato", - "Details": "Dettagli", "Device with Camera": "Dispositivo con telecamera ", "Dimensions": "Dimensioni", "Disc": "Disco", @@ -925,7 +918,6 @@ "Find next": "Trova successivo", "Find previous": "Trova precedente", "Finish": "Fine", - "Finish later": "Termina pi\u00f9 tardi", "First time here?": "Nuovo utente? ", "Follow": "Segui ", "Follow or unfollow posts": "Segui o non seguire post ", @@ -979,7 +971,6 @@ "HTML preview of post": "Anteprima HTML del post", "HTML source code": "Codice sorgente HTML", "Have a computer with a functioning webcam": "Disporre di un computer con una webcam funzionante", - "Have you ever served on active duty in the U.S. Armed Forces, Reserves, or National Guard?": "Hai mai prestato servizio attivo nelle forze armate, nelle riserve o nella guardia nazionale degli Stati Uniti? ", "Have your valid photo ID (e.g. driver's license or passport) ready": "Avere a disposizione un documento d'identit\u00e0 valido con foto (ad es. la patente di guida o il passaporto)", "Header": "Intestazione", "Header 1": "Intestazione 1", @@ -1182,7 +1173,6 @@ "Loading more threads": "Carica ulteriori discussioni", "Loading posts list": "Caricamento dell'elenco dei post ", "Loading your courses": "Caricamento dei tuoi corsi", - "Loading...": "Caricamento...", "Location": "Posizione", "Location in Course": "Posizione in corso", "Lock this asset": "Blocca questa attivit\u00e0", @@ -1246,7 +1236,6 @@ "Name or short description of the configuration": "Nome o breve descrizione della configurazione ", "Navigate to onboarding exam": "Accedi all'esame di onboarding", "Navigate up": "Naviga su", - "Need help logging in?": "Hai bisogno di aiuto per l'accesso?", "Need help signing in?": "Hai bisogno di aiuto per l'accesso? ", "Need other help signing in?": "Hai bisogno di altro aiuto per l'accesso? ", "Needs verified certificate ": "Richiede un certificato verificato ", @@ -1361,7 +1350,6 @@ "Organization ": "Organizzazione", "Organization Name": "Nome organizzazione", "Organization of the signatory": "Organizzazione del firmatario", - "Organization:": "Organizzazione:", "Other": "Altro", "Other sign-in issues": "Altri problemi di collegamento ", "Overall Score": "Voto Finale", @@ -1504,7 +1492,6 @@ "Questions raise issues that need answers. Discussions share ideas and start conversations. (Required)": "Le domande sollevano problemi che richiedono risposte. Le discussioni condividono idee ed avviano le conversazioni. (Obbligatorio) ", "Queued": "In coda", "REMAINING COURSES": "CORSI RIMANENTI ", - "Re-run Course": "Riesegui il corso", "Read More": "Per saperne di pi\u00f9", "Read more": "Per saperne di pi\u00f9", "Ready To Start": "Pronto a partire", @@ -1580,7 +1567,6 @@ "Return to Export": "Ritorna a Esporta", "Return to Your Dashboard": "Ritorna alla tua dashboard", "Return to all posts": "Torna a tutti i post", - "Return to my dashboard": "Torna alla mia dashboard", "Return to team listing": "Torna alla lista del gruppo", "Review Policy Exception": "Verifica Eccezione Policy", "Review Rules": "Regole di controllo", @@ -1632,14 +1618,12 @@ "Select a subject for your support request.": "Seleziona un argomento per la tua richiesta di supporto. ", "Select a time allotment for the exam. If it is over 24 hours, type in the amount of time. You can grant individual learners extra time to complete the exam through the Instructor Dashboard.": "Seleziona il tempo da assegnare per l'esame. Se sono pi\u00f9 di 24 ore, digita la quantit\u00e0 di tempo. Puoi concedere ai singoli studenti pi\u00f9 tempo per completare l'esame tramite la Dashboard dell'Istruttore. ", "Select all": "Seleziona tutto", - "Select employment status": "Seleziona occupazione", "Select fidelity": "Seleziona fedelt\u00e0", "Select language": "Seleziona lingua", "Select one or more groups:": "Seleziona uno o pi\u00f9 gruppi:", "Select the course-wide discussion topics that you want to divide.": "Selezionare gli argomenti di discussione a livello del corso che si desidera dividere. ", "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browser's local time zone.": "Seleziona il fuso orario per la visualizzazione delle date dei corsi. Se non specifichi un fuso orario, le date dei corsi, comprese le scadenze dei compiti, verranno visualizzate nel fuso orario locale del tuo browser. ", "Select turnaround": "Seleziona soluzione temporaneo", - "Selected blocks": "Blocchi selezionati", "Selected tab": "Tab selezionata", "Self": "Autonomo ", "Send to:": "Manda a:", @@ -2259,17 +2243,11 @@ "Weight of Total Grade": "Peso del Voto totale", "We\u2019re sorry to see you go!": "Ci dispiace che tu ci lasci! ", "What You Need for Verification": "Cosa serve per la verifica ", - "What can we help you with, {username}?": "Come possiamo aiutarti, {username}?", "What does %(platformName)s do with this photo?": "Per cosa viene utilizzata la mia foto da %(platformName)s ? ", "What does this mean?": "Cosa significa? ", "What if I can't see the camera image, or if I can't see my photo do determine which side is visible?": "Cosa devo fare se non posso visualizzare l'immagine della fotocamera o se non posso visualizzare la mia foto per determinare il lato visibile? ", "What if I have difficulty holding my ID in position relative to the camera?": "Cosa devo fare se ho difficolt\u00e0 a mantenere il documento di identit\u00e0 in posizione rispetto alla telecamera? ", "What if I have difficulty holding my head in position relative to the camera?": "Cosa devo fare se ho difficolt\u00e0 a mantenere la testa in posizione rispetto alla telecamera? ", - "What industry do you currently work in?": "In quale settore lavori attualmente? ", - "What industry do you want to work in?": "In quale settore vuoi lavorare? ", - "What is the highest level of education that any of your parents or guardians have achieved?": "Qual \u00e8 il livello di istruzione pi\u00f9 elevato raggiunto da uno dei tuoi genitori o tutori? ", - "What is the highest level of education that you have achieved so far?": "Qual \u00e8 il livello di istruzione pi\u00f9 elevato che hai raggiunto finora? ", - "What was the total combined income, during the last 12 months, of all members of your family? ": "Qual \u00e8 stato il reddito totale complessivo, negli ultimi 12 mesi, di tutti i membri della tua famiglia? ", "What's Your Next Accomplishment?": "Quale \u00e8 il tuo prossimo risultato? ", "When learners submit an answer to an assessment, they immediately see whether the answer is correct or incorrect, and the score received.": "Quando gli studenti inviano una risposta a una valutazione, vedono immediatamente se la risposta \u00e8 corretta o errata e il punteggio ottenuto. ", "When your face is in position, use the Take Photo button {icon} below to take your photo.": "Quando il viso \u00e8 in posizione, utilizzare il pulsante Scatta la foto {icon} di seguito per scattare la foto. ", @@ -2278,7 +2256,6 @@ "Whole words": "Parole intere", "Why activate?": "Perch\u00e9 attivare? ", "Why does %(platformName)s need my photo?": "Perch\u00e9 %(platformName)s richiede la mia foto? ", - "Why does edX collect this information?": "Perch\u00e9 edX raccoglie queste informazioni?", "Width": "Larghezza", "Will Be Visible To:": "Sar\u00e0 visibile a:", "Words: {0}": "Parole: {0}", @@ -2480,7 +2457,6 @@ "endorsed %(time_ago)s by %(user)s": "promosso %(time_ago)s da %(user)s", "enter code here": "inserisci qui il codice", "enter link description here": "Inserisci qui la descrizione del link", - "finish later": "termina pi\u00f9 tardi", "follow this post": "segui questo post ", "for": "per ", "group configuration": "Configurazione del gruppo", @@ -2494,7 +2470,6 @@ "minute": "minuto", "minutes": "minuti", "name": "nome", - "next page": "pagina successiva", "off": "off", "on": "on", "one letter Friday\u0004F": "V", @@ -2526,7 +2501,6 @@ "team count": "totale gruppo", "text_word_{uniqueId}": "text_word_{uniqueId}", "text_word_{uniqueId} title_word_{uniqueId}": "text_word_{uniqueId} title_word_{uniqueId}", - "the more quickly and helpfully we can respond!": "rispondere nel modo pi\u00f9 veloce e utile possibile. ", "there is currently {numVotes} vote": [ "attualmente \u00e8 presente {numVotes} voto", "attualmente sono presenti {numVotes} voti", diff --git a/cms/static/js/i18n/mn/djangojs.js b/cms/static/js/i18n/mn/djangojs.js index c733e16930..125410cbf4 100644 --- a/cms/static/js/i18n/mn/djangojs.js +++ b/cms/static/js/i18n/mn/djangojs.js @@ -126,9 +126,7 @@ "Course Id": "\u0425\u0438\u0447\u044d\u044d\u043b\u0438\u0439\u043d \u043a\u043e\u0434", "Course Name": "\u0425\u0438\u0447\u044d\u044d\u043b\u0438\u0439\u043d \u043d\u044d\u0440", "Course Number": "\u0425\u0438\u0447\u044d\u044d\u043b\u0438\u0439\u043d \u0434\u0443\u0433\u0430\u0430\u0440", - "Course Number:": "\u0425\u0438\u0447\u044d\u044d\u043b\u0438\u0439\u043d \u0434\u0443\u0433\u0430\u0430\u0440:", "Course Outline": "\u0425\u0438\u0447\u044d\u044d\u043b\u0438\u0439\u043d \u0433\u0430\u0440\u0447\u0438\u0433", - "Course Run:": "\u0425\u0438\u0447\u044d\u044d\u043b\u0438\u0439\u043d \u0445\u0443\u0433\u0430\u0446\u0430\u0430:", "Course Title": "\u0425\u0438\u0447\u044d\u044d\u043b\u0438\u0439\u043d \u043d\u044d\u0440", "Course is not yet visible to students.": "\u0425\u0438\u0447\u044d\u044d\u043b \u0441\u0443\u0440\u0430\u043b\u0446\u0430\u0433\u0447\u0434\u0430\u0434 \u0430\u0440\u0430\u0439 \u0445\u0430\u0440\u0430\u0433\u0434\u0430\u0445\u0433\u04af\u0439 \u0431\u0430\u0439\u0433\u0430\u0430. ", "Create": "\u04ae\u04af\u0441\u0433\u044d\u0445", @@ -149,7 +147,6 @@ "Delete table": "\u0425\u04af\u0441\u043d\u044d\u0433\u0442 \u0443\u0441\u0442\u0433\u0430\u0445", "Deleting": "\u0423\u0441\u0442\u0433\u0430\u0436 \u0431\u0430\u0439\u043d\u0430", "Description": "\u0422\u0430\u0439\u043b\u0431\u0430\u0440", - "Details": "\u0414\u044d\u043b\u0433\u044d\u0440\u044d\u043d\u0433\u04af\u0439", "Dimensions": "\u0425\u044d\u043c\u0436\u044d\u044d\u0441\u04af\u04af\u0434", "Discard Changes": "\u04e8\u04e9\u0440\u0447\u043b\u04e9\u043b\u0442\u0438\u0439\u0433 \u0431\u043e\u043b\u0438\u0443\u043b\u0430\u0445", "Discussion Home": "\u042d\u0445\u043b\u044d\u043b", @@ -285,7 +282,6 @@ "OpenAssessment Save Error": "\u041d\u044d\u044d\u043b\u0442\u0442\u044d\u0439 \u04af\u043d\u044d\u043b\u0433\u044d\u044d \u0425\u0430\u0434\u0433\u0430\u043b\u0430\u0433\u0434\u0430\u0445 \u0410\u043b\u0434\u0430\u0430", "Order History": "\u0417\u0430\u0445\u0438\u0430\u043b\u0433\u044b\u043d \u0442\u04af\u04af\u0445", "Organization": "\u0411\u0430\u0439\u0433\u0443\u0443\u043b\u043b\u0430\u0433\u0430", - "Organization:": "\u0411\u0430\u0439\u0433\u0443\u0443\u043b\u043b\u0430\u0433\u0430:", "Other": "\u0411\u0443\u0441\u0430\u0434", "Password": "\u041d\u0443\u0443\u0446 \u04af\u0433", "Play": "\u0422\u043e\u0433\u043b\u0443\u0443\u043b\u0430\u0445", @@ -449,7 +445,7 @@ "Very loud": "\u041c\u0430\u0448 \u0447\u0430\u043d\u0433\u0430", "Very low": "\u041c\u0430\u0448 \u0441\u0443\u043b", "View": "\u0425\u0430\u0440\u0430\u0445", - "View Live": "LMS-\u0434 \u04af\u0437\u044d\u0445", + "View Live": "LMS-\u0434 \u0445\u0430\u0440\u0430\u0445", "Volume": "\u0426\u0430\u0440\u0430\u0430", "Vote for good posts and responses": "\u0427\u0430\u043d\u0430\u0440\u0442\u0430\u0439 \u0431\u0438\u0447\u043b\u044d\u0433 \u0431\u043e\u043b\u043e\u043d \u0445\u0430\u0440\u0438\u0443\u043b\u0442\u0430\u0434 \u0441\u0430\u043d\u0430\u043b \u04e9\u0433\u04e9\u0445", "We couldn't sign you in.": "\u0422\u0430\u043d\u044b\u0433 \u0441\u0438\u0441\u0442\u0435\u043c\u0434 \u043d\u044d\u0432\u0442\u0440\u04af\u04af\u043b\u044d\u0445 \u0431\u043e\u043b\u043e\u043c\u0436\u0433\u04af\u0439 \u0431\u0430\u0439\u043d\u0430", diff --git a/cms/static/js/i18n/pt-pt/djangojs.js b/cms/static/js/i18n/pt-pt/djangojs.js index 6688e16f7b..477fc56bff 100644 --- a/cms/static/js/i18n/pt-pt/djangojs.js +++ b/cms/static/js/i18n/pt-pt/djangojs.js @@ -171,7 +171,6 @@ "(Caption will be displayed when you start playing the video.)": "(As legendas ser\u00e3o exibidas quando iniciar a reprodu\u00e7\u00e3o do v\u00eddeo.)", "(Community TA)": "(Assistente de Ensino da Comunidade)", "(Optional)": "(Opcional)", - "(Read-only)": "(S\u00f3 leitura)", "(Required Field)": "(Campo Obrigat\u00f3rio)", "(Self-paced) Ended {end}": "(Ritmo Pr\u00f3prio) Terminado {end}", "(Self-paced) Ends {end}": "(Ritmo Pr\u00f3prio) Termina {end}", @@ -322,7 +321,6 @@ "An error has occurred. Wait a few minutes, and then try again.": "Ocorreu um erro. Aguarde alguns minutos e tente novamente.", "An error occurred retrieving your email. Please try again later, and contact technical support if the problem persists.": "Ocorreu um erro ao recuperar o seu email. Por favor, tente novamente mais tarde e contate o suporte t\u00e9cnico se o problema persistir.", "An error occurred when signing you in to %s.": "Ocorreu um erro ao iniciar sess\u00e3o em %s.", - "An error occurred while attempting to retrieve or save the information below. Please try again later.": "Ocorreu um erro ao tentar recuperar ou salvar as informa\u00e7\u00f5es abaixo. Por favor, tente novamente mais tarde.", "An error occurred while looking up team membership. Try refreshing the page.": "Um erro ocorreu ao procurar a ades\u00e3o \u00e0 equipa. Tente atualizar a p\u00e1gina.", "An error occurred while removing the member from the team. Try again.": "Ocorreu um erro ao remover um membro da equipa. Tente novamente.", "An error occurred.": "Ocorreu um erro.", @@ -409,7 +407,6 @@ "Bottom": "Fundo", "Browse": "Procurar", "Browse recently launched courses and see what\\'s new in your favorite subjects": "Explore os cursos recentemente lan\u00e7ados e veja o que h\u00e1 de novo sobre os seus temas favoritos", - "Browsing": "Navega\u00e7\u00e3o", "Bulk Exceptions": "Exce\u00e7\u00f5es em massa", "Bullet list": "Lista de marcadores", "Bulleted List (Ctrl+U)": "Lista de Marcadores(Ctrl+U)", @@ -590,9 +587,7 @@ "Course Name": "Nome do curso", "Course Number": "C\u00f3digo do Curso", "Course Number Override": "Substitui\u00e7\u00e3o do C\u00f3digo do Curso", - "Course Number:": "N\u00famero do Curso:", "Course Outline": "Descri\u00e7\u00e3o do Curso", - "Course Run:": "Curso de Forma\u00e7\u00e3o:", "Course Start": "Data de In\u00edcio do Curso", "Course Title": "T\u00edtulo do Curso", "Course Title Override": "Substitui\u00e7\u00e3o do T\u00edtulo do Curso", @@ -604,7 +599,6 @@ "Create": "Criar", "Create Account": "Criar Conta", "Create Re-run": "Criar Nova Edi\u00e7\u00e3o do Curso", - "Create Support Ticket": "Criar Pedido de Apoio", "Create a New Team": "Criar nova equipa", "Create a content group": "Criar um grupo de conte\u00fado", "Create a new team if you can't find an existing team to join, or if you would like to learn with friends you know.": "Crie uma nova equipa se n\u00e3o conseguir encontrar uma equipa existente para se juntar, ou se pretende aprender com os amigos que conhece.", @@ -674,7 +668,6 @@ "Describe ": "Descreva ", "Description": "Descri\u00e7\u00e3o", "Description of the certificate": "Descri\u00e7\u00e3o do Certificado", - "Details": "Detalhes", "Device with Camera": "Dispositivo com C\u00e2mara", "Dimensions": "Dimens\u00f5es", "Disc": "Disco", @@ -886,7 +879,6 @@ "Find next": "Encontrar o seguinte", "Find previous": "Localizar anterior", "Finish": "Terminar", - "Finish later": "Terminar mais tarde", "First time here?": "\u00c9 a primeira vez aqui?", "Follow": "Seguir", "Follow or unfollow posts": "Siga ou deixe de seguir publica\u00e7\u00f5es", @@ -939,7 +931,6 @@ "H Align": "Alinhamento horizontal", "HTML preview of post": "HTML pr\u00e9-visualiza\u00e7\u00e3o da publica\u00e7\u00e3o", "HTML source code": "C\u00f3digo-fonte HTML", - "Have you ever served on active duty in the U.S. Armed Forces, Reserves, or National Guard?": "J\u00e1 serviu, em ativo, nas For\u00e7as Armadas, Reservas ou Guarda Nacional dos EUA?", "Header": "Cabe\u00e7alho", "Header 1": "Cabe\u00e7alho 1", "Header 2": "Cabe\u00e7alho 2", @@ -1142,7 +1133,6 @@ "Loading more threads": "A carregar mais t\u00f3picos", "Loading posts list": "A carregar lista de publica\u00e7\u00f5es", "Loading your courses": "Carregando os seus cursos", - "Loading...": "Carregando...", "Location": "Local", "Location in Course": "Localiza\u00e7\u00e3o no Curso", "Lock this asset": "Bloquear este ficheiro", @@ -1203,7 +1193,6 @@ "Name of the signatory": "Nome do Signat\u00e1rio", "Name or short description of the configuration": "Nome ou breve descri\u00e7\u00e3o da configura\u00e7\u00e3o", "Navigate up": "Subir", - "Need help logging in?": "Precisa de ajuda para iniciar sess\u00e3o?", "Need help signing in?": "Precisa de ajuda para entrar?", "Need other help signing in?": "Precisa de outra ajuda para entrar?", "Needs verified certificate ": "Necessita de certificado validado ", @@ -1314,7 +1303,6 @@ "Organization ": "Organiza\u00e7\u00e3o", "Organization Name": "Nome da Organiza\u00e7\u00e3o", "Organization of the signatory": "Organiza\u00e7\u00e3o do signat\u00e1rio", - "Organization:": "Organiza\u00e7\u00e3o:", "Other": "Outro", "Other sign-in issues": "Outros problemas sobre iniciar sess\u00e3o", "Overall Score": "Pontua\u00e7\u00e3o geral", @@ -1456,7 +1444,6 @@ "Questions raise issues that need answers. Discussions share ideas and start conversations. (Required)": "As perguntas levantam quest\u00f5es que precisam de uma resposta. Os debates partilham ideias e iniciam conversas. (Obrigat\u00f3rio)", "Queued": "Em fila de espera", "REMAINING COURSES": "RESTANTES CURSOS", - "Re-run Course": "Reexecutar Curso", "Read More": "Ler Mais", "Read more": "Ler mais", "Ready To Start": "Pronto Para Come\u00e7ar", @@ -1531,7 +1518,6 @@ "Return to Export": "Regressar a Exporta\u00e7\u00e3o", "Return to Your Dashboard": "Regressar ao Painel de Controlo", "Return to all posts": "Regressar a todos os artigos", - "Return to my dashboard": "Voltar ao meu painel", "Return to team listing": "Regressar \u00e0 listagem da equipa", "Review Policy Exception": "Excep\u00e7\u00e3o \u00e0 Pol\u00edtica de Revis\u00e3o", "Review Rules": "Regras de Revis\u00e3o", @@ -1583,14 +1569,12 @@ "Select a subject for your support request.": "Selecione um assunto para o seu pedido de apoio.", "Select a time allotment for the exam. If it is over 24 hours, type in the amount of time. You can grant individual learners extra time to complete the exam through the Instructor Dashboard.": "Selecione o tempo de aloca\u00e7\u00e3o para o exame. Se for mais de 24 horas, digite a quantidade de tempo. Pode conceder tempo adicional aos estudantes para completarem o exame atrav\u00e9s do painel do formador.", "Select all": "Selecionar tudo", - "Select employment status": "Selecione o estatuto profissional.", "Select fidelity": "Selecionar fidelidade", "Select language": "Selecionar idioma", "Select one or more groups:": "Selecione um ou mais grupos:", "Select the course-wide discussion topics that you want to divide.": "Selecionar os t\u00f3picos de debate de todo o curso que pretende dividir.", "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browser's local time zone.": "Selecione o fuso hor\u00e1rio para exibir as datas do curso. Se n\u00e3o for especificado um fuso hor\u00e1rio, as datas do curso, incluindo os prazos de atribui\u00e7\u00e3o, ser\u00e3o exibidos no fuso hor\u00e1rio local do seu navegador.", "Select turnaround": "Selecione uma alernativa", - "Selected blocks": "Blocos selecionados", "Selected tab": "Separador selecionado", "Self": "Auto", "Send to:": "Enviar para:", @@ -2167,7 +2151,7 @@ "View Cohort": "Ver Grupo", "View Course": "Ver Curso", "View Current Team Memberships": "Ver Equipas de Membros Actuais", - "View Live": "Ver em directo", + "View Live": "Visualizar ao vivo", "View Program Record": "Ver Registo do Programa", "View Report": "Ver Relat\u00f3rio", "View Teams in the {topic_name} Topic": "Ver equipas no {topic_name} T\u00f3pico", @@ -2213,17 +2197,11 @@ "Weight of Total Grade": "Peso da Classifica\u00e7\u00e3o Total", "We\u2019re sorry to see you go!": "Temos pena que se v\u00e1 embora!", "What You Need for Verification": "O que \u00e9 necess\u00e1rio para a Valida\u00e7\u00e3o", - "What can we help you with, {username}?": "Em que podemos ajud\u00e1-lo, {username}?", "What does %(platformName)s do with this photo?": "O que far\u00e1 a %(platformName)s com esta fotografia?", "What does this mean?": "O que significa isto?", "What if I can't see the camera image, or if I can't see my photo do determine which side is visible?": "E se n\u00e3o conseguir ver a imagem da c\u00e2mara, ou se eu n\u00e3o conseguir ver a minha fotografia para determinar qual lado \u00e9 vis\u00edvel?", "What if I have difficulty holding my ID in position relative to the camera?": "E se eu tiver dificuldade em manter o meu documento de identifica\u00e7\u00e3o em posi\u00e7\u00e3o em rela\u00e7\u00e3o \u00e0 c\u00e2mara?", "What if I have difficulty holding my head in position relative to the camera?": "E se eu tiver dificuldade em manter minha cabe\u00e7a em posi\u00e7\u00e3o em rela\u00e7\u00e3o \u00e0 c\u00e2mara?", - "What industry do you currently work in?": "Em que sector trabalha atualmente?", - "What industry do you want to work in?": "Em que sector pretende trabalhar?", - "What is the highest level of education that any of your parents or guardians have achieved?": "Qual o n\u00edvel mais alto de educa\u00e7\u00e3o que um dos seus pais ou tutores atingiu?", - "What is the highest level of education that you have achieved so far?": "Qual o n\u00edvel mais alto de educa\u00e7\u00e3o que atingiu at\u00e9 agora?", - "What was the total combined income, during the last 12 months, of all members of your family? ": "Qual foi o rendimento total combinado, de todos os seus membros da sua fam\u00edlia, durante os \u00faltimos 12 meses? ", "What's Your Next Accomplishment?": "Qual \u00e9 a Sua Pr\u00f3xima Realiza\u00e7\u00e3o?", "When learners submit an answer to an assessment, they immediately see whether the answer is correct or incorrect, and the score received.": "Quando os estudantes submetem uma resposta a uma avalia\u00e7\u00e3o, podem confirmar imediatamente se a resposta est\u00e1 correta ou incorreta e qual a pontua\u00e7\u00e3o recebida.", "When your face is in position, use the Take Photo button {icon} below to take your photo.": "Quando a sua cara estiver em posi\u00e7\u00e3o, use o bot\u00e3o Tirar foto {icon} abaixo para tirar uma fotografia.", @@ -2232,7 +2210,6 @@ "Whole words": "Palavras completas", "Why activate?": "Porqu\u00ea ativar?", "Why does %(platformName)s need my photo?": "Porque \u00e9 que a %(platformName)s necessita da minha fotografia?", - "Why does edX collect this information?": "Por que \u00e9 que a edX recolhe estas informa\u00e7\u00f5es?", "Width": "Largura", "Will Be Visible To:": "Estar\u00e1 vis\u00edvel para:", "Words: {0}": "Palavras: {0}", @@ -2421,7 +2398,6 @@ "endorsed %(time_ago)s by %(user)s": "aprovado %(time_ago)s por %(user)s", "enter code here": "introduza o c\u00f3digo aqui", "enter link description here": "inserir descri\u00e7\u00e3o do link aqui", - "finish later": "terminar mais tarde", "follow this post": "siga esta publica\u00e7\u00e3o", "for": "para", "group configuration": "configura\u00e7\u00e3o de grupo", @@ -2435,7 +2411,6 @@ "minute": "minuto", "minutes": "minutos", "name": "nome", - "next page": "p\u00e1gina seguinte", "off": "desligado", "on": "ligado", "one letter Friday\u0004F": "S", @@ -2467,7 +2442,6 @@ "team count": "contagem de equipa", "text_word_{uniqueId}": "texto_palavra_{uniqueId}", "text_word_{uniqueId} title_word_{uniqueId}": "texto_palavra_{uniqueId} t\u00edtulo_palavra_{uniqueId}", - "the more quickly and helpfully we can respond!": "O mais r\u00e1pido e \u00fatil que conseguimos responder!", "there is currently {numVotes} vote": [ "existe atualmente {numVotes} voto", "existem atualmente {numVotes} votos", diff --git a/cms/static/js/i18n/rtl/djangojs.js b/cms/static/js/i18n/rtl/djangojs.js index fd178a6ae9..316b90b64c 100644 --- a/cms/static/js/i18n/rtl/djangojs.js +++ b/cms/static/js/i18n/rtl/djangojs.js @@ -403,6 +403,7 @@ "Code block": "\u023b\u00f8d\u01dd bl\u00f8\u0254\u029e", "Cohort Assignment Method": "\u023b\u00f8\u0265\u00f8\u0279\u0287 \u023ass\u1d09\u0183n\u026f\u01ddn\u0287 M\u01dd\u0287\u0265\u00f8d", "Cohort Name": "\u023b\u00f8\u0265\u00f8\u0279\u0287 N\u0250\u026f\u01dd", + "Cohort assignment not allowed: {email_or_username}": "\u023b\u00f8\u0265\u00f8\u0279\u0287 \u0250ss\u1d09\u0183n\u026f\u01ddn\u0287 n\u00f8\u0287 \u0250ll\u00f8\u028d\u01ddd: {email_or_username}", "Cohorts": "\u023b\u00f8\u0265\u00f8\u0279\u0287s", "Cohorts Disabled": "\u023b\u00f8\u0265\u00f8\u0279\u0287s \u0110\u1d09s\u0250bl\u01ddd", "Cohorts Enabled": "\u023b\u00f8\u0265\u00f8\u0279\u0287s \u0246n\u0250bl\u01ddd", @@ -577,9 +578,7 @@ "Download": "\u0110\u00f8\u028dnl\u00f8\u0250d", "Download Memberships": "\u0110\u00f8\u028dnl\u00f8\u0250d M\u01dd\u026fb\u01dd\u0279s\u0265\u1d09ds", "Download Transcript for Editing": "\u0110\u00f8\u028dnl\u00f8\u0250d \u0166\u0279\u0250ns\u0254\u0279\u1d09d\u0287 \u025f\u00f8\u0279 \u0246d\u1d09\u0287\u1d09n\u0183", - "Download URL": "\u0110\u00f8\u028dnl\u00f8\u0250d \u0244\u024c\u0141", "Download available encodings (.csv)": "\u0110\u00f8\u028dnl\u00f8\u0250d \u0250\u028c\u0250\u1d09l\u0250bl\u01dd \u01ddn\u0254\u00f8d\u1d09n\u0183s (.\u0254s\u028c)", - "Download the user's certificate": "\u0110\u00f8\u028dnl\u00f8\u0250d \u0287\u0265\u01dd ns\u01dd\u0279's \u0254\u01dd\u0279\u0287\u1d09\u025f\u1d09\u0254\u0250\u0287\u01dd", "Draft (Never published)": "\u0110\u0279\u0250\u025f\u0287 (N\u01dd\u028c\u01dd\u0279 dnbl\u1d09s\u0265\u01ddd)", "Draft (Unpublished changes)": "\u0110\u0279\u0250\u025f\u0287 (\u0244ndnbl\u1d09s\u0265\u01ddd \u0254\u0265\u0250n\u0183\u01dds)", "Draft saved on {lastSavedStart}{editedOn}{lastSavedEnd} by {editedByStart}{editedBy}{editedByEnd}": "\u0110\u0279\u0250\u025f\u0287 s\u0250\u028c\u01ddd \u00f8n {lastSavedStart}{editedOn}{lastSavedEnd} b\u028e {editedByStart}{editedBy}{editedByEnd}", @@ -1094,7 +1093,6 @@ "Not Graded": "N\u00f8\u0287 \u01e4\u0279\u0250d\u01ddd", "Not Supported": "N\u00f8\u0287 Sndd\u00f8\u0279\u0287\u01ddd", "Not able to set passing grade to less than %(minimum_grade_cutoff)s%.": "N\u00f8\u0287 \u0250bl\u01dd \u0287\u00f8 s\u01dd\u0287 d\u0250ss\u1d09n\u0183 \u0183\u0279\u0250d\u01dd \u0287\u00f8 l\u01ddss \u0287\u0265\u0250n %(minimum_grade_cutoff)s%.", - "Not available": "N\u00f8\u0287 \u0250\u028c\u0250\u1d09l\u0250bl\u01dd", "Not divided": "N\u00f8\u0287 d\u1d09\u028c\u1d09d\u01ddd", "Not in Use": "N\u00f8\u0287 \u1d09n \u0244s\u01dd", "Not selected": "N\u00f8\u0287 s\u01ddl\u01dd\u0254\u0287\u01ddd", diff --git a/cms/static/js/i18n/ru/djangojs.js b/cms/static/js/i18n/ru/djangojs.js index 5b212d4091..a3327ceec2 100644 --- a/cms/static/js/i18n/ru/djangojs.js +++ b/cms/static/js/i18n/ru/djangojs.js @@ -522,7 +522,6 @@ "Describe ": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435", "Description": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435", "Description of the certificate": "\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430", - "Details": "\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438", "Dimensions": "\u0420\u0430\u0437\u043c\u0435\u0440\u044b", "Disc": "\u0434\u0438\u0441\u043a", "Discard Changes": "\u0421\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f", diff --git a/cms/static/js/i18n/sk/djangojs.js b/cms/static/js/i18n/sk/djangojs.js index 667f58c9c7..ab0fce0ba9 100644 --- a/cms/static/js/i18n/sk/djangojs.js +++ b/cms/static/js/i18n/sk/djangojs.js @@ -153,7 +153,6 @@ "Deleted Group": "Zmazan\u00e1 skupina", "Deleting this %(item_display_name)s is permanent and cannot be undone.": "Odstr\u00e1nenie %(item_display_name)s je trval\u00e9 a nie je mo\u017en\u00e9 ho vr\u00e1ti\u0165 sp\u00e4\u0165.", "Description": "Popis", - "Details": "Podrobnosti", "Dimensions": "Rozmery", "Disc": "Disk", "Discard Changes": "Zahodi\u0165 zmeny", diff --git a/cms/static/js/i18n/tr-tr/djangojs.js b/cms/static/js/i18n/tr-tr/djangojs.js index 8e097b9d5e..2c89b57353 100644 --- a/cms/static/js/i18n/tr-tr/djangojs.js +++ b/cms/static/js/i18n/tr-tr/djangojs.js @@ -121,7 +121,6 @@ "(Caption will be displayed when you start playing the video.)": "(Altyaz\u0131 videoyu oynatmaya ba\u015flad\u0131\u011f\u0131n\u0131zda g\u00f6sterilecek.)", "(Community TA)": "(Topluluk Asistan\u0131)", "(Optional)": "(\u0130ste\u011fe Ba\u011fl\u0131)", - "(Read-only)": "(Salt-okunur)", "(Required Field)": "(Gerekli Alan)", "(Self-paced) Ended {end}": "(Kendi kendine) Bitti {end}", "(Self-paced) Ends {end}": "(Kendi kendine) Biti\u015f {end}", @@ -270,7 +269,6 @@ "An error has occurred. Wait a few minutes, and then try again.": "Bir hata olu\u015ftu. Birka\u00e7 dakika bekleyin, ve ondan sonra tekrar deneyin.", "An error occurred retrieving your email. Please try again later, and contact technical support if the problem persists.": "E-posta adresinizi al\u0131rken bir hata olu\u015ftu. L\u00fctfen daha sonra tekrar deneyiniz, ve e\u011fer problem devam ederse teknik destek ile ileti\u015fime ge\u00e7iniz.", "An error occurred when signing you in to %s.": "%s ortam\u0131nda oturum a\u00e7arken hata olu\u015ftu.", - "An error occurred while attempting to retrieve or save the information below. Please try again later.": "A\u015fa\u011f\u0131daki bilgiyi \u00e7ekmeye ya da kaydetmeye \u00e7al\u0131\u015f\u0131rken bir hata ger\u00e7ekle\u015fti. Bir s\u00fcre sonra l\u00fctfen tekrar deneyin.", "An error occurred while looking up team membership. Try refreshing the page.": "Tak\u0131m \u00fcyeli\u011fine aran\u0131rken bir hata olu\u015ftu. Sayfay\u0131 yenilemeyi deneyin.", "An error occurred while removing the member from the team. Try again.": "Tak\u0131mdan \u00fcyeyi \u00e7\u0131kart\u0131rken bir hata olu\u015ftu. Tekrar deneyin.", "An error occurred.": "Bir hata meydana geldi.", @@ -356,7 +354,6 @@ "Bottom": "Alt", "Browse": "G\u00f6z at", "Browse recently launched courses and see what\\'s new in your favorite subjects": "Yeni a\u00e7\u0131lm\u0131\u015f dersleri taray\u0131n ve ilginizi \u00e7eken konularda ne yenilikler var g\u00f6r\u00fcnt\u00fcleyin", - "Browsing": "G\u00f6z at\u0131l\u0131yor", "Bulk Exceptions": "Toplu \u0130stisnalar", "Bullet list": "Madde imli liste", "Bulleted List (Ctrl+U)": "Noktal\u0131 Liste (Ctrl+U)", @@ -531,9 +528,7 @@ "Course Name": "Ders \u0130smi", "Course Number": "Ders Numaras\u0131", "Course Number Override": "Ders Numaras\u0131n\u0131 Ge\u00e7ersiz K\u0131l", - "Course Number:": "Ders Numaras\u0131:", "Course Outline": "Ders Tasla\u011f\u0131", - "Course Run:": "Ders Yay\u0131n\u0131:", "Course Start": "Ders Ba\u015flang\u0131c\u0131", "Course Title": "Ders Ba\u015fl\u0131\u011f\u0131", "Course Title Override": "Ders Ba\u015fl\u0131\u011f\u0131n\u0131 Ge\u00e7ersiz K\u0131l", @@ -545,7 +540,6 @@ "Create": "Olu\u015ftur", "Create Account": "Hesap Olu\u015ftur", "Create Re-run": "Yeniden \u00c7al\u0131\u015ft\u0131rma Olu\u015ftur", - "Create Support Ticket": "Destek Talebi Olu\u015ftur", "Create a New Team": "Yeni Bir Tak\u0131m Olu\u015ftur", "Create a content group": "\u0130\u00e7erik grubu yarat", "Create a new team if you can't find an existing team to join, or if you would like to learn with friends you know.": "Kat\u0131lacak bir tak\u0131m bulamad\u0131ysan\u0131z, ya da tan\u0131d\u0131\u011f\u0131n\u0131z arkada\u015flar\u0131n\u0131zla \u00f6\u011frenmek istiyorsan\u0131z yeni bir tak\u0131m olu\u015fturun.", @@ -614,7 +608,6 @@ "Describe ": "Tan\u0131mla", "Description": "A\u00e7\u0131klama", "Description of the certificate": "Sertifikan\u0131n a\u00e7\u0131klamas\u0131", - "Details": "Ayr\u0131nt\u0131lar", "Device with Camera": "Kameral\u0131 Cihaz", "Dimensions": "Boyutlar", "Disc": "Disk", @@ -819,7 +812,6 @@ "Find next": "Sonrakini bul", "Find previous": "\u00d6ncekini bul", "Finish": "Bitir", - "Finish later": "Sonra tamamla", "First time here?": "\u0130lk kez mi buradas\u0131n\u0131z?", "Follow": "Takip et", "Follow or unfollow posts": "G\u00f6nderileri takip et veya takipten \u00e7\u0131k", @@ -873,7 +865,6 @@ "H Align": "H Hizalama", "HTML preview of post": "G\u00f6nderinin HTML \u00f6nizlemesi", "HTML source code": "HTML kaynak kodu", - "Have you ever served on active duty in the U.S. Armed Forces, Reserves, or National Guard?": "Bug\u00fcne dek ABD Ordusu, Yedek Kuvvetler ya da Ulusal Muhaf\u0131zlarda hizmet ettiniz mi?", "Header": "\u00dcst bilgi ", "Header 1": "\u00dcst bilgi 1", "Header 2": "\u00dcst bilgi 2", @@ -1071,7 +1062,6 @@ "Loading more threads": "Daha fazla ileti dizisi y\u00fckl\u00fcyor", "Loading posts list": "\u0130leti listesi y\u00fckleniyor", "Loading your courses": "Dersleriniz y\u00fckleniyor", - "Loading...": "Y\u00fckl\u00fcyor...", "Location": "Yer", "Location in Course": "Dersteki Konum", "Lock this asset": "Bu veriyi kitle", @@ -1131,7 +1121,6 @@ "Name of the signatory": "\u0130mza sahibinin ad\u0131", "Name or short description of the configuration": "Yap\u0131land\u0131rman\u0131n ad\u0131 ya da k\u0131sa a\u00e7\u0131klamas\u0131", "Navigate up": "Yukar\u0131 git", - "Need help logging in?": "Oturum a\u00e7mak i\u00e7in yard\u0131ma m\u0131 ihtiyac\u0131n\u0131z var?", "Need help signing in?": "Giri\u015f yapmak i\u00e7in yard\u0131ma m\u0131 ihtiyac\u0131n\u0131z var?", "Need other help signing in?": "Giri\u015f yapmak i\u00e7in farkl\u0131 bir yard\u0131ma m\u0131 ihtiyac\u0131n\u0131z var?", "Needs verified certificate ": "Onayl\u0131 sertifika gerekiyor", @@ -1238,7 +1227,6 @@ "Organization ": "Kurum", "Organization Name": "Kurum Ad\u0131", "Organization of the signatory": "\u0130mza sahibinin kurumu", - "Organization:": "Kurum:", "Other": "Di\u011fer", "Other sign-in issues": "Di\u011fer giri\u015f sorunlar\u0131", "Overall Score": "Toplam Skor", @@ -1382,7 +1370,6 @@ "Questions raise issues that need answers. Discussions share ideas and start conversations. (Required)": "Sorular cevap gerektiren konular\u0131 ileri s\u00fcrer. Tart\u0131\u015fmalar fikirleri payla\u015f\u0131r ve sohbeti ba\u015flat\u0131r. (Gerekli)", "Queued": "Kuyru\u011fa Al\u0131nd\u0131", "REMAINING COURSES": "KALAN DERSLER", - "Re-run Course": "Dersi Yeniden \u00c7al\u0131\u015ft\u0131r", "Read More": "Daha Fazlas\u0131 \u0130\u00e7in", "Read more": "Daha fazlas\u0131", "Ready To Start": "Ba\u015flamaya Haz\u0131r", @@ -1458,7 +1445,6 @@ "Return to Export": "D\u0131\u015fa Aktarmaya Geri D\u00f6n", "Return to Your Dashboard": "Panele Geri D\u00f6n", "Return to all posts": "T\u00fcm g\u00f6nderilere d\u00f6n", - "Return to my dashboard": "Panelime geri d\u00f6n", "Return to team listing": "Tak\u0131m listesine geri d\u00f6n", "Review Policy Exception": "\u0130nceleme Politikas\u0131 \u0130stisnas\u0131", "Review Rules": "Kurallar\u0131 G\u00f6zden Ge\u00e7ir", @@ -1509,12 +1495,10 @@ "Select a subject for your support request.": "Destek talebiniz i\u00e7in bir konu se\u00e7in.", "Select a time allotment for the exam. If it is over 24 hours, type in the amount of time. You can grant individual learners extra time to complete the exam through the Instructor Dashboard.": "S\u0131nav i\u00e7in ayr\u0131lan s\u00fcreyi se\u00e7in. E\u011fer 24 saatten fazlaysa, s\u00fcreyi yazarak girin. E\u011fitmen Paneli arac\u0131l\u0131\u011f\u0131yla belli \u00f6\u011frencilere s\u0131nav\u0131 tamamlamalar\u0131 i\u00e7in daha fazla s\u00fcre tan\u0131mlayabilirsiniz.", "Select all": "T\u00fcm\u00fcn\u00fc se\u00e7", - "Select employment status": "Meslek durumunu se\u00e7in", "Select language": "Dil Se\u00e7imi", "Select one or more groups:": "Bir ya da daha fazla grup se\u00e7in:", "Select the course-wide discussion topics that you want to divide.": "B\u00f6lmek istedi\u011finiz ders-geneli tart\u0131\u015fma konular\u0131n\u0131 se\u00e7in.", "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browser's local time zone.": "Ders tarihleri g\u00f6sterimi i\u00e7in saat dilimini se\u00e7in. Herhangi bir saat dilimini se\u00e7memeniz durumunda, g\u00f6rev teslimleri d\u00e2hil olmak \u00fczere t\u00fcm ders tarihleri, web taray\u0131c\u0131n\u0131z\u0131n yerel zaman\u0131na g\u00f6re g\u00f6sterilecektir. ", - "Selected blocks": "Se\u00e7ili blocklar", "Selected tab": "Se\u00e7ili sekme", "Self": "Kendin", "Send to:": "G\u00f6nderilecek ki\u015fi:", @@ -2111,17 +2095,11 @@ "Weight of Total Grade": "Toplam Not A\u011f\u0131rl\u0131\u011f\u0131", "We\u2019re sorry to see you go!": "Gitti\u011finizi g\u00f6rmek bizi \u00fcz\u00fcyor!", "What You Need for Verification": "Do\u011frulama \u0130\u00e7in Gerekenler", - "What can we help you with, {username}?": "Sana nas\u0131l yard\u0131mc\u0131 olabiliriz, {username}?", "What does %(platformName)s do with this photo?": "%(platformName)s bu foto\u011fraf ile ne yapar?", "What does this mean?": "Ne anlama geliyor?", "What if I can't see the camera image, or if I can't see my photo do determine which side is visible?": "Ya kamera g\u00f6r\u00fcnt\u00fcs\u00fcn\u00fc g\u00f6remezsem veya hangi taraf\u0131n g\u00f6r\u00fcn\u00fcr oldu\u011funu belirlemeye \u00e7al\u0131\u015f\u0131rken foto\u011fraf\u0131m\u0131 g\u00f6remezsem?", "What if I have difficulty holding my ID in position relative to the camera?": "Kimli\u011fimi kameraya g\u00f6re yerinde tutmakta zorluk \u00e7ekersem ne olur? ", "What if I have difficulty holding my head in position relative to the camera?": "Ba\u015f\u0131m\u0131 kameraya g\u00f6re pozisyonda tutmakta zorluk \u00e7ekiyorsam ne olur? ", - "What industry do you currently work in?": "\u015eu anda hangi sekt\u00f6rde \u00e7al\u0131\u015f\u0131yorsunuz?", - "What industry do you want to work in?": "Gelecekte hangi sekt\u00f6rde \u00e7al\u0131\u015fmak istiyorsunuz?", - "What is the highest level of education that any of your parents or guardians have achieved?": "Ebeveyn ya da velilerinizin \u015fimdiye kadar ula\u015ft\u0131\u011f\u0131 en y\u00fcksek e\u011fitim seviyesi nedir?", - "What is the highest level of education that you have achieved so far?": "\u015eimdiye kadar ula\u015ft\u0131\u011f\u0131n\u0131z en y\u00fcksek e\u011fitim seviyesi nedir?", - "What was the total combined income, during the last 12 months, of all members of your family? ": "T\u00fcm aile \u00fcyelerinizin son 12 ayl\u0131k toplam geliri, ne kadard\u0131?", "What's Your Next Accomplishment?": "Bir Sonraki Ba\u015far\u0131n Ne Olacak?", "When learners submit an answer to an assessment, they immediately see whether the answer is correct or incorrect, and the score received.": "\u00d6\u011frenciler bir de\u011ferlendirmeye cevap g\u00f6nderdi\u011finde, de\u011ferlendirmedeki cevaplar\u0131n\u0131n do\u011fru ya da yanl\u0131\u015f olup olmad\u0131\u011f\u0131n\u0131 ve ald\u0131klar\u0131 notu hemen g\u00f6rebilirler.", "When your face is in position, use the Take Photo button {icon} below to take your photo.": "Y\u00fcz\u00fcn\u00fcz \u00e7er\u00e7eve i\u00e7indeyken, a\u015fa\u011f\u0131daki Foto\u011fraf \u00c7ek {icon} d\u00fc\u011fmesini kullanarak foto\u011fraf\u0131n\u0131z\u0131 \u00e7ekin.", @@ -2130,7 +2108,6 @@ "Whole words": "Tam s\u00f6zc\u00fckler", "Why activate?": "Neden etkinle\u015ftirme ad\u0131m\u0131?", "Why does %(platformName)s need my photo?": "Ni\u00e7in %(platformName)s benim foto\u011fraf\u0131ma gerek duyuyor?", - "Why does edX collect this information?": "edX neden bu bilgiyi topluyor?", "Width": "Geni\u015flik", "Will Be Visible To:": "\u015eu Ki\u015filere G\u00f6r\u00fcnecek:", "Words: {0}": "Kelimeler: {0}", @@ -2327,7 +2304,6 @@ "endorsed %(time_ago)s by %(user)s": "%(user)s kullan\u0131c\u0131 taraf\u0131ndan %(time_ago)s \u00f6nce desteklenmi\u015f", "enter code here": "kodu buraya girin", "enter link description here": "ba\u011flant\u0131 a\u00e7\u0131klamas\u0131n\u0131 buraya girin", - "finish later": "sonra tamamla", "follow this post": "bu iletiyi takip et", "for": "i\u00e7in", "group configuration": "grup ayar\u0131", @@ -2341,7 +2317,6 @@ "minute": "dakika", "minutes": "dakika", "name": "isim", - "next page": "sonraki sayfa", "off": "kapal\u0131", "on": "a\u00e7\u0131k", "one letter Friday\u0004F": "C", @@ -2370,7 +2345,6 @@ "strong text": "kal\u0131n metin", "subsection": "altb\u00f6l\u00fcm", "team count": "tak\u0131m say\u0131s\u0131", - "the more quickly and helpfully we can respond!": "o kadar h\u0131zl\u0131 ve yard\u0131mc\u0131 bir \u015fekilde yan\u0131t verebiliriz!", "unanswered question": "cevaplanmayan soru", "unit": "\u00fcnite", "unsubmitted": "g\u00f6nderilmedi", diff --git a/cms/static/js/i18n/uk/djangojs.js b/cms/static/js/i18n/uk/djangojs.js index 88de8b4656..6d1bd8c71b 100644 --- a/cms/static/js/i18n/uk/djangojs.js +++ b/cms/static/js/i18n/uk/djangojs.js @@ -84,7 +84,6 @@ "(Add signatories for a certificate)": "(\u0414\u043e\u0434\u0430\u0442\u0438 \u043f\u0456\u0434\u043f\u0438\u0441\u0430\u043d\u0442\u0456\u0432 \u0434\u043b\u044f \u0441\u0435\u0440\u0442\u0438\u0444\u0456\u043a\u0430\u0442\u0443)", "(Caption will be displayed when you start playing the video.)": "(\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u0438 \u0432\u0456\u0434\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u0438\u043c\u0443\u0442\u044c\u0441\u044f, \u043a\u043e\u043b\u0438 \u0412\u0438 \u043f\u043e\u0447\u043d\u0435\u0442\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u0432\u0430\u0442\u0438 \u0432\u0456\u0434\u0435\u043e.)", "(Optional)": "(\u043d\u0435\u043e\u0431\u043e\u0432'\u044f\u0437\u043a\u043e\u0432\u043e)", - "(Read-only)": "(\u041b\u0438\u0448\u0435 \u0434\u043b\u044f \u0447\u0438\u0442\u0430\u043d\u043d\u044f)", "(Required Field)": "(\u041e\u0431\u043e\u0432'\u044f\u0437\u043a\u043e\u0432\u0435 \u043f\u043e\u043b\u0435)", "(Self-paced) Ended {end}": " (\u0421\u0430\u043c\u043e\u0441\u0442\u0456\u0439\u043d\u0456 \u043a\u0443\u0440\u0441\u0438) \u0417\u0430\u043a\u0456\u043d\u0447\u0438\u043b\u0438\u0441\u044f {end}", "(Self-paced) Ends {end}": "\u0417\u0430\u043a\u0456\u043d\u0447\u0443\u044e\u0442\u044c\u0441\u044f {end} (\u0421\u0430\u043c\u043e\u0441\u0442\u0456\u0439\u043d\u0456 \u043a\u0443\u0440\u0441\u0438) ", @@ -340,7 +339,6 @@ "Course Name": "\u041d\u0430\u0437\u0432\u0430 \u043a\u0443\u0440\u0441\u0443", "Course Number": "\u041d\u043e\u043c\u0435\u0440 \u041a\u0443\u0440\u0441\u0443", "Course Number Override": "\u0417\u0430\u043c\u0456\u043d\u0438\u0442\u0438 \u043d\u043e\u043c\u0435\u0440 \u043a\u0443\u0440\u0441\u0443", - "Course Number:": "\u041d\u043e\u043c\u0435\u0440 \u041a\u0443\u0440\u0441\u0443:", "Course Outline": "\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043a\u0443\u0440\u0441\u0443", "Course Title": "\u041d\u0430\u0437\u0432\u0430 \u043a\u0443\u0440\u0441\u0443", "Course Title Override": "\u0417\u0430\u043c\u0456\u043d\u0438\u0442\u0438 \u043d\u0430\u0437\u0432\u0443 \u043a\u0443\u0440\u0441\u0443", @@ -389,7 +387,6 @@ "Deleting this {xblock_type} is permanent and cannot be undone.": "\u0412\u0438\u0434\u0430\u043b\u0435\u043d\u043d\u044f \u0446\u044c\u043e\u0433\u043e {xblock_type} \u0454 \u0431\u0435\u0437\u043f\u043e\u0432\u043e\u0440\u043e\u0442\u043d\u0456\u043c \u0456 \u043d\u0435 \u043c\u043e\u0436\u0435 \u0431\u0443\u0442\u0438 \u0441\u043a\u0430\u0441\u043e\u0432\u0430\u043d\u0435. ", "Describe ": "\u041e\u043f\u0438\u0448\u0456\u0442\u044c", "Description": "\u041e\u043f\u0438\u0441", - "Details": "\u0414\u0435\u0442\u0430\u043b\u0456", "Dimensions": "\u0420\u043e\u0437\u043c\u0456\u0440\u043d\u0456\u0441\u0442\u044c", "Disc": "\u0414\u0438\u0441\u043a", "Discard Changes": "\u0421\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438 \u0437\u043c\u0456\u043d\u0438", diff --git a/cms/static/js/i18n/vi/djangojs.js b/cms/static/js/i18n/vi/djangojs.js index 9800b0eeea..986d87a76b 100644 --- a/cms/static/js/i18n/vi/djangojs.js +++ b/cms/static/js/i18n/vi/djangojs.js @@ -59,7 +59,6 @@ "(Add signatories for a certificate)": "(Th\u00eam ng\u01b0\u1eddi k\u00fd cho ch\u1ee9ng ch\u1ec9)", "(Caption will be displayed when you start playing the video.)": "(Ph\u1ee5 \u0111\u1ec1 s\u1ebd \u0111\u01b0\u1ee3c hi\u1ec3n th\u1ecb khi b\u1ea1n b\u1eaft \u0111\u1ea7u ph\u00e1t video)", "(Optional)": "(Kh\u00f4ng b\u1eaft bu\u1ed9c)", - "(Read-only)": "(Ch\u1ec9-\u0111\u1ecdc)", "(Required Field)": "(Th\u00f4ng tin b\u1eaft bu\u1ed9c)", "(Self-paced) Ended {end}": "(Self-paced) \u0110\u00e3 k\u1ebft th\u00fac {end}", "(Self-paced) Ends {end}": "(Self-paced) K\u1ebft th\u00fac {end}", @@ -360,9 +359,7 @@ "Course Name": "T\u00ean Kh\u00f3a H\u1ecdc", "Course Number": "S\u1ed1 Kh\u00f3a H\u1ecdc", "Course Number Override": "Ch\u00e8n l\u00ean s\u1ed1 th\u1ee9 t\u1ef1 kh\u00f3a h\u1ecdc", - "Course Number:": "M\u00e3 kh\u00f3a h\u1ecdc:", "Course Outline": "Ph\u00e1c Th\u1ea3o Kh\u00f3a H\u1ecdc", - "Course Run:": "Ch\u1ea1y kh\u00f3a h\u1ecdc", "Course Title": "Kh\u00f3a h\u1ecdc:", "Course Title Override": "Ch\u00e8n l\u00ean t\u00ean kh\u00f3a h\u1ecdc", "Course Video Settings": "Thi\u1ebft l\u1eadp Video Kh\u00f3a h\u1ecdc", @@ -419,7 +416,6 @@ "Deprecated": "\u0110\u00e3 l\u1ed7i th\u1eddi", "Description": "M\u00f4 t\u1ea3", "Description of the certificate": "M\u00f4 t\u1ea3 ch\u1ee9ng ch\u1ec9", - "Details": "Chi ti\u1ebft", "Dimensions": "K\u00edch th\u01b0\u1edbc", "Disc": "\u0110\u0129a", "Discard Changes": "X\u00f3a Thay \u0110\u1ed5i", @@ -886,7 +882,6 @@ "Organization ": "T\u1ed5 ch\u1ee9c", "Organization Name": "T\u00ean t\u1ed5 ch\u1ee9c", "Organization of the signatory": "T\u1ed5 ch\u1ee9c c\u1ee7a ng\u01b0\u1eddi k\u00fd", - "Organization:": "T\u1ed5 ch\u1ee9c:", "Other": "L\u1ef1a ch\u1ecdn kh\u00e1c", "Overall Score": "T\u1ed5ng \u0110i\u1ec3m", "Page break": "Ng\u1eaft trang", @@ -990,7 +985,6 @@ "Publishing": "Ph\u00e1t h\u00e0nh", "Publishing Status": "Tr\u1ea1ng th\u00e1i Xu\u1ea5t b\u1ea3n", "Queued": "\u0110\u00e3 x\u1ebfp h\u00e0ng", - "Re-run Course": "Ch\u1ea1y l\u1ea1i Kh\u00f3a h\u1ecdc", "Read More": "Xem Th\u00eam", "Reason field should not be left blank.": "Vui l\u00f2ng \u0111i\u1ec1n v\u00e0o m\u1ee5c L\u00fd do.", "Receive updates": "Nh\u1eadn th\u00f4ng b\u00e1o khi c\u00f3 b\u00e0i \u0111\u0103ng m\u1edbi", @@ -1543,7 +1537,6 @@ "Weight of Total Grade": "S\u0129 s\u1ed1 c\u1ee7a l\u1edbp", "We\u2019re sorry to see you go!": "Ch\u00fang t\u00f4i r\u1ea5t ti\u1ebfc khi th\u1ea5y b\u1ea1n ra \u0111i!", "What You Need for Verification": "Nh\u1eefng g\u00ec b\u1ea1n c\u1ea7n \u0111\u1ec3 x\u00e1c minh", - "What can we help you with, {username}?": "Ch\u00fang t\u00f4i c\u00f3 th\u1ec3 gi\u00fap g\u00ec cho b\u1ea1n, {username}?", "What does this mean?": "\u0110i\u1ec1u n\u00e0y c\u00f3 ngh\u0129a l\u00e0 g\u00ec?", "When learners submit an answer to an assessment, they immediately see whether the answer is correct or incorrect, and the score received.": "Khi h\u1ecdc vi\u00ean g\u1eedi c\u00e2u tr\u1ea3 l\u1eddi cho m\u1ed9t b\u00e0i \u0111\u00e1nh gi\u00e1, h\u1ecd s\u1ebd ngay l\u1eadp t\u1ee9c \u0111\u01b0\u1ee3c xem c\u00e2u tr\u1ea3 l\u1eddi l\u00e0 ch\u00ednh x\u00e1c hay kh\u00f4ng, v\u00e0 \u0111i\u1ec3m s\u1ed1 nh\u1eadn \u0111\u01b0\u1ee3c.", "Which timed transcript would you like to use?": "B\u1ea1n mu\u1ed1n s\u1eed d\u1ee5ng b\u1ea3n d\u1ecbch n\u00e0o?", diff --git a/cms/static/js/i18n/zh-cn/djangojs.js b/cms/static/js/i18n/zh-cn/djangojs.js index 93efdd50ee..051cee1b7c 100644 --- a/cms/static/js/i18n/zh-cn/djangojs.js +++ b/cms/static/js/i18n/zh-cn/djangojs.js @@ -95,7 +95,6 @@ "(Add signatories for a certificate)": "(\u6dfb\u52a0\u8bc1\u4e66\u4e2d\u7684\u7b7e\u7f72\u65b9)", "(Caption will be displayed when you start playing the video.)": "(\u5f00\u59cb\u64ad\u653e\u89c6\u9891\u65f6\u5c06\u663e\u793a\u5b57\u5e55)", "(Optional)": "(\u975e\u5fc5\u586b)", - "(Read-only)": "\uff08\u53ea\u8bfb\uff09", "(Required Field)": "(\u5fc5\u586b\u5b57\u6bb5)", "(Self-paced) Ended {end}": "(\u81ea\u4e3b\u6a21\u5f0f) \u7ed3\u675f {end}", "(Self-paced) Ends {end}": "(\u81ea\u4e3b\u6a21\u5f0f) \u7ed3\u675f {end}", @@ -308,7 +307,6 @@ "Bottom": "\u5e95\u7aef", "Browse": "\u6d4f\u89c8", "Browse recently launched courses and see what\\'s new in your favorite subjects": "\u6d4f\u89c8\u6700\u65b0\u4e0a\u7ebf\u7684\u8bfe\u7a0b\u5e76\u67e5\u770b\u60a8\u6700\u559c\u7231\u79d1\u76ee\u7684\u66f4\u65b0\u60c5\u51b5", - "Browsing": "\u6d4f\u89c8", "Bulk Exceptions": "\u6279\u91cf\u7279\u6b8a\u5904\u7406", "Bullet list": "\u9879\u76ee\u7b26\u53f7\u5217\u8868", "Bulleted List (Ctrl+U)": "\u7b26\u53f7\u5217\u8868(Ctrl+U)", @@ -464,9 +462,7 @@ "Course Name": "\u8bfe\u7a0b\u540d\u79f0", "Course Number": "\u8bfe\u7a0b\u4ee3\u7801", "Course Number Override": "\u91cd\u5199\u8bfe\u7a0b\u7f16\u53f7", - "Course Number:": "\u8bfe\u7a0b\u7f16\u53f7\uff1a", "Course Outline": "\u8bfe\u7a0b\u5927\u7eb2", - "Course Run:": "\u8bfe\u7a0b\u957f\u5ea6\uff1a", "Course Start": "\u8bfe\u7a0b\u5f00\u59cb", "Course Title": "\u8bfe\u7a0b\u6807\u9898", "Course Title Override": "\u91cd\u5199\u8bfe\u7a0b\u6807\u9898", @@ -537,7 +533,6 @@ "Describe ": "\u63cf\u8ff0", "Description": "\u63cf\u8ff0", "Description of the certificate": "\u8ba4\u8bc1\u63cf\u8ff0", - "Details": "\u7ec6\u8282", "Dimensions": "\u5c3a\u5bf8", "Disc": "\u5b9e\u5fc3\u5706", "Discard Changes": "\u653e\u5f03\u66f4\u6539", @@ -1089,7 +1084,6 @@ "Organization ": "\u673a\u6784", "Organization Name": "\u673a\u6784\u540d\u79f0", "Organization of the signatory": "\u7ec4\u7ec7\u673a\u6784\u7b7e\u7ae0", - "Organization:": "\u673a\u6784\uff1a", "Other": "\u5176\u4ed6", "Overall Score": "\u603b\u6210\u7ee9", "PDF Chapters": "PDF\u5404\u7ae0\u8282", @@ -1219,7 +1213,6 @@ "Publishing Status": "\u53d1\u5e03\u72b6\u6001", "Queued": "\u5df2\u6392\u961f", "REMAINING COURSES": "\u5269\u4f59\u8bfe\u7a0b", - "Re-run Course": "\u91cd\u542f\u8bfe\u7a0b", "Read More": "\u4e86\u89e3\u66f4\u591a", "Read more": "\u4e86\u89e3\u66f4\u591a", "Ready To Start": "\u51c6\u5907\u5f00\u59cb", @@ -1850,7 +1843,7 @@ "View Archived Course": "\u67e5\u770b\u5b58\u6863\u7684\u8bfe\u7a0b", "View Cohort": "\u67e5\u770b\u7fa4\u7ec4", "View Course": "\u67e5\u770b\u8bfe\u7a0b", - "View Live": "\u67e5\u770b\u7ebf\u4e0a\u7248\u672c", + "View Live": "\u67e5\u770b\u5728\u7ebf\u7248", "View Program Record": "\u67e5\u770b\u8bfe\u7a0b\u8bb0\u5f55", "View all errors": "\u67e5\u770b\u6240\u6709\u9519\u8bef", "View child items": "\u67e5\u770b\u5b50\u7c7b\u76ee", @@ -1884,7 +1877,6 @@ "Weight of Total Grade": "\u5728\u603b\u8bc4\u5206\u4e2d\u7684\u6743\u91cd", "We\u2019re sorry to see you go!": "\u5f88\u9057\u61be\u770b\u5230\u60a8\u8981\u79bb\u5f00\uff01", "What You Need for Verification": "\u8ba4\u8bc1\u6240\u9700", - "What can we help you with, {username}?": "\u4eb2\u7231\u7684{username}\uff0c\u6709\u4ec0\u4e48\u53ef\u4ee5\u5e2e\u52a9\u60a8\uff1f", "What does %(platformName)s do with this photo?": "%(platformName)s\u7528\u8fd9\u5f20\u7167\u7247\u505a\u4ec0\u4e48\uff1f", "What does this mean?": "\u8fd9\u662f\u4ec0\u4e48\u610f\u601d\uff1f", "What's Your Next Accomplishment?": "\u60a8\u7684\u4e0b\u4e00\u4e2a\u76ee\u6807\u662f\u4ec0\u4e48\uff1f", diff --git a/cms/static/js/views/pages/container.js b/cms/static/js/views/pages/container.js index 286111e7f0..347f7ea77b 100644 --- a/cms/static/js/views/pages/container.js +++ b/cms/static/js/views/pages/container.js @@ -187,15 +187,15 @@ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/pages/base_page event.preventDefault(); if(!options || options.view !== 'visibility_view' ){ - var useNewTextEditor = this.$('.edit-button').attr("use-new-editor-text"), - useNewVideoEditor = this.$('.edit-button').attr("use-new-editor-video"), - useNewProblemEditor = this.$('.edit-button').attr("use-new-editor-problem"), - blockType = xblockElement.find('.xblock').attr("data-block-type"); + var useNewTextEditor = this.$('.xblock-header-primary').attr("use-new-editor-text"), + useNewVideoEditor = this.$('.xblock-header-primary').attr("use-new-editor-video"), + useNewProblemEditor = this.$('.xblock-header-primary').attr("use-new-editor-problem"), + blockType = xblockElement.find('.xblock').attr("data-block-type"); if( (useNewTextEditor === "True" && blockType === "html") || (useNewVideoEditor === "True" && blockType === "video") || (useNewProblemEditor === "True" && blockType === "problem") ) { - var destinationUrl = this.$('.edit-button').attr("authoring_MFE_base_url") + '/' + blockType + '/' + encodeURI(xblockElement.find('.xblock').attr("data-usage-id")); + var destinationUrl = this.$('.xblock-header-primary').attr("authoring_MFE_base_url") + '/' + blockType + '/' + encodeURI(xblockElement.find('.xblock').attr("data-usage-id")); window.location.href = destinationUrl; return; } @@ -322,6 +322,23 @@ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/pages/base_page }, onNewXBlock: function(xblockElement, scrollOffset, is_duplicate, data) { + var useNewTextEditor = this.$('.xblock-header-primary').attr("use-new-editor-text"), + useNewVideoEditor = this.$('.xblock-header-primary').attr("use-new-editor-video"), + useNewProblemEditor = this.$('.xblock-header-primary').attr("use-new-editor-problem"); + + //find the block type in the locator if availible + if(data.hasOwnProperty('locator')){ + var matchBlockTypeFromLocator = /\@(.*?)\+/; + var blockType = data.locator.match(matchBlockTypeFromLocator); + } + if((useNewTextEditor === "True" && blockType.includes("html")) || + (useNewVideoEditor === "True" && blockType.includes("video"))|| + (useNewProblemEditor === "True" && blockType.includes("problem")) + ){ + var destinationUrl = this.$('.xblock-header-primary').attr("authoring_MFE_base_url") + '/' + blockType[1] + '/' + encodeURI(data.locator); + window.location.href = destinationUrl; + return; + } ViewUtils.setScrollOffset(xblockElement, scrollOffset); xblockElement.data('locator', data.locator); return this.refreshXBlock(xblockElement, true, is_duplicate); diff --git a/cms/templates/studio_xblock_wrapper.html b/cms/templates/studio_xblock_wrapper.html index 5f3b5bf875..1bd2c3070e 100644 --- a/cms/templates/studio_xblock_wrapper.html +++ b/cms/templates/studio_xblock_wrapper.html @@ -59,7 +59,12 @@ block_is_unit = is_unit(xblock) % if not show_preview: is-collapsed % endif - "> + " + use-new-editor-text = ${use_new_editor_text} + use-new-editor-video = ${use_new_editor_video} + use-new-editor-problem = ${use_new_editor_problem} + authoring_MFE_base_url = ${get_editor_page_base_url(xblock.location.course_key)} + >
% if show_inline: @@ -80,12 +85,7 @@ block_is_unit = is_unit(xblock) % if can_edit: % if not show_inline:
  • - diff --git a/common/djangoapps/student/signals/__init__.py b/common/djangoapps/student/signals/__init__.py index 0ae2390011..9d134198f0 100644 --- a/common/djangoapps/student/signals/__init__.py +++ b/common/djangoapps/student/signals/__init__.py @@ -4,5 +4,6 @@ from common.djangoapps.student.signals.signals import ( ENROLL_STATUS_CHANGE, ENROLLMENT_TRACK_UPDATED, REFUND_ORDER, - UNENROLL_DONE + UNENROLL_DONE, + USER_EMAIL_CHANGED ) diff --git a/common/djangoapps/student/signals/receivers.py b/common/djangoapps/student/signals/receivers.py index c05813dc2d..6d0cf4f061 100644 --- a/common/djangoapps/student/signals/receivers.py +++ b/common/djangoapps/student/signals/receivers.py @@ -3,7 +3,8 @@ Signal receivers for the "student" application. """ # pylint: disable=unused-argument - +import logging +from asyncio.log import logger from django.conf import settings from django.contrib.auth import get_user_model from django.db import IntegrityError @@ -11,6 +12,7 @@ from django.db.models.signals import post_save, pre_save from django.dispatch import receiver from lms.djangoapps.courseware.toggles import courseware_mfe_progress_milestones_are_active +from lms.djangoapps.utils import get_braze_client from common.djangoapps.student.helpers import EMAIL_EXISTS_MSG_FMT, USERNAME_EXISTS_MSG_FMT, AccountValidationError from common.djangoapps.student.models import ( CourseEnrollment, @@ -20,8 +22,11 @@ from common.djangoapps.student.models import ( is_username_retired ) from common.djangoapps.student.models_api import confirm_name_change +from common.djangoapps.student.signals import USER_EMAIL_CHANGED from openedx.features.name_affirmation_api.utils import is_name_affirmation_installed +logger = logging.getLogger(__name__) + @receiver(pre_save, sender=get_user_model()) def on_user_updated(sender, instance, **kwargs): @@ -97,3 +102,18 @@ if is_name_affirmation_installed(): # pylint: disable=import-error from edx_name_affirmation.signals import VERIFIED_NAME_APPROVED VERIFIED_NAME_APPROVED.connect(listen_for_verified_name_approved) + + +@receiver(USER_EMAIL_CHANGED) +def _listen_for_user_email_changed(sender, user, **kwargs): + """ If user has changed their email, update that in email Braze. """ + email = user.email + user_id = user.id + attributes = [{'email': email, 'external_id': user_id}] + + try: + braze_client = get_braze_client() + if braze_client: + braze_client.track_user(attributes=attributes) + except Exception as exc: # pylint: disable=broad-except + logger.exception(f'Unable to sync new email [{email}] with Braze for user [{user_id}]') diff --git a/common/djangoapps/student/signals/signals.py b/common/djangoapps/student/signals/signals.py index 0682bb5ea9..49745ca48c 100644 --- a/common/djangoapps/student/signals/signals.py +++ b/common/djangoapps/student/signals/signals.py @@ -19,3 +19,5 @@ ENROLL_STATUS_CHANGE = Signal() # providing_args=["course_enrollment"] REFUND_ORDER = Signal() + +USER_EMAIL_CHANGED = Signal() diff --git a/common/djangoapps/student/tests/test_certificates.py b/common/djangoapps/student/tests/test_certificates.py index a9f6335661..f935653e34 100644 --- a/common/djangoapps/student/tests/test_certificates.py +++ b/common/djangoapps/student/tests/test_certificates.py @@ -15,7 +15,6 @@ from xmodule.modulestore.tests.django_utils import TEST_DATA_MONGO_AMNESTY_MODUL from xmodule.modulestore.tests.factories import CourseFactory from xmodule.data import CertificatesDisplayBehaviors -from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory from lms.djangoapps.certificates.api import get_certificate_url from lms.djangoapps.certificates.data import CertificateStatuses @@ -36,7 +35,6 @@ class CertificateDisplayTestBase(SharedModuleStoreTestCase): MODULESTORE = TEST_DATA_MONGO_AMNESTY_MODULESTORE USERNAME = "test_user" PASSWORD = "password" - DOWNLOAD_URL = "http://www.example.com/certificate.pdf" @classmethod def setUpClass(cls): @@ -63,7 +61,7 @@ class CertificateDisplayTestBase(SharedModuleStoreTestCase): else: self.assertNotContains(response, 'Add Certificate to LinkedIn Profile') - def _create_certificate(self, enrollment_mode, download_url=DOWNLOAD_URL): + def _create_certificate(self, enrollment_mode): """Simulate that the user has a generated certificate. """ CourseEnrollmentFactory.create( user=self.user, @@ -73,38 +71,10 @@ class CertificateDisplayTestBase(SharedModuleStoreTestCase): user=self.user, course_id=self.course.id, mode=enrollment_mode, - download_url=download_url, status=CertificateStatuses.downloadable, grade=0.98, ) - def _check_can_download_certificate(self): - """ - Inspect the dashboard to see if a certificate can be downloaded. - """ - response = self.client.get(reverse('dashboard')) - self.assertContains(response, 'Download my') - self.assertContains(response, self.DOWNLOAD_URL) - - def _check_can_download_certificate_no_id(self): - """ - Inspects the dashboard to see if a certificate for a non verified course enrollment - is present - """ - response = self.client.get(reverse('dashboard')) - self.assertContains(response, 'Download') - self.assertContains(response, self.DOWNLOAD_URL) - - def _check_can_not_download_certificate(self): - """ - Make sure response does not have any of the download certificate buttons - """ - response = self.client.get(reverse('dashboard')) - self.assertNotContains(response, 'View Test_Certificate') - self.assertNotContains(response, 'Download my Test_Certificate') - self.assertNotContains(response, 'Download my Test_Certificate') - self.assertNotContains(response, self.DOWNLOAD_URL) - @ddt.ddt @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @@ -131,7 +101,6 @@ class CertificateDashboardMessageDisplayTest(CertificateDisplayTestBase): if is_past: self.assertNotContains(response, test_message) self.assertNotContains(response, "View Test_Certificate") - self._check_can_download_certificate() else: self.assertContains(response, test_message) @@ -175,27 +144,6 @@ class CertificateDisplayTest(CertificateDisplayTestBase): Tests of certificate display. """ - @ddt.data('verified', 'professional') - @patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False}) - def test_display_verified_certificate(self, enrollment_mode): - self._create_certificate(enrollment_mode) - self._check_can_download_certificate() - - @patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False}) - def test_no_certificate_status_no_problem(self): - with patch('common.djangoapps.student.views.dashboard.cert_info', return_value={}): - self._create_certificate('honor') - self._check_can_not_download_certificate() - - @patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': False}) - def test_display_verified_certificate_no_id(self): - """ - Confirm that if we get a certificate with a no-id-professional mode - we still can download our certificate - """ - self._create_certificate(CourseMode.NO_ID_PROFESSIONAL_MODE) - self._check_can_download_certificate_no_id() - @ddt.data('verified', 'honor', 'professional') def test_unverified_certificate_message(self, enrollment_mode): cert = self._create_certificate(enrollment_mode) @@ -225,34 +173,6 @@ class CertificateDisplayTest(CertificateDisplayTestBase): self._check_linkedin_visibility(True) -@ddt.ddt -@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') -class CertificateDisplayTestHtmlView(CertificateDisplayTestBase): - """ - Tests of webview certificate display - """ - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.course.cert_html_view_enabled = True - cls.course.save() - cls.store.update_item(cls.course, cls.USERNAME) - - @ddt.data('verified', 'honor') - @override_settings(CERT_NAME_SHORT='Test_Certificate') - @patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True}) - def test_display_download_certificate_button(self, enrollment_mode): - """ - Tests if CERTIFICATES_HTML_VIEW is True - and course has enabled web certificates via cert_html_view_enabled setting - and no active certificate configuration available - then any of the web view certificate Download button should not be visible. - """ - self._create_certificate(enrollment_mode, download_url='') - self._check_can_not_download_certificate() - - @ddt.ddt @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') class CertificateDisplayTestLinkedHtmlView(CertificateDisplayTestBase): diff --git a/common/djangoapps/student/tests/test_email.py b/common/djangoapps/student/tests/test_email.py index 940d54e77a..22f1197576 100644 --- a/common/djangoapps/student/tests/test_email.py +++ b/common/djangoapps/student/tests/test_email.py @@ -562,6 +562,7 @@ class EmailChangeConfirmationTests(EmailTestMixin, EmailTemplateTagMixin, CacheI @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', "Test only valid in LMS") @override_settings(MKTG_URLS={'ROOT': 'https://dummy-root', 'CONTACT': '/help/contact-us'}) + @patch('common.djangoapps.student.signals.signals.USER_EMAIL_CHANGED.send') @ddt.data( ('plain_text', False), ('plain_text', True), @@ -569,9 +570,10 @@ class EmailChangeConfirmationTests(EmailTestMixin, EmailTemplateTagMixin, CacheI ('html', True) ) @ddt.unpack - def test_successful_email_change(self, test_body_type, test_marketing_enabled): + def test_successful_email_change(self, test_body_type, test_marketing_enabled, mock_email_change_signal): with patch.dict(settings.FEATURES, {'ENABLE_MKTG_SITE': test_marketing_enabled}): self.assertChangeEmailSent(test_body_type) + assert mock_email_change_signal.called meta = json.loads(UserProfile.objects.get(user=self.user).meta) assert 'old_emails' in meta diff --git a/common/djangoapps/student/tests/test_receivers.py b/common/djangoapps/student/tests/test_receivers.py index 39e654adeb..fb8d285fdb 100644 --- a/common/djangoapps/student/tests/test_receivers.py +++ b/common/djangoapps/student/tests/test_receivers.py @@ -1,18 +1,15 @@ """ Tests for student signal receivers. """ from unittest import skipUnless +from unittest.mock import patch + +from django.conf import settings from edx_toggles.toggles.testutils import override_waffle_flag + +from common.djangoapps.student.models import CourseEnrollmentCelebration, PendingNameChange, UserProfile +from common.djangoapps.student.signals.signals import USER_EMAIL_CHANGED +from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory, UserProfileFactory from lms.djangoapps.courseware.toggles import COURSEWARE_MICROFRONTEND_PROGRESS_MILESTONES -from common.djangoapps.student.models import ( - CourseEnrollmentCelebration, - PendingNameChange, - UserProfile -) -from common.djangoapps.student.tests.factories import ( - CourseEnrollmentFactory, - UserFactory, - UserProfileFactory -) from openedx.features.name_affirmation_api.utils import is_name_affirmation_installed from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order @@ -73,3 +70,15 @@ class ReceiversTest(SharedModuleStoreTestCase): assert PendingNameChange.objects.count() == 0 profile = UserProfile.objects.get(user=user) assert profile.name == new_name + + @skipUnless(settings.ROOT_URLCONF == 'lms.urls', "Test only valid in LMS") + @patch('common.djangoapps.student.signals.receivers.get_braze_client') + def test_listen_for_user_email_changed(self, mock_get_braze_client): + """ + Ensure that USER_EMAIL_CHANGED signal triggers correct calls to get_braze_client. + """ + user = UserFactory(email='email@test.com', username='jdoe') + + USER_EMAIL_CHANGED.send(sender=None, user=user) + + assert mock_get_braze_client.called diff --git a/common/djangoapps/student/views/management.py b/common/djangoapps/student/views/management.py index 1d918ac57a..5ec791ba27 100644 --- a/common/djangoapps/student/views/management.py +++ b/common/djangoapps/student/views/management.py @@ -79,6 +79,7 @@ from common.djangoapps.student.models import ( # lint-amnesty, pylint: disable= from common.djangoapps.student.signals import REFUND_ORDER from common.djangoapps.util.db import outer_atomic from common.djangoapps.util.json_request import JsonResponse +from common.djangoapps.student.signals import USER_EMAIL_CHANGED from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order log = logging.getLogger("edx.student") @@ -902,6 +903,8 @@ def confirm_email_change(request, key): return response response = render_to_response("email_change_successful.html", address_context) + + USER_EMAIL_CHANGED.send(sender=None, user=user) return response diff --git a/common/djangoapps/third_party_auth/admin.py b/common/djangoapps/third_party_auth/admin.py index 35f43ad129..6b0ca785fd 100644 --- a/common/djangoapps/third_party_auth/admin.py +++ b/common/djangoapps/third_party_auth/admin.py @@ -108,8 +108,12 @@ class SAMLProviderConfigAdmin(KeyedConfigurationModelAdmin): """ Do we have cached metadata for this SAML provider? """ if not inst.is_active: return None # N/A - data = SAMLProviderData.current(inst.entity_id) - return bool(data and data.is_valid()) + records = SAMLProviderData.objects.filter(entity_id=inst.entity_id) + for record in records: + if record.is_valid(): + return True + return False + has_data.short_description = 'Metadata Ready' has_data.boolean = True diff --git a/common/djangoapps/third_party_auth/models.py b/common/djangoapps/third_party_auth/models.py index f0a37616a3..4d1c1f84a9 100644 --- a/common/djangoapps/third_party_auth/models.py +++ b/common/djangoapps/third_party_auth/models.py @@ -784,16 +784,23 @@ class SAMLProviderConfig(ProviderConfig): conf['attr_defaults'][field] = default # Now get the data fetched automatically from the metadata.xml: - data = SAMLProviderData.current(self.entity_id) - if not data or not data.is_valid(): + data_records = SAMLProviderData.objects.filter(entity_id=self.entity_id) + public_keys = [] + for record in data_records: + if record.is_valid(): + public_keys.append(record.public_key) + sso_url = record.sso_url + if not public_keys: log.error( 'No SAMLProviderData found for provider "%s" with entity id "%s" and IdP slug "%s". ' 'Run "manage.py saml pull" to fix or debug.', self.name, self.entity_id, self.slug ) raise AuthNotConfigured(provider_name=self.name) - conf['x509cert'] = data.public_key - conf['url'] = data.sso_url + + conf['x509certMulti'] = {'signing': public_keys} + conf['x509cert'] = '' + conf['url'] = sso_url # Add SAMLConfiguration appropriate for this IdP conf['saml_sp_configuration'] = ( diff --git a/common/djangoapps/third_party_auth/samlproviderdata/tests/test_samlproviderdata.py b/common/djangoapps/third_party_auth/samlproviderdata/tests/test_samlproviderdata.py index 8854d320ae..5dbf7a803e 100644 --- a/common/djangoapps/third_party_auth/samlproviderdata/tests/test_samlproviderdata.py +++ b/common/djangoapps/third_party_auth/samlproviderdata/tests/test_samlproviderdata.py @@ -203,7 +203,7 @@ class SAMLProviderDataTests(APITestCase): POST auth/saml/v0/provider_data/sync_provider_data -d data """ mock_fetch.return_value = 'tag' - public_key = 'askdjf;sakdjfs;adkfjas;dkfjas;dkfjas;dlkfj' + public_key = ['askdjf;sakdjfs;adkfjas;dkfjas;dkfjas;dlkfj'] sso_url = 'https://fake-test.id' expires_at = datetime.now() mock_parse.return_value = (public_key, sso_url, expires_at) @@ -219,11 +219,11 @@ class SAMLProviderDataTests(APITestCase): response = self.client.post(url, data) assert response.status_code == status.HTTP_201_CREATED - assert response.data == " Created new record for SAMLProviderData for entityID http://entity-id-1" + assert response.data == " Created new record(s) for SAMLProviderData for entityID http://entity-id-1" assert SAMLProviderData.objects.count() == orig_count + 1 # should only update this time response = self.client.post(url, data) assert response.status_code == status.HTTP_200_OK - assert response.data == (" Updated existing SAMLProviderData for entityID http://entity-id-1") + assert response.data == (" Updated existing SAMLProviderData record(s) for entityID http://entity-id-1") assert SAMLProviderData.objects.count() == orig_count + 1 diff --git a/common/djangoapps/third_party_auth/samlproviderdata/views.py b/common/djangoapps/third_party_auth/samlproviderdata/views.py index 09ad65dfa8..f61b237c12 100644 --- a/common/djangoapps/third_party_auth/samlproviderdata/views.py +++ b/common/djangoapps/third_party_auth/samlproviderdata/views.py @@ -18,7 +18,7 @@ from rest_framework.response import Response from common.djangoapps.third_party_auth.utils import ( convert_saml_slug_provider_id, - create_or_update_saml_provider_data, + create_or_update_bulk_saml_provider_data, fetch_metadata_xml, parse_metadata_xml, validate_uuid4_string @@ -110,12 +110,12 @@ class SAMLProviderDataViewSet(PermissionRequiredMixin, SAMLProviderDataMixin, vi entity_id = request.POST.get('entity_id') metadata_url = request.POST.get('metadata_url') sso_url = request.POST.get('sso_url') - public_key = request.POST.get('public_key') + public_keys = request.POST.get('public_key') if not entity_id: return Response('entity_id is required', status.HTTP_400_BAD_REQUEST) - if not metadata_url and not (sso_url and public_key): + if not metadata_url and not (sso_url and public_keys): return Response('either metadata_url or sso and public key are required', status.HTTP_400_BAD_REQUEST) - if metadata_url and (sso_url or public_key): + if metadata_url and (sso_url or public_keys): return Response( 'either metadata_url or sso and public key can be provided, not both', status.HTTP_400_BAD_REQUEST ) @@ -131,18 +131,18 @@ class SAMLProviderDataViewSet(PermissionRequiredMixin, SAMLProviderDataMixin, vi # part 2: create/update samlproviderdata log.info("Processing IdP with entityID %s", entity_id) - public_key, sso_url, expires_at = parse_metadata_xml(xml, entity_id) + public_keys, sso_url, expires_at = parse_metadata_xml(xml, entity_id) else: now = datetime.now() expires_at = now.replace(year=now.year + 10) - changed = create_or_update_saml_provider_data(entity_id, public_key, sso_url, expires_at) + changed = create_or_update_bulk_saml_provider_data(entity_id, public_keys, sso_url, expires_at) if changed: - str_message = f" Created new record for SAMLProviderData for entityID {entity_id}" + str_message = f" Created new record(s) for SAMLProviderData for entityID {entity_id}" log.info(str_message) response = str_message http_status = status.HTTP_201_CREATED else: - str_message = f" Updated existing SAMLProviderData for entityID {entity_id}" + str_message = f" Updated existing SAMLProviderData record(s) for entityID {entity_id}" log.info(str_message) response = str_message http_status = status.HTTP_200_OK diff --git a/common/djangoapps/third_party_auth/tasks.py b/common/djangoapps/third_party_auth/tasks.py index 88b118a689..d702932f4d 100644 --- a/common/djangoapps/third_party_auth/tasks.py +++ b/common/djangoapps/third_party_auth/tasks.py @@ -14,7 +14,7 @@ from requests import exceptions from common.djangoapps.third_party_auth.models import SAMLConfiguration, SAMLProviderConfig from common.djangoapps.third_party_auth.utils import ( MetadataParseError, - create_or_update_saml_provider_data, + create_or_update_bulk_saml_provider_data, parse_metadata_xml, ) @@ -87,8 +87,8 @@ def fetch_saml_metadata(): for entity_id in entity_ids: log.info("Processing IdP with entityID %s", entity_id) - public_key, sso_url, expires_at = parse_metadata_xml(xml, entity_id) - changed = create_or_update_saml_provider_data(entity_id, public_key, sso_url, expires_at) + public_keys, sso_url, expires_at = parse_metadata_xml(xml, entity_id) + changed = create_or_update_bulk_saml_provider_data(entity_id, public_keys, sso_url, expires_at) if changed: log.info(f"→ Created new record for SAMLProviderData for entityID {entity_id}") num_updated += 1 diff --git a/common/djangoapps/third_party_auth/tests/test_utils.py b/common/djangoapps/third_party_auth/tests/test_utils.py index 2d971e69d0..5d35d92b46 100644 --- a/common/djangoapps/third_party_auth/tests/test_utils.py +++ b/common/djangoapps/third_party_auth/tests/test_utils.py @@ -157,8 +157,44 @@ class TestUtils(TestCase): ''' xml = etree.fromstring(xml_text, parser) - public_key, sso_url, _ = parse_metadata_xml(xml, entity_id) - assert public_key == 'abc+hkIuUktxkg=' + public_keys, sso_url, _ = parse_metadata_xml(xml, entity_id) + assert public_keys == ['abc+hkIuUktxkg='] + assert sso_url == 'https://idp/SSOService.php' + + def test_parse_metadata_uses_multiple_signing_cert(self): + entity_id = 'http://testid' + parser = etree.XMLParser(remove_comments=True) + xml_text = ''' + + + + + + abc+hkIuUktxkg= + + + + + + + xyz+ayylmao= + + + + + + + blachabc+hkIuUktxkg=blaal;skdjf;ksd + + + + + + + ''' + xml = etree.fromstring(xml_text, parser) + public_keys, sso_url, _ = parse_metadata_xml(xml, entity_id) + assert public_keys == ['abc+hkIuUktxkg=', 'xyz+ayylmao='] assert sso_url == 'https://idp/SSOService.php' def test_parse_metadata_with_use_attribute_missing(self): @@ -179,6 +215,6 @@ class TestUtils(TestCase): ''' xml = etree.fromstring(xml_text, parser) - public_key, sso_url, _ = parse_metadata_xml(xml, entity_id) - assert public_key == 'abc+hkIuUktxkg=' + public_keys, sso_url, _ = parse_metadata_xml(xml, entity_id) + assert public_keys == ['abc+hkIuUktxkg='] assert sso_url == 'https://idp/SSOService.php' diff --git a/common/djangoapps/third_party_auth/utils.py b/common/djangoapps/third_party_auth/utils.py index 8517af0328..0cb6981030 100644 --- a/common/djangoapps/third_party_auth/utils.py +++ b/common/djangoapps/third_party_auth/utils.py @@ -101,18 +101,24 @@ def parse_metadata_xml(xml, entity_id): # Now we just need to get the public_key and sso_url # We want the use='signing' cert, not the 'encryption' one - public_key = sso_desc.findtext("./{}[@use='signing']//{}".format( + # There may be multiple signing certs returned by the server so create one record per signing cert found. + certs = sso_desc.findall("./{}[@use='signing']//{}".format( etree.QName(SAML_XML_NS, "KeyDescriptor"), "{http://www.w3.org/2000/09/xmldsig#}X509Certificate" )) - if not public_key: + + if not certs: # it's possible that there is just one keyDescription with no use attribute # that is a shortcut for both signing and encryption combined. So we can use that as fallback. - public_key = sso_desc.findtext("./{}//{}".format( + certs = sso_desc.findall("./{}//{}".format( etree.QName(SAML_XML_NS, "KeyDescriptor"), "{http://www.w3.org/2000/09/xmldsig#}X509Certificate" )) - if not public_key: + if not certs: raise MetadataParseError("Public Key missing. Expected an ") - public_key = public_key.replace(" ", "") + + public_keys = [] + for key in certs: + public_keys.append(key.text.replace(" ", "")) + binding_elements = sso_desc.iterfind("./{}".format(etree.QName(SAML_XML_NS, "SingleSignOnService"))) sso_bindings = {element.get('Binding'): element.get('Location') for element in binding_elements} try: @@ -120,7 +126,7 @@ def parse_metadata_xml(xml, entity_id): sso_url = sso_bindings['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'] except KeyError: raise MetadataParseError("Unable to find SSO URL with HTTP-Redirect binding.") # lint-amnesty, pylint: disable=raise-missing-from - return public_key, sso_url, expires_at + return public_keys, sso_url, expires_at def user_exists(details): @@ -164,29 +170,22 @@ def get_user_from_email(details): return None -def create_or_update_saml_provider_data(entity_id, public_key, sso_url, expires_at): +def create_or_update_bulk_saml_provider_data(entity_id, public_keys, sso_url, expires_at): """ - Update/Create the SAMLProviderData for the given entity ID. - Return value: - False if nothing has changed and existing data's "fetched at" timestamp is just updated. - True if a new record was created. (Either this is a new provider or something changed.) + Placeholder """ - data_obj = SAMLProviderData.current(entity_id) fetched_at = now() - if data_obj and (data_obj.public_key == public_key and data_obj.sso_url == sso_url): - data_obj.expires_at = expires_at - data_obj.fetched_at = fetched_at - data_obj.save() - return False - else: - SAMLProviderData.objects.create( - entity_id=entity_id, - fetched_at=fetched_at, - expires_at=expires_at, - sso_url=sso_url, - public_key=public_key, + new_records_created = False + # Create a data record for each of the public keys provided + for key in public_keys: + _, created = SAMLProviderData.objects.update_or_create( + public_key=key, entity_id=entity_id, + defaults={'sso_url': sso_url, 'expires_at': expires_at, 'fetched_at': fetched_at}, ) - return True + if created: + new_records_created = True + + return new_records_created def convert_saml_slug_provider_id(provider): # lint-amnesty, pylint: disable=redefined-outer-name diff --git a/common/djangoapps/util/tests/test_codejail_includes.py b/common/djangoapps/util/tests/test_codejail_includes.py new file mode 100644 index 0000000000..0e56240a54 --- /dev/null +++ b/common/djangoapps/util/tests/test_codejail_includes.py @@ -0,0 +1,26 @@ +""" +Tests for codejail-includes package. +""" + +import unittest + +import eia +import loncapa +from verifiers import draganddrop + + +class TestCodeJailIncludes(unittest.TestCase): + """ tests for codejail includes""" + + def test_loncapa(self): + random_integer = loncapa.lc_random(2, 60, 5) + assert random_integer <= 60 + assert random_integer >= 2 + + def test_nested_list_and_list1(self): + assert draganddrop.PositionsCompare([[1, 2], 40]) == draganddrop.PositionsCompare([1, 3]) + + def test_Eia(self): + # Test cases. All of these should return True + assert eia.iseia(100) # 100 ohm resistor is EIA + assert not eia.iseia(101) # 101 is not diff --git a/common/djangoapps/util/views.py b/common/djangoapps/util/views.py index 3cbaaf018e..4bc4fd5522 100644 --- a/common/djangoapps/util/views.py +++ b/common/djangoapps/util/views.py @@ -167,11 +167,6 @@ def calculate(request): return HttpResponse(json.dumps({'result': str(result)})) # lint-amnesty, pylint: disable=http-response-with-json-dumps -def info(request): - """ Info page (link from main header) """ - return render_to_response("info.html", {}) - - def add_p3p_header(view_func): """ This decorator should only be used with views which may be displayed through the iframe. diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index cc1ea427f5..30295b0919 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -104,7 +104,6 @@ class LoncapaSystem(object): get_python_lib_zip, DEBUG, i18n, - node_path, render_template, resources_fs, seed, # Why do we do this if we have self.seed? @@ -119,7 +118,6 @@ class LoncapaSystem(object): self.get_python_lib_zip = get_python_lib_zip self.DEBUG = DEBUG # pylint: disable=invalid-name self.i18n = i18n - self.node_path = node_path self.render_template = render_template self.resources_fs = resources_fs self.seed = seed # Why do we do this if we have self.seed? diff --git a/common/lib/capa/capa/safe_exec/README.rst b/common/lib/capa/capa/safe_exec/README.rst index 9a28480caf..ef12f45af6 100644 --- a/common/lib/capa/capa/safe_exec/README.rst +++ b/common/lib/capa/capa/safe_exec/README.rst @@ -15,18 +15,12 @@ CodeJail`__, with a few customized tweaks. __ https://github.com/edx/codejail/blob/master/README.rst -1. At the instruction to install packages into the sandboxed code, you'll +1. At the instruction to install packages into the sandboxed code, you'll need to install the requirements from requirements/edx-sandbox:: $ pip install -r requirements/edx-sandbox/base.txt -2. At the instruction to create the AppArmor profile, you'll need a line in - the profile for the sandbox packages. is the full path to - your edx_platform repo:: - - /common/lib/sandbox-packages/** r, - -3. You can configure resource limits in settings.py. A CODE_JAIL setting is +2. You can configure resource limits in settings.py. A CODE_JAIL setting is available, a dictionary. The "limits" key lets you adjust the limits for CPU time, real time, and memory use. Setting any of them to zero disables that limit:: diff --git a/common/lib/capa/capa/tests/helpers.py b/common/lib/capa/capa/tests/helpers.py index 1779168c1a..0d30b1b60f 100644 --- a/common/lib/capa/capa/tests/helpers.py +++ b/common/lib/capa/capa/tests/helpers.py @@ -73,7 +73,6 @@ def test_capa_system(render_template=None): get_python_lib_zip=lambda: None, DEBUG=True, i18n=gettext.NullTranslations(), - node_path=os.environ.get("NODE_PATH", "/usr/local/lib/node_modules"), render_template=render_template or tst_render_template, resources_fs=fs.osfs.OSFS(os.path.join(TEST_DIR, "test_files")), seed=0, diff --git a/common/lib/sandbox-packages/README b/common/lib/sandbox-packages/README deleted file mode 100644 index 706998b08e..0000000000 --- a/common/lib/sandbox-packages/README +++ /dev/null @@ -1 +0,0 @@ -This directory is in the Python path for sandboxed Python execution. diff --git a/common/lib/sandbox-packages/eia.py b/common/lib/sandbox-packages/eia.py deleted file mode 100644 index f8c2da59f1..0000000000 --- a/common/lib/sandbox-packages/eia.py +++ /dev/null @@ -1,112 +0,0 @@ -""" -Standard resistor values. - -Commonly used for verifying electronic components in circuit classes are -standard values, or conversely, for generating realistic component -values in parameterized problems. For details, see: - -http://en.wikipedia.org/wiki/Electronic_color_code -""" - - -# pylint: disable=invalid-name -# r is standard name for a resistor. We would like to use it as such. - -import math -import numbers - -E6 = [10, 15, 22, 33, 47, 68] - -E12 = [10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82] -E24 = [10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82, 11, 13, 16, 20, - 24, 30, 36, 43, 51, 62, 75, 91] - -E48 = [100, 121, 147, 178, 215, 261, 316, 383, 464, 562, 681, 825, 105, - 127, 154, 187, 226, 274, 332, 402, 487, 590, 715, 866, 110, 133, - 162, 196, 237, 287, 348, 422, 511, 619, 750, 909, 115, 140, 169, - 205, 249, 301, 365, 442, 536, 649, 787, 953] - -E96 = [100, 121, 147, 178, 215, 261, 316, 383, 464, 562, 681, 825, 102, - 124, 150, 182, 221, 267, 324, 392, 475, 576, 698, 845, 105, 127, - 154, 187, 226, 274, 332, 402, 487, 590, 715, 866, 107, 130, 158, - 191, 232, 280, 340, 412, 499, 604, 732, 887, 110, 133, 162, 196, - 237, 287, 348, 422, 511, 619, 750, 909, 113, 137, 165, 200, 243, - 294, 357, 432, 523, 634, 768, 931, 115, 140, 169, 205, 249, 301, - 365, 442, 536, 649, 787, 953, 118, 143, 174, 210, 255, 309, 374, - 453, 549, 665, 806, 976] - -E192 = [100, 121, 147, 178, 215, 261, 316, 383, 464, 562, 681, 825, 101, - 123, 149, 180, 218, 264, 320, 388, 470, 569, 690, 835, 102, 124, - 150, 182, 221, 267, 324, 392, 475, 576, 698, 845, 104, 126, 152, - 184, 223, 271, 328, 397, 481, 583, 706, 856, 105, 127, 154, 187, - 226, 274, 332, 402, 487, 590, 715, 866, 106, 129, 156, 189, 229, - 277, 336, 407, 493, 597, 723, 876, 107, 130, 158, 191, 232, 280, - 340, 412, 499, 604, 732, 887, 109, 132, 160, 193, 234, 284, 344, - 417, 505, 612, 741, 898, 110, 133, 162, 196, 237, 287, 348, 422, - 511, 619, 750, 909, 111, 135, 164, 198, 240, 291, 352, 427, 517, - 626, 759, 920, 113, 137, 165, 200, 243, 294, 357, 432, 523, 634, - 768, 931, 114, 138, 167, 203, 246, 298, 361, 437, 530, 642, 777, - 942, 115, 140, 169, 205, 249, 301, 365, 442, 536, 649, 787, 953, - 117, 142, 172, 208, 252, 305, 370, 448, 542, 657, 796, 965, 118, - 143, 174, 210, 255, 309, 374, 453, 549, 665, 806, 976, 120, 145, - 176, 213, 258, 312, 379, 459, 556, 673, 816, 988] - - -def iseia(r, valid_types=(E6, E12, E24)): - ''' - Check if a component is a valid EIA value. - - By default, check 5% component values - ''' - - # Step 1: Discount things which are not numbers - if not isinstance(r, numbers.Number) or \ - r < 0 or \ - math.isnan(r) or \ - math.isinf(r): - return False - - # Special case: 0 is an okay resistor - if r == 0: - return True - - # Step 2: Move into the range [100, 1000) - while r < 100: - r = r * 10 - while r >= 1000: - r = r / 10 - - # Step 3: Discount things which are not integers, and cast to int - if abs(r - round(r)) > 0.01: - return False - r = int(round(r)) - - # Step 4: Check if we're a valid EIA value - for type_list in valid_types: - if r in type_list: - return True - if int(r / 10.) in type_list and (r % 10) == 0: - return True - - return False - -if __name__ == '__main__': - # Test cases. All of these should return True - print(iseia(100)) # 100 ohm resistor is EIA - print(not iseia(101)) # 101 is not - print(not iseia(100.3)) # Floating point close to EIA is not EIA - print(iseia(100.001)) # But within floating point error is - print(iseia(1e5)) # We handle big numbers well - print(iseia(2200)) # We handle middle-of-the-list well - # We can handle 1% components correctly; 2.2k is EIA24, but not EIA48. - print(not iseia(2200, (E48, E96, E192))) - print(iseia(5490e2, (E48, E96, E192))) - print(iseia(2200)) - print(not iseia(5490e2)) - print(iseia(1e-5)) # We handle little numbers well - print(not iseia("Hello")) # Junk handled okay - print(not iseia(float('NaN'))) - print(not iseia(-1)) - print(not iseia(iseia)) - print(not iseia(float('Inf'))) - print(iseia(0)) # Corner case. 0 is a standard resistor value. diff --git a/common/lib/sandbox-packages/loncapa/__init__.py b/common/lib/sandbox-packages/loncapa/__init__.py deleted file mode 100644 index 3d613e04de..0000000000 --- a/common/lib/sandbox-packages/loncapa/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/python # lint-amnesty, pylint: disable=missing-module-docstring - -from .loncapa_check import * # lint-amnesty, pylint: disable=redefined-builtin diff --git a/common/lib/sandbox-packages/loncapa/loncapa_check.py b/common/lib/sandbox-packages/loncapa/loncapa_check.py deleted file mode 100644 index 80a0a51545..0000000000 --- a/common/lib/sandbox-packages/loncapa/loncapa_check.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/python # lint-amnesty, pylint: disable=missing-module-docstring -# -# File: mitx/lib/loncapa/loncapa_check.py -# -# Python functions which duplicate the standard comparison functions available to LON-CAPA problems. -# Used in translating LON-CAPA problems to i4x problem specification language. - - -import math -import random -from six.moves import range - - -def lc_random(lower, upper, stepsize): - ''' - like random.randrange but lower and upper can be non-integer - ''' - nstep = int((upper - lower) / (1.0 * stepsize)) - choices = [lower + x * stepsize for x in range(nstep)] - return random.choice(choices) - - -def lc_choose(index, *args): - ''' - return args[index] - ''' - try: - return args[int(index) - 1] - except Exception as err: # lint-amnesty, pylint: disable=broad-except, unused-variable - pass - if len(args): # lint-amnesty, pylint: disable=len-as-condition - return args[0] - raise Exception( - "loncapa_check.lc_choose error, index={index}, args={args}".format( - index=index, - args=args, - ) - ) - -deg2rad = math.pi / 180.0 -rad2deg = 180.0 / math.pi diff --git a/common/lib/sandbox-packages/setup.py b/common/lib/sandbox-packages/setup.py deleted file mode 100644 index 3a02c854bc..0000000000 --- a/common/lib/sandbox-packages/setup.py +++ /dev/null @@ -1,17 +0,0 @@ -# lint-amnesty, pylint: disable=missing-module-docstring - -from setuptools import setup - -setup( - name="sandbox-packages", - version="0.1.1", - packages=[ - "loncapa", - "verifiers", - ], - py_modules=[ - "eia", - ], - install_requires=[ - ], -) diff --git a/common/lib/sandbox-packages/verifiers/draganddrop.py b/common/lib/sandbox-packages/verifiers/draganddrop.py deleted file mode 100644 index 9d41366c58..0000000000 --- a/common/lib/sandbox-packages/verifiers/draganddrop.py +++ /dev/null @@ -1,433 +0,0 @@ -""" Grader of drag and drop input. - -Client side behavior: user can drag and drop images from list on base image. - - - Then json returned from client is: - { - "draggable": [ - { "image1": "t1" }, - { "ant": "t2" }, - { "molecule": "t3" }, - ] -} -values are target names. - -or: - { - "draggable": [ - { "image1": "[10, 20]" }, - { "ant": "[30, 40]" }, - { "molecule": "[100, 200]" }, - ] -} -values are (x, y) coordinates of centers of dragged images. -""" - - -import json -import six -from six.moves import zip - - -def flat_user_answer(user_answer): - """ - Convert nested `user_answer` to flat format. - - {'up': {'first': {'p': 'p_l'}}} - - to - - {'up': 'p_l[p][first]'} - """ - - def parse_user_answer(answer): - key = list(answer.keys())[0] - value = list(answer.values())[0] - if isinstance(value, dict): - - # Make complex value: - # Example: - # Create like 'p_l[p][first]' from {'first': {'p': 'p_l'} - complex_value_list = [] - v_value = value - while isinstance(v_value, dict): - v_key = list(v_value.keys())[0] - v_value = list(v_value.values())[0] - complex_value_list.append(v_key) - - complex_value = '{0}'.format(v_value) - for i in reversed(complex_value_list): - complex_value = '{0}[{1}]'.format(complex_value, i) - - res = {key: complex_value} - return res - else: - return answer - - result = [] - for answer in user_answer: - parse_answer = parse_user_answer(answer) - result.append(parse_answer) - - return result - - -class PositionsCompare(list): - """ Class for comparing positions. - - Args: - list or string:: - "abc" - target - [10, 20] - list of integers - [[10, 20], 200] list of list and integer - - """ - def __eq__(self, other): - """ Compares two arguments. - - Default lists behavior is conversion of string "abc" to list - ["a", "b", "c"]. We will use that. - - If self or other is empty - returns False. - - Args: - self, other: str, unicode, list, int, float - - Returns: bool - """ - # checks if self or other is not empty list (empty lists = false) - if not self or not other: - return False - - if (isinstance(self[0], (list, int, float)) and - isinstance(other[0], (list, int, float))): - return self.coordinate_positions_compare(other) - - elif (isinstance(self[0], (six.text_type, str)) and - isinstance(other[0], (six.text_type, str))): - return ''.join(self) == ''.join(other) - else: # improper argument types: no (float / int or lists of list - #and float / int pair) or two string / unicode lists pair - return False - - def __ne__(self, other): - return not self.__eq__(other) - - def coordinate_positions_compare(self, other, r=10): - """ Checks if self is equal to other inside radius of forgiveness - (default 10 px). - - Args: - self, other: [x, y] or [[x, y], r], where r is radius of - forgiveness; - x, y, r: int - - Returns: bool. - """ - # get max radius of forgiveness - if isinstance(self[0], list): # [(x, y), r] case - r = max(self[1], r) - x1, y1 = self[0] - else: - x1, y1 = self - - if isinstance(other[0], list): # [(x, y), r] case - r = max(other[1], r) - x2, y2 = other[0] - else: - x2, y2 = other - - if (x2 - x1) ** 2 + (y2 - y1) ** 2 > r * r: - return False - - return True - - -class DragAndDrop(object): - """ Grader class for drag and drop inputtype. - """ - - def grade(self): - ''' Grader user answer. - - Checks if every draggable isplaced on proper target or on proper - coordinates within radius of forgiveness (default is 10). - - Returns: bool. - ''' - for draggable in self.excess_draggables: - if self.excess_draggables[draggable]: - return False # user answer has more draggables than correct answer - - # Number of draggables in user_groups may be differ that in - # correct_groups, that is incorrect, except special case with 'number' - for index, draggable_ids in enumerate(self.correct_groups): - # 'number' rule special case - # for reusable draggables we may get in self.user_groups - # {'1': [u'2', u'2', u'2'], '0': [u'1', u'1'], '2': [u'3']} - # if '+number' is in rule - do not remove duplicates and strip - # '+number' from rule - current_rule = list(self.correct_positions[index].keys())[0] - if 'number' in current_rule: - rule_values = self.correct_positions[index][current_rule] - # clean rule, do not do clean duplicate items - self.correct_positions[index].pop(current_rule, None) - parsed_rule = current_rule.replace('+', '').replace('number', '') - self.correct_positions[index][parsed_rule] = rule_values - else: # remove dublicates - self.user_groups[index] = list(set(self.user_groups[index])) - - if sorted(draggable_ids) != sorted(self.user_groups[index]): - return False - - # Check that in every group, for rule of that group, user positions of - # every element are equal with correct positions - for index, _ in enumerate(self.correct_groups): - rules_executed = 0 - for rule in ('exact', 'anyof', 'unordered_equal'): - # every group has only one rule - if self.correct_positions[index].get(rule, None): - rules_executed += 1 - if not self.compare_positions( - self.correct_positions[index][rule], - self.user_positions[index]['user'], flag=rule): - return False - if not rules_executed: # no correct rules for current group - # probably xml content mistake - wrong rules names - return False - - return True - - def compare_positions(self, correct, user, flag): - """ Compares two lists of positions with flag rules. Order of - correct/user arguments is matter only in 'anyof' flag. - - Rules description: - - 'exact' means 1-1 ordered relationship:: - - [el1, el2, el3] is 'exact' equal to [el5, el6, el7] when - el1 == el5, el2 == el6, el3 == el7. - Equality function is custom, see below. - - - 'anyof' means subset relationship:: - - user = [el1, el2] is 'anyof' equal to correct = [el1, el2, el3] - when - set(user) <= set(correct). - - 'anyof' is ordered relationship. It always checks if user - is subset of correct - - Equality function is custom, see below. - - Examples: - - - many draggables per position: - user ['1', '2', '2', '2'] is 'anyof' equal to ['1', '2', '3'] - - - draggables can be placed in any order: - user ['1', '2', '3', '4'] is 'anyof' equal to ['4', '2', '1', 3'] - - 'unordered_equal' is same as 'exact' but disregards on order - - Equality functions: - - Equality functon depends on type of element. They declared in - PositionsCompare class. For position like targets - ids ("t1", "t2", etc..) it is string equality function. For coordinate - positions ([1, 2] or [[1, 2], 15]) it is coordinate_positions_compare - function (see docstrings in PositionsCompare class) - - Args: - correst, user: lists of positions - - Returns: True if within rule lists are equal, otherwise False. - """ - if flag == 'exact': - if len(correct) != len(user): - return False - for el1, el2 in zip(correct, user): - if PositionsCompare(el1) != PositionsCompare(el2): - return False - - if flag == 'anyof': - for u_el in user: - for c_el in correct: - if PositionsCompare(u_el) == PositionsCompare(c_el): - break - else: - # General: the else is executed after the for, - # only if the for terminates normally (not by a break) - - # In this case, 'for' is terminated normally if every element - # from 'correct' list isn't equal to concrete element from - # 'user' list. So as we found one element from 'user' list, - # that not in 'correct' list - we return False - return False - - if flag == 'unordered_equal': - if len(correct) != len(user): - return False - temp = correct[:] - for u_el in user: - for c_el in temp: - if PositionsCompare(u_el) == PositionsCompare(c_el): - temp.remove(c_el) - break - else: - # same as upper - if we found element from 'user' list, - # that not in 'correct' list - we return False. - return False - - return True - - def __init__(self, correct_answer, user_answer): - """ Populates DragAndDrop variables from user_answer and correct_answer. - If correct_answer is dict, converts it to list. - Correct answer in dict form is simple structure for fast and simple - grading. Example of correct answer dict example:: - - correct_answer = {'name4': 't1', - 'name_with_icon': 't1', - '5': 't2', - '7': 't2'} - - It is draggable_name: dragable_position mapping. - - Advanced form converted from simple form uses 'exact' rule - for matching. - - Correct answer in list form is designed for advanced cases:: - - correct_answers = [ - { - 'draggables': ['1', '2', '3', '4', '5', '6'], - 'targets': [ - 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2'], - 'rule': 'anyof'}, - { - 'draggables': ['7', '8', '9', '10'], - 'targets': ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2'], - 'rule': 'anyof' - } - ] - - Advanced answer in list form is list of dicts, and every dict must have - 3 keys: 'draggables', 'targets' and 'rule'. 'Draggables' value is - list of draggables ids, 'targes' values are list of targets ids, 'rule' - value one of 'exact', 'anyof', 'unordered_equal', 'anyof+number', - 'unordered_equal+number' - - Advanced form uses "all dicts must match with their rule" logic. - - Same draggable cannot appears more that in one dict. - - Behavior is more widely explained in sphinx documentation. - - Args: - user_answer: json - correct_answer: dict or list - """ - - self.correct_groups = [] # Correct groups from xml. - self.correct_positions = [] # Correct positions for comparing. - self.user_groups = [] # Will be populated from user answer. - self.user_positions = [] # Will be populated from user answer. - - # Convert from dict answer format to list format. - if isinstance(correct_answer, dict): - tmp = [] - for key in sorted(correct_answer.keys()): - value = correct_answer[key] - tmp.append({ - 'draggables': [key], - 'targets': [value], - 'rule': 'exact'}) - correct_answer = tmp - - # Convert string `user_answer` to object. - user_answer = json.loads(user_answer) - - # This dictionary will hold a key for each draggable the user placed on - # the image. The value is True if that draggable is not mentioned in any - # correct_answer entries. If the draggable is mentioned in at least one - # correct_answer entry, the value is False. - # default to consider every user answer excess until proven otherwise. - self.excess_draggables = dict( - (list(users_draggable.keys())[0], True) - for users_draggable in user_answer - ) - - # Convert nested `user_answer` to flat format. - user_answer = flat_user_answer(user_answer) - - # Create identical data structures from user answer and correct answer. - for answer in correct_answer: - user_groups_data = [] - user_positions_data = [] - for draggable_dict in user_answer: - # Draggable_dict is 1-to-1 {draggable_name: position}. - draggable_name = list(draggable_dict.keys())[0] - if draggable_name in answer['draggables']: - user_groups_data.append(draggable_name) - user_positions_data.append( - draggable_dict[draggable_name] - ) - # proved that this is not excess - self.excess_draggables[draggable_name] = False - - self.correct_groups.append(answer['draggables']) - self.correct_positions.append({answer['rule']: answer['targets']}) - self.user_groups.append(user_groups_data) - self.user_positions.append({'user': user_positions_data}) - - -def grade(user_input, correct_answer): - """ Creates DragAndDrop instance from user_input and correct_answer and - calls DragAndDrop.grade for grading. - - Supports two interfaces for correct_answer: dict and list. - - Args: - user_input: json. Format:: - - { "draggables": - [{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}' - - or - - {"draggables": [{"1": "t1"}, \ - {"name_with_icon": "t2"}]} - - correct_answer: dict or list. - - Dict form:: - - {'1': 't1', 'name_with_icon': 't2'} - - or - - {'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'} - - List form:: - - correct_answer = [ - { - 'draggables': ['l3_o', 'l10_o'], - 'targets': ['t1_o', 't9_o'], - 'rule': 'anyof' - }, - { - 'draggables': ['l1_c', 'l8_c'], - 'targets': ['t5_c', 't6_c'], - 'rule': 'anyof' - } - ] - - Returns: bool - """ - return DragAndDrop(correct_answer=correct_answer, - user_answer=user_input).grade() diff --git a/common/lib/sandbox-packages/verifiers/tests_draganddrop.py b/common/lib/sandbox-packages/verifiers/tests_draganddrop.py deleted file mode 100644 index 03bea08587..0000000000 --- a/common/lib/sandbox-packages/verifiers/tests_draganddrop.py +++ /dev/null @@ -1,843 +0,0 @@ -# lint-amnesty, pylint: disable=missing-module-docstring - -import json -import unittest - -from . import draganddrop - -from .draganddrop import PositionsCompare - - -class Test_PositionsCompare(unittest.TestCase): - """ describe""" - - def test_nested_list_and_list1(self): - assert PositionsCompare([[1, 2], 40]) == PositionsCompare([1, 3]) - - def test_nested_list_and_list2(self): - assert PositionsCompare([1, 12]) != PositionsCompare([1, 1]) - - def test_list_and_list1(self): - assert PositionsCompare([[1, 2], 12]) != PositionsCompare([1, 15]) - - def test_list_and_list2(self): - assert PositionsCompare([1, 11]) == PositionsCompare([1, 1]) - - def test_numerical_list_and_string_list(self): - assert PositionsCompare([1, 2]) != PositionsCompare(['1']) - - def test_string_and_string_list1(self): - assert PositionsCompare('1') == PositionsCompare(['1']) - - def test_string_and_string_list2(self): - assert PositionsCompare('abc') == PositionsCompare('abc') - - def test_string_and_string_list3(self): - assert PositionsCompare('abd') != PositionsCompare('abe') - - def test_float_and_string(self): - assert PositionsCompare([3.5, 5.7]) != PositionsCompare(['1']) - - def test_floats_and_ints(self): - assert PositionsCompare([3.5, 4.5]) == PositionsCompare([5, 7]) - - -class Test_DragAndDrop_Grade(unittest.TestCase): # lint-amnesty, pylint: disable=missing-class-docstring - - def test_targets_are_draggable_1(self): - user_input = json.dumps([ - {'p': 'p_l'}, - {'up': {'first': {'p': 'p_l'}}} - ]) - - correct_answer = [ - { - 'draggables': ['p'], - 'targets': ['p_l', 'p_r'], - 'rule': 'anyof' - }, - { - 'draggables': ['up'], - 'targets': [ - 'p_l[p][first]' - ], - 'rule': 'anyof' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_targets_are_draggable_2(self): - user_input = json.dumps([ - {'p': 'p_l'}, - {'p': 'p_r'}, - {'s': 's_l'}, - {'s': 's_r'}, - {'up': {'1': {'p': 'p_l'}}}, - {'up': {'3': {'p': 'p_l'}}}, - {'up': {'1': {'p': 'p_r'}}}, - {'up': {'3': {'p': 'p_r'}}}, - {'up_and_down': {'1': {'s': 's_l'}}}, - {'up_and_down': {'1': {'s': 's_r'}}} - ]) - - correct_answer = [ - { - 'draggables': ['p'], - 'targets': ['p_l', 'p_r'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['s'], - 'targets': ['s_l', 's_r'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['up_and_down'], - 'targets': ['s_l[s][1]', 's_r[s][1]'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['up'], - 'targets': [ - 'p_l[p][1]', - 'p_l[p][3]', - 'p_r[p][1]', - 'p_r[p][3]', - ], - 'rule': 'unordered_equal' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_targets_are_draggable_2_manual_parsing(self): - user_input = json.dumps([ - {'up': 'p_l[p][1]'}, - {'p': 'p_l'}, - {'up': 'p_l[p][3]'}, - {'up': 'p_r[p][1]'}, - {'p': 'p_r'}, - {'up': 'p_r[p][3]'}, - {'up_and_down': 's_l[s][1]'}, - {'s': 's_l'}, - {'up_and_down': 's_r[s][1]'}, - {'s': 's_r'} - ]) - - correct_answer = [ - { - 'draggables': ['p'], - 'targets': ['p_l', 'p_r'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['s'], - 'targets': ['s_l', 's_r'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['up_and_down'], - 'targets': ['s_l[s][1]', 's_r[s][1]'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['up'], - 'targets': [ - 'p_l[p][1]', - 'p_l[p][3]', - 'p_r[p][1]', - 'p_r[p][3]', - ], - 'rule': 'unordered_equal' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_targets_are_draggable_3_nested(self): - user_input = json.dumps([ - {'molecule': 'left_side_tagret'}, - {'molecule': 'right_side_tagret'}, - {'p': {'p_target': {'molecule': 'left_side_tagret'}}}, - {'p': {'p_target': {'molecule': 'right_side_tagret'}}}, - {'s': {'s_target': {'molecule': 'left_side_tagret'}}}, - {'s': {'s_target': {'molecule': 'right_side_tagret'}}}, - {'up': {'1': {'p': {'p_target': {'molecule': 'left_side_tagret'}}}}}, - {'up': {'3': {'p': {'p_target': {'molecule': 'left_side_tagret'}}}}}, - {'up': {'1': {'p': {'p_target': {'molecule': 'right_side_tagret'}}}}}, - {'up': {'3': {'p': {'p_target': {'molecule': 'right_side_tagret'}}}}}, - {'up_and_down': {'1': {'s': {'s_target': {'molecule': 'left_side_tagret'}}}}}, - {'up_and_down': {'1': {'s': {'s_target': {'molecule': 'right_side_tagret'}}}}} - ]) - - correct_answer = [ - { - 'draggables': ['molecule'], - 'targets': ['left_side_tagret', 'right_side_tagret'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['p'], - 'targets': [ - 'left_side_tagret[molecule][p_target]', - 'right_side_tagret[molecule][p_target]', - ], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['s'], - 'targets': [ - 'left_side_tagret[molecule][s_target]', - 'right_side_tagret[molecule][s_target]', - ], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['up_and_down'], - 'targets': [ - 'left_side_tagret[molecule][s_target][s][1]', - 'right_side_tagret[molecule][s_target][s][1]', - ], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['up'], - 'targets': [ - 'left_side_tagret[molecule][p_target][p][1]', - 'left_side_tagret[molecule][p_target][p][3]', - 'right_side_tagret[molecule][p_target][p][1]', - 'right_side_tagret[molecule][p_target][p][3]', - ], - 'rule': 'unordered_equal' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_targets_are_draggable_4_real_example(self): - user_input = json.dumps([ - {'single_draggable': 's_l'}, - {'single_draggable': 's_r'}, - {'single_draggable': 'p_sigma'}, - {'single_draggable': 'p_sigma*'}, - {'single_draggable': 's_sigma'}, - {'single_draggable': 's_sigma*'}, - {'double_draggable': 'p_pi*'}, - {'double_draggable': 'p_pi'}, - {'triple_draggable': 'p_l'}, - {'triple_draggable': 'p_r'}, - {'up': {'1': {'triple_draggable': 'p_l'}}}, - {'up': {'2': {'triple_draggable': 'p_l'}}}, - {'up': {'2': {'triple_draggable': 'p_r'}}}, - {'up': {'3': {'triple_draggable': 'p_r'}}}, - {'up_and_down': {'1': {'single_draggable': 's_l'}}}, - {'up_and_down': {'1': {'single_draggable': 's_r'}}}, - {'up_and_down': {'1': {'single_draggable': 's_sigma'}}}, - {'up_and_down': {'1': {'single_draggable': 's_sigma*'}}}, - {'up_and_down': {'1': {'double_draggable': 'p_pi'}}}, - {'up_and_down': {'2': {'double_draggable': 'p_pi'}}} - ]) - - # 10 targets: - # s_l, s_r, p_l, p_r, s_sigma, s_sigma*, p_pi, p_sigma, p_pi*, p_sigma* - # - # 3 draggable objects, which have targets (internal target ids - 1, 2, 3): - # single_draggable, double_draggable, triple_draggable - # - # 2 draggable objects: - # up, up_and_down - correct_answer = [ - { - 'draggables': ['triple_draggable'], - 'targets': ['p_l', 'p_r'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['double_draggable'], - 'targets': ['p_pi', 'p_pi*'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['single_draggable'], - 'targets': ['s_l', 's_r', 's_sigma', 's_sigma*', 'p_sigma', 'p_sigma*'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['up'], - 'targets': [ - 'p_l[triple_draggable][1]', - 'p_l[triple_draggable][2]', - 'p_r[triple_draggable][2]', - 'p_r[triple_draggable][3]', - ], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['up_and_down'], - 'targets': [ - 's_l[single_draggable][1]', - 's_r[single_draggable][1]', - 's_sigma[single_draggable][1]', - 's_sigma*[single_draggable][1]', - 'p_pi[double_draggable][1]', - 'p_pi[double_draggable][2]', - ], - 'rule': 'unordered_equal' - }, - - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_targets_true(self): - user_input = '[{"1": "t1"}, \ - {"name_with_icon": "t2"}]' - correct_answer = {'1': 't1', 'name_with_icon': 't2'} - assert draganddrop.grade(user_input, correct_answer) - - def test_expect_no_actions_wrong(self): - user_input = '[{"1": "t1"}, \ - {"name_with_icon": "t2"}]' - correct_answer = [] - assert not draganddrop.grade(user_input, correct_answer) - - def test_expect_no_actions_right(self): - user_input = '[]' - correct_answer = [] - assert draganddrop.grade(user_input, correct_answer) - - def test_targets_false(self): - user_input = '[{"1": "t1"}, \ - {"name_with_icon": "t2"}]' - correct_answer = {'1': 't3', 'name_with_icon': 't2'} - assert not draganddrop.grade(user_input, correct_answer) - - def test_multiple_images_per_target_true(self): - user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}, \ - {"2": "t1"}]' - correct_answer = {'1': 't1', 'name_with_icon': 't2', '2': 't1'} - assert draganddrop.grade(user_input, correct_answer) - - def test_multiple_images_per_target_false(self): - user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}, \ - {"2": "t1"}]' - correct_answer = {'1': 't2', 'name_with_icon': 't2', '2': 't1'} - assert not draganddrop.grade(user_input, correct_answer) - - def test_targets_and_positions(self): - user_input = '[{"1": [10,10]}, \ - {"name_with_icon": [[10,10],4]}]' - correct_answer = {'1': [10, 10], 'name_with_icon': [[10, 10], 4]} - assert draganddrop.grade(user_input, correct_answer) - - def test_position_and_targets(self): - user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}]' - correct_answer = {'1': 't1', 'name_with_icon': 't2'} - assert draganddrop.grade(user_input, correct_answer) - - def test_positions_exact(self): - user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]' - correct_answer = {'1': [10, 10], 'name_with_icon': [20, 20]} - assert draganddrop.grade(user_input, correct_answer) - - def test_positions_false(self): - user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]' - correct_answer = {'1': [25, 25], 'name_with_icon': [20, 20]} - assert not draganddrop.grade(user_input, correct_answer) - - def test_positions_true_in_radius(self): - user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]' - correct_answer = {'1': [14, 14], 'name_with_icon': [20, 20]} - assert draganddrop.grade(user_input, correct_answer) - - def test_positions_true_in_manual_radius(self): - user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]' - correct_answer = {'1': [[40, 10], 30], 'name_with_icon': [20, 20]} - assert draganddrop.grade(user_input, correct_answer) - - def test_positions_false_in_manual_radius(self): - user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]' - correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]} - assert not draganddrop.grade(user_input, correct_answer) - - def test_correct_answer_not_has_key_from_user_answer(self): - user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}]' - correct_answer = {'3': 't3', 'name_with_icon': 't2'} - assert not draganddrop.grade(user_input, correct_answer) - - def test_anywhere(self): - """Draggables can be places anywhere on base image. - Place grass in the middle of the image and ant in the - right upper corner.""" - user_input = '[{"ant":[610.5,57.449951171875]},\ - {"grass":[322.5,199.449951171875]}]' - - correct_answer = {'grass': [[300, 200], 200], 'ant': [[500, 0], 200]} - assert draganddrop.grade(user_input, correct_answer) - - def test_lcao_correct(self): - """Describe carbon molecule in LCAO-MO""" - user_input = '[{"1":"s_left"}, \ - {"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \ - {"8":"p_left_2"},{"10":"p_right_1"},{"9":"p_right_2"}, \ - {"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \ - {"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \ - {"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]' - - correct_answer = [{ - 'draggables': ['1', '2', '3', '4', '5', '6'], - 'targets': [ - 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2' - ], - 'rule': 'anyof' - }, { - 'draggables': ['7', '8', '9', '10'], - 'targets': ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2'], - 'rule': 'anyof' - }, { - 'draggables': ['11', '12'], - 'targets': ['s_sigma_name', 'p_sigma_name'], - 'rule': 'anyof' - }, { - 'draggables': ['13', '14'], - 'targets': ['s_sigma_star_name', 'p_sigma_star_name'], - 'rule': 'anyof' - }, { - 'draggables': ['15'], - 'targets': ['p_pi_name'], - 'rule': 'anyof' - }, { - 'draggables': ['16'], - 'targets': ['p_pi_star_name'], - 'rule': 'anyof' - }] - - assert draganddrop.grade(user_input, correct_answer) - - def test_lcao_extra_element_incorrect(self): - """Describe carbon molecule in LCAO-MO""" - user_input = '[{"1":"s_left"}, \ - {"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \ - {"8":"p_left_2"},{"17":"p_left_3"},{"10":"p_right_1"},{"9":"p_right_2"}, \ - {"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \ - {"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \ - {"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]' - - correct_answer = [{ - 'draggables': ['1', '2', '3', '4', '5', '6'], - 'targets': [ - 's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2' - ], - 'rule': 'anyof' - }, { - 'draggables': ['7', '8', '9', '10'], - 'targets': ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2'], - 'rule': 'anyof' - }, { - 'draggables': ['11', '12'], - 'targets': ['s_sigma_name', 'p_sigma_name'], - 'rule': 'anyof' - }, { - 'draggables': ['13', '14'], - 'targets': ['s_sigma_star_name', 'p_sigma_star_name'], - 'rule': 'anyof' - }, { - 'draggables': ['15'], - 'targets': ['p_pi_name'], - 'rule': 'anyof' - }, { - 'draggables': ['16'], - 'targets': ['p_pi_star_name'], - 'rule': 'anyof' - }] - - assert not draganddrop.grade(user_input, correct_answer) - - def test_reuse_draggable_no_mupliples(self): - """Test reusable draggables (no mupltiple draggables per target)""" - user_input = '[{"1":"target1"}, \ - {"2":"target2"},{"1":"target3"},{"2":"target4"},{"2":"target5"}, \ - {"3":"target6"}]' - correct_answer = [ - { - 'draggables': ['1'], - 'targets': ['target1', 'target3'], - 'rule': 'anyof' - }, - { - 'draggables': ['2'], - 'targets': ['target2', 'target4', 'target5'], - 'rule': 'anyof' - }, - { - 'draggables': ['3'], - 'targets': ['target6'], - 'rule': 'anyof' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_reuse_draggable_with_mupliples(self): - """Test reusable draggables with mupltiple draggables per target""" - user_input = '[{"1":"target1"}, \ - {"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"}, \ - {"3":"target6"}]' - correct_answer = [ - { - 'draggables': ['1'], - 'targets': ['target1', 'target3'], - 'rule': 'anyof' - }, - { - 'draggables': ['2'], - 'targets': ['target2', 'target4'], - 'rule': 'anyof' - }, - { - 'draggables': ['3'], - 'targets': ['target6'], - 'rule': 'anyof' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_reuse_many_draggable_with_mupliples(self): - """Test reusable draggables with mupltiple draggables per target""" - user_input = '[{"1":"target1"}, \ - {"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"}, \ - {"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \ - {"5": "target5"}, {"6": "target2"}]' - correct_answer = [ - { - 'draggables': ['1', '4'], - 'targets': ['target1', 'target3'], - 'rule': 'anyof' - }, - { - 'draggables': ['2', '6'], - 'targets': ['target2', 'target4'], - 'rule': 'anyof' - }, - { - 'draggables': ['5'], - 'targets': ['target4', 'target5'], - 'rule': 'anyof' - }, - { - 'draggables': ['3'], - 'targets': ['target6'], - 'rule': 'anyof' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_reuse_many_draggable_with_mupliples_wrong(self): - """Test reusable draggables with mupltiple draggables per target""" - user_input = '[{"1":"target1"}, \ - {"2":"target2"},{"1":"target1"}, \ - {"2":"target3"}, \ - {"2":"target4"}, \ - {"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \ - {"5": "target5"}, {"6": "target2"}]' - correct_answer = [ - { - 'draggables': ['1', '4'], - 'targets': ['target1', 'target3'], - 'rule': 'anyof' - }, - { - 'draggables': ['2', '6'], - 'targets': ['target2', 'target4'], - 'rule': 'anyof' - }, - { - 'draggables': ['5'], - 'targets': ['target4', 'target5'], - 'rule': 'anyof' - }, - { - 'draggables': ['3'], - 'targets': ['target6'], - 'rule': 'anyof' - }] - assert not draganddrop.grade(user_input, correct_answer) - - def test_label_10_targets_with_a_b_c_false(self): - """Test reusable draggables (no mupltiple draggables per target)""" - user_input = '[{"a":"target1"}, \ - {"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \ - {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ - {"a":"target1"}]' - correct_answer = [ - { - 'draggables': ['a'], - 'targets': ['target1', 'target4', 'target7', 'target10'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['b'], - 'targets': ['target2', 'target5', 'target8'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['c'], - 'targets': ['target3', 'target6', 'target9'], - 'rule': 'unordered_equal' - } - ] - assert not draganddrop.grade(user_input, correct_answer) - - def test_label_10_targets_with_a_b_c_(self): - """Test reusable draggables (no mupltiple draggables per target)""" - user_input = '[{"a":"target1"}, \ - {"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \ - {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ - {"a":"target10"}]' - correct_answer = [ - { - 'draggables': ['a'], - 'targets': ['target1', 'target4', 'target7', 'target10'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['b'], - 'targets': ['target2', 'target5', 'target8'], - 'rule': 'unordered_equal' - }, - { - 'draggables': ['c'], - 'targets': ['target3', 'target6', 'target9'], - 'rule': 'unordered_equal' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_label_10_targets_with_a_b_c_multiple(self): - """Test reusable draggables (mupltiple draggables per target)""" - user_input = '[{"a":"target1"}, \ - {"b":"target2"},{"c":"target3"},{"b":"target5"}, \ - {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ - {"a":"target1"}]' - correct_answer = [ - { - 'draggables': ['a', 'a', 'a'], - 'targets': ['target1', 'target4', 'target7', 'target10'], - 'rule': 'anyof+number' - }, - { - 'draggables': ['b', 'b', 'b'], - 'targets': ['target2', 'target5', 'target8'], - 'rule': 'anyof+number' - }, - { - 'draggables': ['c', 'c', 'c'], - 'targets': ['target3', 'target6', 'target9'], - 'rule': 'anyof+number' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_label_10_targets_with_a_b_c_multiple_false(self): - """Test reusable draggables (mupltiple draggables per target)""" - user_input = '[{"a":"target1"}, \ - {"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \ - {"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \ - {"a":"target1"}]' - correct_answer = [ - { - 'draggables': ['a', 'a', 'a'], - 'targets': ['target1', 'target4', 'target7', 'target10'], - 'rule': 'anyof+number' - }, - { - 'draggables': ['b', 'b', 'b'], - 'targets': ['target2', 'target5', 'target8'], - 'rule': 'anyof+number' - }, - { - 'draggables': ['c', 'c', 'c'], - 'targets': ['target3', 'target6', 'target9'], - 'rule': 'anyof+number' - } - ] - assert not draganddrop.grade(user_input, correct_answer) - - def test_label_10_targets_with_a_b_c_reused(self): - """Test a b c in 10 labels reused""" - user_input = '[{"a":"target1"}, \ - {"b":"target2"},{"c":"target3"},{"b":"target5"}, \ - {"c":"target6"}, {"b":"target8"},{"c":"target9"}, \ - {"a":"target10"}]' - correct_answer = [ - { - 'draggables': ['a', 'a'], - 'targets': ['target1', 'target10'], - 'rule': 'unordered_equal+number' - }, - { - 'draggables': ['b', 'b', 'b'], - 'targets': ['target2', 'target5', 'target8'], - 'rule': 'unordered_equal+number' - }, - { - 'draggables': ['c', 'c', 'c'], - 'targets': ['target3', 'target6', 'target9'], - 'rule': 'unordered_equal+number' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_label_10_targets_with_a_b_c_reused_false(self): - """Test a b c in 10 labels reused false""" - user_input = '[{"a":"target1"}, \ - {"b":"target2"},{"c":"target3"},{"b":"target5"}, {"a":"target8"},\ - {"c":"target6"}, {"b":"target8"},{"c":"target9"}, \ - {"a":"target10"}]' - correct_answer = [ - { - 'draggables': ['a', 'a'], - 'targets': ['target1', 'target10'], - 'rule': 'unordered_equal+number' - }, - { - 'draggables': ['b', 'b', 'b'], - 'targets': ['target2', 'target5', 'target8'], - 'rule': 'unordered_equal+number' - }, - { - 'draggables': ['c', 'c', 'c'], - 'targets': ['target3', 'target6', 'target9'], - 'rule': 'unordered_equal+number' - } - ] - assert not draganddrop.grade(user_input, correct_answer) - - def test_mixed_reuse_and_not_reuse(self): - """Test reusable draggables """ - user_input = '[{"a":"target1"}, \ - {"b":"target2"},{"c":"target3"}, {"a":"target4"},\ - {"a":"target5"}]' - correct_answer = [ - { - 'draggables': ['a', 'b'], - 'targets': ['target1', 'target2', 'target4', 'target5'], - 'rule': 'anyof' - }, - { - 'draggables': ['c'], - 'targets': ['target3'], - 'rule': 'exact' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_mixed_reuse_and_not_reuse_number(self): - """Test reusable draggables with number """ - user_input = '[{"a":"target1"}, \ - {"b":"target2"},{"c":"target3"}, {"a":"target4"}]' - correct_answer = [ - { - 'draggables': ['a', 'a', 'b'], - 'targets': ['target1', 'target2', 'target4'], - 'rule': 'anyof+number' - }, - { - 'draggables': ['c'], - 'targets': ['target3'], - 'rule': 'exact' - } - ] - assert draganddrop.grade(user_input, correct_answer) - - def test_mixed_reuse_and_not_reuse_number_false(self): - """Test reusable draggables with numbers, but wrong""" - user_input = '[{"a":"target1"}, \ - {"b":"target2"},{"c":"target3"}, {"a":"target4"}, {"a":"target10"}]' - correct_answer = [ - { - 'draggables': ['a', 'a', 'b'], - 'targets': ['target1', 'target2', 'target4', 'target10'], - 'rule': 'anyof_number' - }, - { - 'draggables': ['c'], - 'targets': ['target3'], - 'rule': 'exact' - } - ] - assert not draganddrop.grade(user_input, correct_answer) - - def test_alternative_correct_answer(self): - user_input = '[{"name_with_icon":"t1"},\ - {"name_with_icon":"t1"},{"name_with_icon":"t1"},{"name4":"t1"}, \ - {"name4":"t1"}]' - correct_answer = [ - {'draggables': ['name4'], 'targets': ['t1', 't1'], 'rule': 'exact'}, - {'draggables': ['name_with_icon'], 'targets': ['t1', 't1', 't1'], - 'rule': 'exact'} - ] - assert draganddrop.grade(user_input, correct_answer) - - -class Test_DragAndDrop_Populate(unittest.TestCase): # lint-amnesty, pylint: disable=missing-class-docstring - - def test_1(self): - correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]} - user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]' - dnd = draganddrop.DragAndDrop(correct_answer, user_input) - - correct_groups = [['1'], ['name_with_icon']] - correct_positions = [{'exact': [[[40, 10], 29]]}, {'exact': [[20, 20]]}] - user_groups = [['1'], ['name_with_icon']] - user_positions = [{'user': [[10, 10]]}, {'user': [[20, 20]]}] - - assert correct_groups == dnd.correct_groups - assert correct_positions == dnd.correct_positions - assert user_groups == dnd.user_groups - assert user_positions == dnd.user_positions - - -class Test_DraAndDrop_Compare_Positions(unittest.TestCase): # lint-amnesty, pylint: disable=missing-class-docstring - - def test_1(self): - dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]') - assert dnd.compare_positions(correct=[[1, 1], [2, 3]], user=[[2, 3], [1, 1]], flag='anyof') - - def test_2a(self): - dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]') - assert dnd.compare_positions(correct=[[1, 1], [2, 3]], user=[[2, 3], [1, 1]], flag='exact') - - def test_2b(self): - dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]') - assert not dnd.compare_positions(correct=[[1, 1], [2, 3]], user=[[2, 13], [1, 1]], flag='exact') - - def test_3(self): - dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]') - assert not dnd.compare_positions(correct=['a', 'b'], user=['a', 'b', 'c'], flag='anyof') - - def test_4(self): - dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]') - assert dnd.compare_positions(correct=['a', 'b', 'c'], user=['a', 'b'], flag='anyof') - - def test_5(self): - dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]') - assert not dnd.compare_positions(correct=['a', 'b', 'c'], user=['a', 'c', 'b'], flag='exact') - - def test_6(self): - dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]') - assert dnd.compare_positions(correct=['a', 'b', 'c'], user=['a', 'c', 'b'], flag='anyof') - - def test_7(self): - dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]') - assert not dnd.compare_positions(correct=['a', 'b', 'b'], user=['a', 'c', 'b'], flag='anyof') - - -def suite(): # lint-amnesty, pylint: disable=missing-function-docstring - - testcases = [Test_PositionsCompare, - Test_DragAndDrop_Populate, - Test_DragAndDrop_Grade, - Test_DraAndDrop_Compare_Positions] - suites = [] - for testcase in testcases: - suites.append(unittest.TestLoader().loadTestsFromTestCase(testcase)) - return unittest.TestSuite(suites) - -if __name__ == "__main__": - unittest.TextTestRunner(verbosity=2).run(suite()) diff --git a/common/lib/xmodule/xmodule/assets/library_content/public/js/library_content_reset.js b/common/lib/xmodule/xmodule/assets/library_content/public/js/library_content_reset.js new file mode 100644 index 0000000000..e985d3c2a6 --- /dev/null +++ b/common/lib/xmodule/xmodule/assets/library_content/public/js/library_content_reset.js @@ -0,0 +1,18 @@ +/* JavaScript for reset option that can be done on a randomized LibraryContentBlock */ +function LibraryContentReset(runtime, element) { + $('.problem-reset-btn', element).click((e) => { + e.preventDefault(); + $.post({ + url: runtime.handlerUrl(element, 'reset_selected_children'), + success(data) { + edx.HtmlUtils.setHtml(element, edx.HtmlUtils.HTML(data)); + // Rebind the reset button for the block + XBlock.initializeBlock(element); + // Render the new set of problems (XBlocks) + $(".xblock", element).each(function(i, child) { + XBlock.initializeBlock(child); + }); + }, + }); + }); +} diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 064c0c81f8..225d9aa46b 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -490,6 +490,15 @@ class ProblemBlock( return self.display_name + @property + def debug(self): + """ + If CAPA block fails to render, we want course authors to be able to see + the error in Studio. At the same time, in production, we don't want + to show errors to students. + """ + return getattr(self.runtime, 'is_author_mode', False) or settings.DEBUG + @classmethod def filter_templates(cls, template, course): """ @@ -618,7 +627,6 @@ class ProblemBlock( get_python_lib_zip=None, DEBUG=None, i18n=self.runtime.service(self, "i18n"), - node_path=None, render_template=None, resources_fs=self.runtime.resources_fs, seed=None, @@ -680,7 +688,6 @@ class ProblemBlock( get_python_lib_zip=(lambda: get_python_lib_zip(contentstore, self.runtime.course_id)), DEBUG=None, i18n=self.runtime.service(self, "i18n"), - node_path=None, render_template=None, resources_fs=self.runtime.resources_fs, seed=1, @@ -827,9 +834,8 @@ class ProblemBlock( cache=cache_service, can_execute_unsafe_code=sandbox_service.can_execute_unsafe_code, get_python_lib_zip=sandbox_service.get_python_lib_zip, - DEBUG=self.runtime.DEBUG, + DEBUG=self.debug, i18n=self.runtime.service(self, "i18n"), - node_path=self.runtime.node_path, render_template=self.runtime.service(self, 'mako').render_template, resources_fs=self.runtime.resources_fs, seed=seed, # Why do we do this if we have self.seed? @@ -1063,8 +1069,7 @@ class ProblemBlock( str(err) ) - # TODO (vshnayder): another switch on DEBUG. - if self.runtime.DEBUG: + if self.debug: msg = HTML( '[courseware.capa.capa_module] ' 'Failed to generate HTML for problem {url}' @@ -1780,7 +1785,7 @@ class ProblemBlock( self.set_last_submission_time() except (StudentInputError, ResponseError, LoncapaProblemError) as inst: - if self.runtime.DEBUG: + if self.debug: log.warning( "StudentInputError in capa_module:problem_check", exc_info=True @@ -1814,7 +1819,7 @@ class ProblemBlock( self.set_state_from_lcp() self.set_score(self.score_from_lcp(self.lcp)) - if self.runtime.DEBUG: + if self.debug: msg = f"Error checking problem: {str(err)}" msg += f'\nTraceback:\n{traceback.format_exc()}' return {'success': msg} diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 8136ad3c50..417b9060cc 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -532,14 +532,6 @@ class CourseFields: # lint-amnesty, pylint: disable=missing-class-docstring scope=Scope.settings ) has_children = True - info_sidebar_name = String( - display_name=_("Course Home Sidebar Name"), - help=_( - "Enter the heading that you want students to see above your course handouts on the Course Home page. " - "Your course handouts appear in the right panel of the page." - ), - deprecated=True, - scope=Scope.settings, default=_('Course Handouts')) show_timezone = Boolean( help=_( "True if timezones should be shown on dates in the course. " diff --git a/common/lib/xmodule/xmodule/library_content_module.py b/common/lib/xmodule/xmodule/library_content_module.py index e7f1fbaff2..599656e676 100644 --- a/common/lib/xmodule/xmodule/library_content_module.py +++ b/common/lib/xmodule/xmodule/library_content_module.py @@ -8,6 +8,7 @@ import logging import random from copy import copy from gettext import ngettext +from rest_framework import status import bleach from django.conf import settings @@ -21,7 +22,7 @@ from web_fragments.fragment import Fragment from webob import Response from xblock.completable import XBlockCompletionMode from xblock.core import XBlock -from xblock.fields import Integer, List, Scope, String +from xblock.fields import Integer, List, Scope, String, Boolean from capa.responsetypes import registry from xmodule.mako_module import MakoTemplateBlockBase @@ -176,6 +177,14 @@ class LibraryContentBlock( default=[], scope=Scope.user_state, ) + # This cannot be called `show_reset_button`, because children blocks inherit this as a default value. + allow_resetting_children = Boolean( + display_name=_("Show Reset Button"), + help=_("Determines whether a 'Reset Problems' button is shown, so users may reset their answers and reshuffle " + "selected items."), + scope=Scope.settings, + default=False + ) @property def source_library_key(self): @@ -346,6 +355,27 @@ class LibraryContentBlock( return self.selected + @XBlock.handler + def reset_selected_children(self, _, __): + """ + Resets the XBlock's state for a user. + + This resets the state of all `selected` children and then clears the `selected` field + so that the new blocks are randomly chosen for this user. + """ + if not self.allow_resetting_children: + return Response('"Resetting selected children" is not allowed for this XBlock', + status=status.HTTP_400_BAD_REQUEST) + + for block_type, block_id in self.selected_children(): + block = self.runtime.get_block(self.location.course_key.make_usage_key(block_type, block_id)) + if hasattr(block, 'reset_problem'): + block.reset_problem(None) + block.save() + + self.selected = [] + return Response(json.dumps(self.student_view({}).content)) + def _get_selected_child_blocks(self): """ Generator returning XBlock instances of the children selected for the @@ -383,7 +413,11 @@ class LibraryContentBlock( 'show_bookmark_button': False, 'watched_completable_blocks': set(), 'completion_delay_ms': None, + 'reset_button': self.allow_resetting_children, })) + + fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/library_content_reset.js')) + fragment.initialize_js('LibraryContentReset') return fragment def author_view(self, context): diff --git a/common/lib/xmodule/xmodule/lti_2_util.py b/common/lib/xmodule/xmodule/lti_2_util.py index 17dfe680c0..93faa807c7 100644 --- a/common/lib/xmodule/xmodule/lti_2_util.py +++ b/common/lib/xmodule/xmodule/lti_2_util.py @@ -12,6 +12,7 @@ import re from unittest import mock from urllib import parse +from django.conf import settings from oauthlib.oauth1 import Client from webob import Response from xblock.core import XBlock @@ -63,7 +64,7 @@ class LTI20BlockMixin: Returns: webob.response: response to this request. See above for details. """ - if self.system.debug: + if settings.DEBUG: self._log_correct_authorization_header(request) if not self.accept_grades_past_due and self.is_past_due(): @@ -78,7 +79,7 @@ class LTI20BlockMixin: except LTIError: return Response(status=401) # Unauthorized in this case. 401 is right - real_user = self.system.service(self, 'user').get_user_by_anonymous_id(anon_id) + real_user = self.runtime.service(self, 'user').get_user_by_anonymous_id(anon_id) if not real_user: # that means we can't save to database, as we do not have real user id. msg = f"[LTI]: Real user not found against anon_id: {anon_id}" log.info(msg) @@ -170,7 +171,7 @@ class LTI20BlockMixin: "@context": "http://purl.imsglobal.org/ctx/lis/v2/Result", "@type": "Result" } - self.system.rebind_noauth_module_to_user(self, real_user) + self.runtime.service(self, 'rebind_user').rebind_noauth_module_to_user(self, real_user) if self.module_score is None: # In this case, no score has been ever set return Response(json.dumps(base_json_obj).encode('utf-8'), content_type=LTI_2_0_JSON_CONTENT_TYPE) @@ -253,10 +254,10 @@ class LTI20BlockMixin: else: scaled_score = None - self.system.rebind_noauth_module_to_user(self, user) + self.runtime.service(self, 'rebind_user').rebind_noauth_module_to_user(self, user) # have to publish for the progress page... - self.system.publish( + self.runtime.publish( self, 'grade', { diff --git a/common/lib/xmodule/xmodule/lti_module.py b/common/lib/xmodule/xmodule/lti_module.py index a2174f1f67..71be6d6b75 100644 --- a/common/lib/xmodule/xmodule/lti_module.py +++ b/common/lib/xmodule/xmodule/lti_module.py @@ -65,6 +65,7 @@ from urllib import parse import bleach import oauthlib.oauth1 +from django.conf import settings from lxml import etree from oauthlib.oauth1.rfc5849 import signature from pkg_resources import resource_string @@ -274,6 +275,7 @@ class LTIFields: @XBlock.needs("i18n") @XBlock.needs("mako") @XBlock.needs("user") +@XBlock.needs("rebind_user") class LTIBlock( LTIFields, LTI20BlockMixin, @@ -584,7 +586,7 @@ class LTIBlock( i4x-2-3-lti-31de800015cf4afb973356dbe81496df this part of resource_link_id: makes resource_link_id to be unique among courses inside same system. """ - return str(parse.quote(f"{self.system.hostname}-{self.location.html_id()}")) # lint-amnesty, pylint: disable=line-too-long + return str(parse.quote(f"{settings.LMS_BASE}-{self.location.html_id()}")) def get_lis_result_sourcedid(self): """ diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py b/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py index 55623b217e..4b6331589e 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py @@ -97,7 +97,6 @@ class SplitModuleTest(unittest.TestCase): "fields": { "tabs": [ CourseTab.load('courseware'), - CourseTab.load('course_info'), CourseTab.load('discussion'), CourseTab.load('wiki'), ], @@ -147,7 +146,6 @@ class SplitModuleTest(unittest.TestCase): "end": _date_field.from_json("2013-04-13T04:30"), "tabs": [ CourseTab.load('courseware'), - CourseTab.load('course_info'), CourseTab.load('discussion'), CourseTab.load('wiki'), CourseTab.load( @@ -320,7 +318,6 @@ class SplitModuleTest(unittest.TestCase): "fields": { "tabs": [ CourseTab.load('courseware'), - CourseTab.load('course_info'), CourseTab.load('discussion'), CourseTab.load('wiki'), ], @@ -417,7 +414,6 @@ class SplitModuleTest(unittest.TestCase): "fields": { "tabs": [ CourseTab.load('courseware'), - CourseTab.load('course_info'), CourseTab.load('discussion'), CourseTab.load('wiki'), ], @@ -581,7 +577,7 @@ class SplitModuleCourseTests(SplitModuleTest): course = self.findByIdInResult(courses, "head12345") assert course.location.org == 'testx' assert course.category == 'course', 'wrong category' - assert len(course.tabs) == 6, 'wrong number of tabs' + assert len(course.tabs) == 5, 'wrong number of tabs' assert course.display_name == 'The Ancient Greek Hero', 'wrong display name' assert course.advertised_start == 'Fall 2013', 'advertised_start' assert len(course.children) == 4, 'children' @@ -635,7 +631,7 @@ class SplitModuleCourseTests(SplitModuleTest): assert course.location.course_key.org == 'testx' assert course.location.course_key.course == 'wonderful' assert course.category == 'course', 'wrong category' - assert len(course.tabs) == 4, 'wrong number of tabs' + assert len(course.tabs) == 3, 'wrong number of tabs' assert course.display_name == 'The most wonderful course', course.display_name assert course.advertised_start is None assert len(course.children) == 0, 'children' @@ -665,7 +661,7 @@ class SplitModuleCourseTests(SplitModuleTest): assert course.location.course_key.org is None assert course.location.version_guid == head_course.previous_version assert course.category == 'course' - assert len(course.tabs) == 6 + assert len(course.tabs) == 5 assert course.display_name == 'The Ancient Greek Hero' assert course.graceperiod == datetime.timedelta(hours=2) assert course.advertised_start is None @@ -681,7 +677,7 @@ class SplitModuleCourseTests(SplitModuleTest): assert course.location.course_key.course == 'GreekHero' assert course.location.course_key.run == 'run' assert course.category == 'course' - assert len(course.tabs) == 6 + assert len(course.tabs) == 5 assert course.display_name == 'The Ancient Greek Hero' assert course.advertised_start == 'Fall 2013' assert len(course.children) == 4 @@ -933,7 +929,7 @@ class SplitModuleItemTests(SplitModuleTest): assert block.location.org == 'testx' assert block.location.course == 'GreekHero' assert block.location.run == 'run' - assert len(block.tabs) == 6, 'wrong number of tabs' + assert len(block.tabs) == 5, 'wrong number of tabs' assert block.display_name == 'The Ancient Greek Hero' assert block.advertised_start == 'Fall 2013' assert len(block.children) == 4 diff --git a/common/lib/xmodule/xmodule/services.py b/common/lib/xmodule/xmodule/services.py index be5534e1c1..a45ad61a44 100644 --- a/common/lib/xmodule/xmodule/services.py +++ b/common/lib/xmodule/xmodule/services.py @@ -4,10 +4,26 @@ Module contains various XModule/XBlock services import inspect +import logging + +from functools import partial from config_models.models import ConfigurationModel from django.conf import settings +from edx_when.field_data import DateLookupFieldData from xmodule.modulestore.django import modulestore +from xblock.reference.plugins import Service +from xblock.runtime import KvsFieldData + + +from lms.djangoapps.courseware.field_overrides import OverrideFieldData +from lms.djangoapps.courseware.model_data import DjangoKeyValueStore, FieldDataCache +from lms.djangoapps.lms_xblock.field_data import LmsFieldData +from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig +from openedx.core.lib.courses import get_course_by_id + + +log = logging.getLogger(__name__) class SettingsService: @@ -119,3 +135,91 @@ class TeamsConfigurationService: if not self._course: self._course = self.get_course(course_id) return self._course.teams_configuration + + +class RebindUserServiceError(Exception): + pass + + +class RebindUserService(Service): + """ + An XBlock Service that allows modules to get rebound to real users if it was previously bound to an AnonymousUser. + + This used to be a local function inside the `lms.djangoapps.courseware.module_render.get_module_system_for_user` + method, and was passed as a constructor argument to x_module.ModuleSystem. This has been refactored out into a + service to simplify the ModuleSystem and lives in this module temporarily. + + TODO: Only the old LTI XBlock uses it in 2 places for LTI 2.0 integration. As the LTI XBlock is deprecated in + favour of the LTI Consumer XBlock, this should be removed when the LTI XBlock is removed. + + Arguments: + user (User) - A Django User object + course_id (str) - Course ID + course (Course) - Course Object + get_module_system_for_user (function) - The helper function that will be called to create a module system + for a specfic user. This is the parent function from which this service was reactored out. + `lms.djangoapps.courseware.module_render.get_module_system_for_user` + kwargs (dict) - all the keyword arguments that need to be passed to the `get_module_system_for_user` + function when it is called during rebinding + """ + def __init__(self, user, course_id, get_module_system_for_user, **kwargs): + super().__init__(**kwargs) + self.user = user + self.course_id = course_id + self._ref = { + "get_module_system_for_user": get_module_system_for_user + } + self._kwargs = kwargs + + def rebind_noauth_module_to_user(self, block, real_user): + """ + Function that rebinds the module to the real_user. + + Will only work within a module bound to an AnonymousUser, e.g. one that's instantiated by the noauth_handler. + + Arguments: + block (any xblock type): the module to rebind + real_user (django.contrib.auth.models.User): the user to bind to + + Returns: + nothing (but the side effect is that module is re-bound to real_user) + """ + if self.user.is_authenticated: + err_msg = "rebind_noauth_module_to_user can only be called from a module bound to an anonymous user" + log.error(err_msg) + raise RebindUserServiceError(err_msg) + + field_data_cache_real_user = FieldDataCache.cache_for_descriptor_descendents( + self.course_id, + real_user, + block, + asides=XBlockAsidesConfig.possible_asides(), + ) + student_data_real_user = KvsFieldData(DjangoKeyValueStore(field_data_cache_real_user)) + + with modulestore().bulk_operations(self.course_id): + course = modulestore().get_course(course_key=self.course_id) + + (inner_system, inner_student_data) = self._ref["get_module_system_for_user"]( + user=real_user, + student_data=student_data_real_user, # These have implicit user bindings, rest of args considered not to + descriptor=block, + course_id=self.course_id, + course=course, + **self._kwargs + ) + + block.bind_for_student( + inner_system, + real_user.id, + [ + partial(DateLookupFieldData, course_id=self.course_id, user=self.user), + partial(OverrideFieldData.wrap, real_user, course), + partial(LmsFieldData, student_data=inner_student_data), + ], + ) + + block.scope_ids = block.scope_ids._replace(user_id=real_user.id) + # now bind the module to the new ModuleSystem instance and vice-versa + block.runtime = inner_system + inner_system.xmodule_instance = block diff --git a/common/lib/xmodule/xmodule/tabs.py b/common/lib/xmodule/xmodule/tabs.py index 1292db60ac..56c3faaf08 100644 --- a/common/lib/xmodule/xmodule/tabs.py +++ b/common/lib/xmodule/xmodule/tabs.py @@ -382,7 +382,6 @@ class CourseTabList(List): within the course. """ course_tabs = [ - CourseTab.load('course_info'), CourseTab.load('courseware') ] @@ -481,14 +480,18 @@ class CourseTabList(List): @classmethod def upgrade_tabs(cls, tabs): """ - Reverse and Rename Courseware to Course and Course Info to Home Tabs. + Remove course_info tab, and rename courseware tab to Course if needed. """ if tabs and len(tabs) > 1: + # Reverse them so that course_info is first, and rename courseware to Course if tabs[0].get('type') == 'courseware' and tabs[1].get('type') == 'course_info': tabs[0], tabs[1] = tabs[1], tabs[0] - tabs[0]['name'] = _('Home') tabs[1]['name'] = _('Course') + # NOTE: this check used for legacy courses containing the course_info tab. course_info + # should be removed according to https://github.com/openedx/public-engineering/issues/56. + if tabs[0].get('type') == 'course_info': + tabs.pop(0) return tabs @classmethod @@ -499,22 +502,15 @@ class CourseTabList(List): Specific rules checked: - if no tabs specified, that's fine - - if tabs specified, first two must have type 'courseware' and 'course_info', in that order. + - if tabs specified, first must have type 'courseware'. """ if tabs is None or len(tabs) == 0: return - if len(tabs) < 2: - raise InvalidTabsException(f"Expected at least two tabs. tabs: '{tabs}'") - - if tabs[0].get('type') != 'course_info': + if tabs[0].get('type') != 'courseware': raise InvalidTabsException( - f"Expected first tab to have type 'course_info'. tabs: '{tabs}'") - - if tabs[1].get('type') != 'courseware': - raise InvalidTabsException( - f"Expected second tab to have type 'courseware'. tabs: '{tabs}'") + f"Expected first tab to have type 'courseware'. tabs: '{tabs}'") # the following tabs should appear only once # TODO: don't import openedx capabilities from common diff --git a/common/lib/xmodule/xmodule/templates/html/raw.yaml b/common/lib/xmodule/xmodule/templates/html/raw.yaml index 8d4c78ca89..d844f15403 100644 --- a/common/lib/xmodule/xmodule/templates/html/raw.yaml +++ b/common/lib/xmodule/xmodule/templates/html/raw.yaml @@ -6,7 +6,6 @@ data: |

    This template is similar to the Text template. The only difference is that this template opens in the Raw HTML editor rather than in the Visual editor.

    -

    The Raw HTML editor saves your HTML exactly as you enter it. You can switch to the Visual editor by clicking the Settings tab and changing the Editor setting to Visual. Note, however, that some of your diff --git a/common/lib/xmodule/xmodule/templates/problem/problem_with_hint.yaml b/common/lib/xmodule/xmodule/templates/problem/problem_with_hint.yaml index 1f04d45f73..52e02c42bf 100644 --- a/common/lib/xmodule/xmodule/templates/problem/problem_with_hint.yaml +++ b/common/lib/xmodule/xmodule/templates/problem/problem_with_hint.yaml @@ -11,7 +11,7 @@ data: |