/
githubmirror
/
oppia
Обзор
Документация
Войти
/
githubmirror
/
oppia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
main.py
1 750 строк
62 KB
NITISH KUMAR
[GSoC 2026] M2.5.1 - Fix part of #24716: Added Handlers, service functions and domain objects for the My suggestion tab and creator feedback tab. (#26962)
04 авг 2026, 13:59
Не верифицирован
04 авг 2026, 13:59
9dde735
Код
Авторство
О чём код?
# Copyright 2014 The Oppia Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS-IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """URL routing definitions, and some basic error/warmup handlers.""" from __future__ import annotations import logging from core import android_validation_constants, feconf from core.constants import constants from core.controllers import ( access_validators, acl_decorators, admin, android, base, beam_jobs, blog_admin, blog_dashboard, blog_homepage, certificate_assessment, classroom, collection_editor, collection_viewer, concept_card_viewer, contributor_dashboard, contributor_dashboard_admin, creator_dashboard, cron, custom_landing_pages, diagnostic_test_player, editor, feature_flag, features, feedback, feedback_updates, firebase, general_feedback, improvements, incoming_app_feedback_report, learner_dashboard, learner_goals, learner_group, library, moderator, oppia_root, pages, practice_sessions, profile, question_editor, questions_list, reader, recent_commits, release_coordinator, resources, review_tests, skill_editor, skill_mastery, story_editor, story_viewer, subscriptions, subtopic_viewer, suggestion, tasks, topic_editor, topic_viewer, topics_and_skills_dashboard, voice_artist, voiceover, ) from core.platform import models from core.platform.auth import firebase_auth_services import google.cloud.logging import webapp2 from typing import Dict, Optional, Type, TypeVar from webapp2_extras import routes MYPY = False if MYPY: # pragma: no cover from mypy_imports import datastore_services from mypy_imports import memory_cache_services as cache_services T = TypeVar('T') # pylint: disable=invalid-name cache_services = models.Registry.import_cache_services() datastore_services = models.Registry.import_datastore_services() # Cloud Logging is disabled in emulator mode, since it is unnecessary and # creates a lot of noise. if not constants.EMULATOR_MODE: # Instantiates a client and retrieves a Cloud Logging handler based on the # environment you're running in and integrates the handler with the Python # logging module. client = google.cloud.logging.Client() client.setup_logging() # Suppress debug logging for chardet. See https://stackoverflow.com/a/48581323. # Without this, a lot of unnecessary debug logs are printed in error logs, # which makes it tiresome to identify the actual error. logging.getLogger(name='chardet.charsetprober').setLevel(logging.INFO) class InternetConnectivityHandler( base.BaseHandler[Dict[str, str], Dict[str, str]] ): """Handles the get request to the server from the frontend to check for internet connection.""" GET_HANDLER_ERROR_RETURN_TYPE = feconf.HANDLER_TYPE_JSON URL_PATH_ARGS_SCHEMAS: Dict[str, str] = {} HANDLER_ARGS_SCHEMAS: Dict[str, Dict[str, str]] = {'GET': {}} @acl_decorators.open_access def get(self) -> None: """Handles GET requests.""" self.render_json({'is_internet_connected': True}) class FrontendErrorHandler(base.BaseHandler[Dict[str, str], Dict[str, str]]): """Handles errors arising from the frontend.""" REQUIRE_PAYLOAD_CSRF_CHECK = False URL_PATH_ARGS_SCHEMAS: Dict[str, str] = {} HANDLER_ARGS_SCHEMAS = { 'POST': {'error': {'schema': {'type': 'basestring'}}} } @acl_decorators.open_access def post(self) -> None: """Records errors reported by the frontend.""" assert self.normalized_payload is not None logging.error( 'Frontend error: %s' % self.normalized_payload.get('error') ) self.render_json(self.values) class WarmupPage(base.BaseHandler[Dict[str, str], Dict[str, str]]): """Handles warmup requests.""" GET_HANDLER_ERROR_RETURN_TYPE = feconf.HANDLER_TYPE_HTML URL_PATH_ARGS_SCHEMAS: Dict[str, str] = {} HANDLER_ARGS_SCHEMAS: Dict[str, Dict[str, str]] = {'GET': {}} @acl_decorators.open_access def get(self) -> None: """Handles GET warmup requests.""" pass class SplashRedirectPage(base.BaseHandler[Dict[str, str], Dict[str, str]]): """Redirect the old splash URL, '/splash' to the new one, '/'.""" GET_HANDLER_ERROR_RETURN_TYPE = feconf.HANDLER_TYPE_HTML URL_PATH_ARGS_SCHEMAS: Dict[str, str] = {} HANDLER_ARGS_SCHEMAS: Dict[str, Dict[str, str]] = {'GET': {}} @acl_decorators.open_access def get(self) -> None: self.redirect('/') # Type for `defaults` is set to Dict[str, str] based on the usage in our # backend. Should be changed in future as per the requirements. def get_redirect_route( regex_route: str, handler: Type[webapp2.RequestHandler], defaults: Optional[Dict[str, str]] = None, ) -> routes.RedirectRoute: """Returns a route that redirects /foo/ to /foo. Warning: this method strips off parameters after the trailing slash. URLs with parameters should be formulated without the trailing slash. Args: regex_route: unicode. A raw string representing a route. handler: RequestHandler. A callable to handle the route. defaults: dict. Optional defaults parameter to be passed into the RedirectRoute object. Returns: RedirectRoute. A RedirectRoute object for redirects. """ if defaults is None: defaults = {} name = regex_route.replace('/', '_') return routes.RedirectRoute( regex_route, handler, name, strict_slash=True, defaults=defaults ) # Register the URLs with the classes responsible for handling them. URLS = [ get_redirect_route( '/<firebase_path:__/auth(?:/.*)?>', firebase.FirebaseProxyPage ), get_redirect_route(r'/_ah/warmup', WarmupPage), get_redirect_route( r'%s/can_access_story_viewer_page/<classroom_url_fragment>/<topic_url_fragment>/story/<story_url_fragment>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.StoryViewerPageAccessValidationHandler, ), get_redirect_route(r'/splash', SplashRedirectPage), get_redirect_route( r'/internetconnectivityhandler', InternetConnectivityHandler ), get_redirect_route(r'/credits', pages.AboutRedirectPage), get_redirect_route(r'/participate', pages.TeachRedirectPage), get_redirect_route(r'/site_guidelines', pages.TeachRedirectPage), get_redirect_route(r'/forum', pages.ForumRedirectPage), # Access Validators. get_redirect_route( r'%s/can_access_classroom_page' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ClassroomAccessValidationHandler, ), get_redirect_route( r'%s/can_access_classrooms_page' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ClassroomsPageAccessValidationHandler, ), get_redirect_route( r'%s/can_access_collection_editor_page/<collection_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.CollectionEditorAccessValidationPage, ), get_redirect_route( r'%s/can_access_exploration_editor_page/<exploration_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ExplorationEditorAccessValidationHandlerPage, ), get_redirect_route( r'%s/can_access_story_editor_page/<story_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.StoryEditorAccessValidationHandlerPage, ), get_redirect_route( r'%s/can_access_blog_home_page' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.BlogHomePageAccessValidationHandler, ), get_redirect_route( '/learn/<classroom_url_fragment>/<topic_url_fragment>/revision/<subtopic_url_fragment>', # pylint: disable=line-too-long access_validators.SubtopicViewerPageRevisionRedirectHandler, ), get_redirect_route( r'%s/can_access_subtopic_viewer_page/<classroom_url_fragment>/<topic_url_fragment>/studyguide/<subtopic_url_fragment>' # pylint: disable=line-too-long % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.SubtopicViewerPageAccessValidationHandler, ), get_redirect_route( r'%s/can_access_blog_post_page' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.BlogPostPageAccessValidationHandler, ), get_redirect_route( r'%s/can_access_blog_author_profile_page/<author_username>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.BlogAuthorProfilePageAccessValidationHandler, ), get_redirect_route( r'%s/can_manage_own_account' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ManageOwnAccountValidationHandler, ), get_redirect_route( r'%s/can_access_topic_editor/<topic_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.TopicEditorAccessValidationPage, ), get_redirect_route( r'%s/does_profile_exist/<username>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ProfileExistsValidationHandler, ), get_redirect_route( r'%s/can_access_release_coordinator_page' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ReleaseCoordinatorAccessValidationHandler, ), get_redirect_route( r'%s/can_access_facilitator_dashboard_page' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.FacilitatorDashboardPageAccessValidationHandler, ), get_redirect_route( r'%s/can_access_create_learner_group_page' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.CreateLearnerGroupPageAccessValidationHandler, ), get_redirect_route( r'%s/can_access_edit_learner_group_page/<learner_group_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.EditLearnerGroupPageAccessValidationHandler, ), get_redirect_route( r'%s/does_learner_group_exist/<learner_group_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ViewLearnerGroupPageAccessValidationHandler, ), get_redirect_route( r'%s/can_access_collection_player_page/<collection_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.CollectionViewerPageAccessValidationHandler, ), get_redirect_route( r'%s/can_access_diagnostic_test_player_page' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.DiagnosticTestPlayerAccessValidationHandler, ), get_redirect_route( r'%s/can_access_practice_session_page/<classroom_url_fragment>' r'/<topic_url_fragment>/practice/session' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.PracticeSessionAccessValidationPage, ), get_redirect_route( r'%s/can_access_practice_session_page/<classroom_url_fragment>' r'/<topic_url_fragment>/practice/<node_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.PracticeSessionAccessValidationPage, ), get_redirect_route( r'%s/can_access_practice_session_page/<classroom_url_fragment>' r'/<topic_url_fragment>/test/arc/<arc_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.PracticeSessionAccessValidationPage, ), get_redirect_route( r'%s/can_access_practice_session_page/<classroom_url_fragment>' r'/<topic_url_fragment>/mastery-challenge' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.PracticeSessionAccessValidationPage, ), get_redirect_route( r'%s/can_access_topic_viewer_page/<classroom_url_fragment>' r'/<topic_url_fragment>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.TopicViewerPageAccessValidationHandler, ), get_redirect_route( r'%s/can_access_topic_viewer_page/<classroom_url_fragment>' r'/<topic_url_fragment>/story' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.TopicViewerPageAccessValidationHandler, ), get_redirect_route( '/learn/<classroom_url_fragment>/<topic_url_fragment>/revision', access_validators.TopicViewerPageRevisionRedirectHandler, ), get_redirect_route( r'%s/can_access_topic_viewer_page/<classroom_url_fragment>' r'/<topic_url_fragment>/studyguide' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.TopicViewerPageAccessValidationHandler, ), get_redirect_route( r'%s/can_access_topic_viewer_page/<classroom_url_fragment>' r'/<topic_url_fragment>/practice' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.TopicViewerPageAccessValidationHandler, ), get_redirect_route( r'%s/can_access_technical_feedback_dashboard' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.TechnicalFeedbackDashboardAccessValidationHandler, ), get_redirect_route(r'%s' % feconf.ADMIN_URL, oppia_root.OppiaRootPage), get_redirect_route(r'/adminhandler', admin.AdminHandler), get_redirect_route(r'/adminrolehandler', admin.AdminRoleHandler), get_redirect_route(r'/bannedusershandler', admin.BannedUsersHandler), get_redirect_route( r'/topicmanagerrolehandler', admin.TopicManagerRoleHandler ), get_redirect_route( r'/translationcoordinatorrolehandler', admin.TranslationCoordinatorRoleHandler, ), get_redirect_route( r'/adminsuperadminhandler', admin.AdminSuperAdminPrivilegesHandler ), get_redirect_route( r'/admintopicscsvdownloadhandler', admin.AdminTopicsCsvFileDownloader ), get_redirect_route( r'%s' % feconf.AUTOMATIC_VOICEOVER_ADMIN_CONTROL_URL, admin.AutomaticVoiceoverAdminControlHandler, ), get_redirect_route( r'/updateblogpostdatahandler', admin.UpdateBlogPostHandler ), get_redirect_route( r'%s' % feconf.REGENERATE_TOPIC_SUMMARIES_URL, admin.RegenerateTopicSummariesHandler, ), get_redirect_route( r'%s' % feconf.GENERATE_STUDY_GUIDE_MODELS_URL, admin.GenerateStudyGuideModelsHandler, ), get_redirect_route( r'%s' % feconf.DELETE_STUDY_GUIDE_MODELS_URL, admin.DeleteStudyGuideModelsHandler, ), get_redirect_route( r'%s' % feconf.VERIFY_STUDY_GUIDE_MODELS_URL, admin.VerifyStudyGuideModelsHandler, ), get_redirect_route( r'/contributionrightshandler/<category>', contributor_dashboard_admin.ContributionRightsHandler, ), get_redirect_route( r'/getcontributorusershandler/<category>', contributor_dashboard_admin.ContributorUsersListHandler, ), get_redirect_route( r'/contributionrightsdatahandler', contributor_dashboard_admin.ContributionRightsDataHandler, ), get_redirect_route( r'%s/<contribution_type>/<contribution_subtype>' % (feconf.CONTRIBUTOR_DASHBOARD_ADMIN_STATS_URL_PREFIX), contributor_dashboard_admin.ContributorDashboardAdminStatsHandler, ), get_redirect_route( r'%s' % (feconf.COMMUNITY_CONTRIBUTION_STATS_URL), contributor_dashboard_admin.CommunityContributionStatsHandler, ), get_redirect_route( r'/translationcontributionstatshandler', contributor_dashboard_admin.TranslationContributionStatsHandler, ), get_redirect_route( r'%s/<contribution_type>/<contribution_subtype>/<username>' % (feconf.CONTRIBUTOR_STATS_SUMMARIES_URL), contributor_dashboard.ContributorStatsSummariesHandler, ), get_redirect_route( r'%s/<username>' % feconf.CONTRIBUTOR_ALL_STATS_SUMMARIES_URL, contributor_dashboard.ContributorAllStatsSummariesHandler, ), get_redirect_route( r'%s/<username>/<suggestion_type>' % (feconf.CONTRIBUTOR_CERTIFICATE_URL), contributor_dashboard.ContributorCertificateHandler, ), get_redirect_route( '/contributor_dashboard', creator_dashboard.OldContributorDashboardRedirectPage, ), get_redirect_route( '/creator_dashboard', creator_dashboard.OldCreatorDashboardRedirectPage ), get_redirect_route( r'%s' % feconf.CREATOR_DASHBOARD_DATA_URL, creator_dashboard.CreatorDashboardHandler, ), get_redirect_route( r'%s' % feconf.NEW_EXPLORATION_URL, creator_dashboard.NewExplorationHandler, ), get_redirect_route( r'%s' % feconf.NEW_COLLECTION_URL, creator_dashboard.NewCollectionHandler, ), get_redirect_route( r'%s' % feconf.FRACTIONS_LANDING_PAGE_URL, custom_landing_pages.FractionLandingRedirectPage, ), get_redirect_route( r'/learn/maths/<topic>', custom_landing_pages.TopicLandingRedirectPage ), get_redirect_route( r'%s/<opportunity_type>' % feconf.CONTRIBUTOR_OPPORTUNITIES_DATA_URL, contributor_dashboard.ContributionOpportunitiesHandler, ), get_redirect_route( r'%s' % feconf.CONTRIBUTOR_OPPORTUNITIES_DATA_V2_URL, contributor_dashboard.ContributionOpportunitiesHandlerV2, ), get_redirect_route( r'/preferredtranslationlanguage', contributor_dashboard.TranslationPreferenceHandler, ), get_redirect_route( r'%s' % feconf.REVIEWABLE_OPPORTUNITIES_URL, contributor_dashboard.ReviewableOpportunitiesHandler, ), get_redirect_route( r'%s' % feconf.REVIEWABLE_OPPORTUNITIES_V2_URL, contributor_dashboard.ReviewableOpportunitiesHandlerV2, ), get_redirect_route( r'%s' % feconf.PINNED_OPPORTUNITIES_URL, contributor_dashboard.LessonsPinningHandler, ), get_redirect_route( r'/gettranslatabletexthandler', contributor_dashboard.TranslatableTextHandler, ), get_redirect_route( r'%s' % feconf.TRANSLATABLE_CONTENTS_V2_URL, contributor_dashboard.TranslatableContentsHandlerV2, ), get_redirect_route( r'%s' % feconf.MACHINE_TRANSLATION_DATA_URL, contributor_dashboard.MachineTranslationStateTextsHandler, ), get_redirect_route( r'/usercontributionrightsdatahandler', contributor_dashboard.UserContributionRightsDataHandler, ), get_redirect_route( r'/retrievefeaturedtranslationlanguages', contributor_dashboard.FeaturedTranslationLanguagesHandler, ), get_redirect_route( r'/gettranslatabletopicnames', contributor_dashboard.TranslatableTopicNamesHandler, ), get_redirect_route( r'/gettranslatabletopicnamesperclassroom', contributor_dashboard.TranslatableTopicNamesPerClassroomHandler, ), get_redirect_route( r'%s' % feconf.NEW_SKILL_URL, topics_and_skills_dashboard.NewSkillHandler, ), get_redirect_route( r'%s' % feconf.NEW_QUESTION_URL, question_editor.QuestionCreationHandler ), get_redirect_route( r'%s/<comma_separated_skill_ids>' % feconf.QUESTIONS_LIST_URL_PREFIX, questions_list.QuestionsListHandler, ), get_redirect_route( r'%s/<comma_separated_skill_ids>' % feconf.QUESTION_COUNT_URL_PREFIX, questions_list.QuestionCountDataHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>/<topic_url_fragment>' % feconf.PRACTICE_SESSION_DATA_URL_PREFIX, practice_sessions.PracticeSessionsPageDataHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>/<topic_url_fragment>' r'/<node_id>' % feconf.PRACTICE_SESSION_DATA_URL_PREFIX, practice_sessions.PracticeSessionsPageDataHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>/<topic_url_fragment>' r'/arc/<arc_id>' % feconf.PRACTICE_SESSION_DATA_URL_PREFIX, practice_sessions.PracticeSessionsPageDataHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>/<topic_url_fragment>' r'/<story_url_fragment>' % feconf.REVIEW_TEST_DATA_URL_PREFIX, review_tests.ReviewTestsPageDataHandler, ), get_redirect_route( r'%s/can_access_review_tests_page/<classroom_url_fragment>' r'/<topic_url_fragment>/<story_url_fragment>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ReviewTestsPageAccessValidationHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>/<topic_url_fragment>' r'/<story_url_fragment>' % feconf.STORY_DATA_HANDLER, story_viewer.StoryPageDataHandler, ), get_redirect_route( r'%s/<story_url_fragment>' % feconf.STORY_URL_FRAGMENT_HANDLER, story_editor.StoryUrlFragmentHandler, ), get_redirect_route( r'%s/<topic_name>' % feconf.TOPIC_NAME_HANDLER, topic_viewer.TopicNameHandler, ), get_redirect_route( r'%s/<topic_url_fragment>' % feconf.TOPIC_URL_FRAGMENT_HANDLER, topic_editor.TopicUrlFragmentHandler, ), get_redirect_route( r'%s/<skill_description>' % feconf.SKILL_DESCRIPTION_HANDLER, skill_editor.SkillDescriptionHandler, ), get_redirect_route( r'%s/<skill_id>' % feconf.DIAGNOSTIC_TEST_SKILL_ASSIGNMENT_HANDLER, skill_editor.DiagnosticTestSkillAssignmentHandler, ), get_redirect_route( r'%s/' % feconf.TOPIC_ID_TO_DIAGNOSTIC_TEST_SKILL_IDS_HANDLER, topics_and_skills_dashboard.TopicIdToDiagnosticTestSkillIdsHandler, ), get_redirect_route( r'%s/<topic_id>' % feconf.DIAGNOSTIC_TEST_QUESTIONS_HANDLER_URL, diagnostic_test_player.DiagnosticTestQuestionsHandler, ), get_redirect_route( r'%s' % feconf.CLASSROOM_DISPLAY_INFO_HANDLER_URL, classroom.ClassroomDisplayInfoHandler, ), get_redirect_route( r'%s' % feconf.UPDATE_CLASSROOMS_ORDER_HANDLER_URL, classroom.UpdateClassroomIndexMappingHandler, ), get_redirect_route( r'%s' % feconf.UNUSED_TOPICS_HANDLER_URL, classroom.UnusedTopicsHandler ), get_redirect_route( r'%s' % feconf.NEW_CLASSROOM_ID_HANDLER_URL, classroom.NewClassroomIdHandler, ), get_redirect_route( r'%s' % feconf.NEW_CLASSROOM_HANDLER_URL, classroom.NewClassroomHandler ), get_redirect_route( r'%s' % feconf.TOPICS_TO_CLASSROOM_RELATION_HANDLER_URL, classroom.TopicsToClassroomsRelationHandler, ), get_redirect_route( r'%s' % feconf.ALL_CLASSROOMS_SUMMARY_HANDLER_URL, classroom.AllClassroomsSummaryHandler, ), get_redirect_route( r'%s/<classroom_id>' % feconf.CLASSROOM_HANDLER_URL, classroom.ClassroomHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>' % feconf.CLASSROOM_URL_FRAGMENT_HANDLER, classroom.ClassroomUrlFragmentHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>' % feconf.CLASSROOM_ID_HANDLER_URL, classroom.ClassroomIdHandler, ), get_redirect_route( r'%s' % feconf.VOICEOVER_ADMIN_DATA_HANDLER_URL, voiceover.VoiceoverAdminDataHandler, ), get_redirect_route( r'%s' % feconf.VOICEOVER_LANGUAGE_CODES_MAPPING_HANDLER_URL, voiceover.VoiceoverLanguageCodesMappingHandler, ), get_redirect_route( r'/entity_voiceovers_bulk_handler/<entity_type>/<entity_id>/' r'<entity_version>/<language_code>', voiceover.EntityVoiceoversBulkHandler, ), get_redirect_route( r'%s' % feconf.REGENERATE_AUTOMATIC_VOICEOVER_HANDLER_URL, voiceover.RegenerateAutomaticVoiceoverHandler, ), get_redirect_route( r'%s' % feconf.REGENERATE_VOICEOVER_ON_EXP_UPDATE_URL, voiceover.RegenerateVoiceoverOnExpUpdateHandler, ), get_redirect_route( r'/exploration_voiceovers_data/<exploration_id>', voiceover.ExplorationDataForVoiceoverRegenerationHandler, ), get_redirect_route( r'%s' % feconf.REGENERATE_VOICEOVERS_FOR_EXPLORATION_URL, voiceover.RegenerateVoiceoversForExplorationHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>/<topic_url_fragment>' r'/<story_url_fragment>/<node_id>' % feconf.STORY_PROGRESS_URL_PREFIX, story_viewer.StoryProgressHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>/<topic_url_fragment>' r'/<subtopic_url_fragment>' % feconf.SUBTOPIC_DATA_HANDLER, subtopic_viewer.SubtopicPageDataHandler, ), get_redirect_route( r'%s/<topic_id>' % feconf.TOPIC_EDITOR_STORY_URL, topic_editor.TopicEditorStoryHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>/<topic_url_fragment>' % feconf.TOPIC_DATA_HANDLER, topic_viewer.TopicPageDataHandler, ), get_redirect_route( r'%s/<classroom_url_fragment>' % feconf.CLASSROOM_DATA_HANDLER, classroom.ClassroomDataHandler, ), get_redirect_route( r'%s' % feconf.NEW_TOPIC_URL, topics_and_skills_dashboard.NewTopicHandler, ), get_redirect_route( r'%s' % feconf.UPLOAD_EXPLORATION_URL, creator_dashboard.UploadExplorationHandler, ), get_redirect_route( '/learner_dashboard', learner_dashboard.OldLearnerDashboardRedirectPage ), get_redirect_route( r'%s' % feconf.LEARNER_DASHBOARD_TOPIC_AND_STORY_DATA_URL, learner_dashboard.LearnerDashboardTopicsAndStoriesProgressHandler, ), get_redirect_route( r'%s' % feconf.LEARNER_COMPLETED_CHAPTERS_COUNT_DATA_URL, learner_dashboard.LearnerCompletedChaptersCountHandler, ), get_redirect_route( r'%s' % feconf.LEARNER_DASHBOARD_COLLECTION_DATA_URL, learner_dashboard.LearnerDashboardCollectionsProgressHandler, ), get_redirect_route( r'%s' % feconf.LEARNER_DASHBOARD_EXPLORATION_DATA_URL, learner_dashboard.LearnerDashboardExplorationsProgressHandler, ), get_redirect_route( r'%s' % feconf.FEEDBACK_UPDATES_DATA_URL, feedback_updates.FeedbackUpdatesHandler, ), get_redirect_route( r'%s' % feconf.LEARNER_DASHBOARD_IDS_DATA_URL, learner_dashboard.LearnerDashboardIdsHandler, ), get_redirect_route( r'%s/<thread_id>' % feconf.FEEDBACK_UPDATES_THREAD_DATA_URL, feedback_updates.FeedbackThreadHandler, ), get_redirect_route( r'%s' % feconf.MERGE_SKILLS_URL, topics_and_skills_dashboard.MergeSkillHandler, ), get_redirect_route( r'%s' % feconf.TOPICS_AND_SKILLS_DASHBOARD_DATA_URL, topics_and_skills_dashboard.TopicsAndSkillsDashboardPageDataHandler, ), get_redirect_route( r'%s/<skill_id>' % feconf.UNASSIGN_SKILL_DATA_HANDLER_URL, topics_and_skills_dashboard.TopicAssignmentsHandler, ), get_redirect_route( r'%s' % feconf.SKILL_DASHBOARD_DATA_URL, topics_and_skills_dashboard.SkillsDashboardPageDataHandler, ), get_redirect_route( r'%s/<activity_type>/<activity_id>' % feconf.LEARNER_INCOMPLETE_ACTIVITY_DATA_URL, reader.LearnerIncompleteActivityHandler, ), get_redirect_route( r'%s/<activity_type>/<topic_id>' % feconf.LEARNER_GOALS_DATA_URL, learner_goals.LearnerGoalsHandler, ), get_redirect_route( r'%s/<blog_post_url>' % feconf.BLOG_HOMEPAGE_DATA_URL, blog_homepage.BlogPostDataHandler, ), get_redirect_route( r'%s/<author_username>' % feconf.BLOG_AUTHOR_PROFILE_PAGE_DATA_URL_PREFIX, blog_homepage.AuthorsPageHandler, ), get_redirect_route( r'%s' % feconf.BLOG_HOMEPAGE_DATA_URL, blog_homepage.BlogHomepageDataHandler, ), get_redirect_route( r'%s' % feconf.BLOG_SEARCH_DATA_URL, blog_homepage.BlogPostSearchHandler ), get_redirect_route( r'/assetsdevhandler/<page_context>/<page_identifier>/' 'assets/<asset_type:(image|audio|thumbnail)>/<encoded_filename>', resources.AssetDevHandler, ), get_redirect_route( r'/value_generator_handler/<generator_id>', resources.ValueGeneratorHandler, ), get_redirect_route(r'/promo_bar_handler', resources.PromoBarHandler), get_redirect_route('/library', library.OldLibraryRedirectPage), get_redirect_route( r'%s' % feconf.LIBRARY_INDEX_DATA_URL, library.LibraryIndexHandler ), get_redirect_route( r'%s' % feconf.LIBRARY_GROUP_DATA_URL, library.LibraryGroupIndexHandler ), get_redirect_route( r'%s' % feconf.LIBRARY_SEARCH_DATA_URL, library.SearchHandler ), get_redirect_route(r'/gallery', library.LibraryRedirectPage), get_redirect_route(r'/contribute', library.LibraryRedirectPage), get_redirect_route(r'/playtest', library.LibraryRedirectPage), get_redirect_route( feconf.EXPLORATION_SUMMARIES_DATA_URL, library.ExplorationSummariesHandler, ), get_redirect_route( feconf.COLLECTION_SUMMARIES_DATA_URL, library.CollectionSummariesHandler ), get_redirect_route( r'/profilehandler/data/<username>', profile.ProfileHandler ), get_redirect_route( r'/mailinglistsubscriptionhandler', profile.MailingListSubscriptionHandler, ), get_redirect_route( r'%s/<secret>' % feconf.BULK_EMAIL_WEBHOOK_ENDPOINT, profile.BulkEmailWebhookEndpoint, ), get_redirect_route(feconf.PREFERENCES_DATA_URL, profile.PreferencesHandler), get_redirect_route(r'%s' % feconf.SIGNUP_URL, profile.SignupPage), get_redirect_route(r'%s' % feconf.SIGNUP_DATA_URL, profile.SignupHandler), get_redirect_route( feconf.DELETE_ACCOUNT_HANDLER_URL, profile.DeleteAccountHandler ), get_redirect_route( feconf.EXPORT_ACCOUNT_HANDLER_URL, profile.ExportAccountHandler ), get_redirect_route( r'%s' % feconf.USERNAME_CHECK_DATA_URL, profile.UsernameCheckHandler ), get_redirect_route( r'%s' % feconf.SITE_LANGUAGE_DATA_URL, profile.SiteLanguageHandler ), get_redirect_route(r'/userinfohandler', profile.UserInfoHandler), get_redirect_route(r'/userinfohandler/data', profile.UserInfoHandler), get_redirect_route(r'/url_handler', profile.UrlHandler), get_redirect_route( r'/moderatorhandler/featured', moderator.FeaturedActivitiesHandler ), get_redirect_route( r'/moderatorhandler/email_draft', moderator.EmailDraftHandler ), get_redirect_route( r'/memorycachehandler', release_coordinator.MemoryCacheHandler ), get_redirect_route( r'%s' % feconf.FEATURE_FLAGS_URL, release_coordinator.FeatureFlagsHandler, ), get_redirect_route( r'%s' % feconf.USER_GROUPS_HANDLER_URL, release_coordinator.UserGroupHandler, ), get_redirect_route( r'%s/can_access_exploration_player_page/<exploration_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ExplorationPlayerAccessValidationPage, ), get_redirect_route( r'%s/<exploration_id>' % feconf.EXPLORATION_INIT_URL_PREFIX, reader.ExplorationHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.EXPLORATION_PRETESTS_URL_PREFIX, reader.PretestHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.EXPLORATION_FEATURES_PREFIX, features.ExplorationFeaturesHandler, ), get_redirect_route( '/explorehandler/exploration_start_event/<exploration_id>', reader.ExplorationStartEventHandler, ), get_redirect_route( '/explorehandler/exploration_actual_start_event/<exploration_id>', reader.ExplorationActualStartEventHandler, ), get_redirect_route( '/explorehandler/solution_hit_event/<exploration_id>', reader.SolutionHitEventHandler, ), get_redirect_route( '/sync_logged_out_and_logged_in_progress/<exploration_id>', reader.SyncLoggedOutLearnerProgressHandler, ), get_redirect_route( r'/explorehandler/state_hit_event/<exploration_id>', reader.StateHitEventHandler, ), get_redirect_route( r'/explorehandler/state_complete_event/<exploration_id>', reader.StateCompleteEventHandler, ), get_redirect_route( r'/explorehandler/checkpoint_reached_by_logged_out_user/<exploration_id>', # pylint: disable=line-too-long reader.SaveTransientCheckpointProgressHandler, ), get_redirect_route( '/progress/<unique_progress_url_id>', reader.TransientCheckpointUrlPage ), get_redirect_route( r'/explorehandler/leave_for_refresher_exp_event/<exploration_id>', reader.LeaveForRefresherExpEventHandler, ), get_redirect_route( r'/explorehandler/answer_submitted_event/<exploration_id>', reader.AnswerSubmittedEventHandler, ), get_redirect_route( r'/explorehandler/checkpoint_reached/<exploration_id>', reader.CheckpointReachedEventHandler, ), get_redirect_route( r'/explorehandler/restart/<exploration_id>', reader.ExplorationRestartEventHandler, ), get_redirect_route( r'/explorehandler/give_feedback/<exploration_id>', reader.ReaderFeedbackHandler, ), get_redirect_route( r'/explorehandler/exploration_complete_event/<exploration_id>', reader.ExplorationCompleteEventHandler, ), get_redirect_route( r'/explorehandler/exploration_maybe_leave_event/<exploration_id>', reader.ExplorationMaybeLeaveHandler, ), get_redirect_route( r'/explorehandler/stats_events/<exploration_id>', reader.StatsEventsHandler, ), get_redirect_route( r'/explorehandler/store_playthrough/<exploration_id>', reader.StorePlaythroughHandler, ), get_redirect_route( r'/explorehandler/rating/<exploration_id>', reader.RatingHandler ), get_redirect_route( r'/explorehandler/recommendations/<exploration_id>', reader.RecommendationsHandler, ), get_redirect_route( r'%s/<entity_type>/<entity_id>' % (feconf.LEARNER_ANSWER_DETAILS_SUBMIT_URL), reader.LearnerAnswerDetailsSubmissionHandler, ), get_redirect_route( r'%s/<exploration_id>/<state_name>/<version>' % (feconf.STATE_VERSION_HISTORY_URL_PREFIX), reader.StateVersionHistoryHandler, ), get_redirect_route( r'%s/<exploration_id>/<version>' % (feconf.METADATA_VERSION_HISTORY_URL_PREFIX), reader.MetadataVersionHistoryHandler, ), get_redirect_route( r'%s/<question_id>' % feconf.QUESTION_EDITOR_DATA_URL_PREFIX, question_editor.EditableQuestionDataHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.EXPLORATION_DATA_PREFIX, editor.ExplorationHandler, ), get_redirect_route( r'/editsallowedhandler/<exploration_id>', editor.ExplorationEditsAllowedHandler, ), get_redirect_route( r'/createhandler/download/<exploration_id>', editor.ExplorationFileDownloader, ), get_redirect_route( r'%s/<entity_type>/<entity_id>' % (feconf.EXPLORATION_IMAGE_UPLOAD_PREFIX), editor.ImageUploadHandler, ), get_redirect_route( r'/createhandler/audioupload/<exploration_id>', voice_artist.AudioUploadHandler, ), get_redirect_route( r'/createhandler/state_yaml/<exploration_id>', editor.StateYamlHandler ), get_redirect_route( r'/createhandler/check_revert_valid/<exploration_id>/<version>', editor.ExplorationCheckRevertValidHandler, ), get_redirect_route( r'/createhandler/revert/<exploration_id>', editor.ExplorationRevertHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.EXPLORATION_RIGHTS_PREFIX, editor.ExplorationRightsHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.USER_PERMISSIONS_URL_PREFIX, editor.UserExplorationPermissionsHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.EXPLORATION_STATUS_PREFIX, editor.ExplorationStatusHandler, ), get_redirect_route( r'/createhandler/moderatorrights/<exploration_id>', editor.ExplorationModeratorRightsHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.USER_EXPLORATION_EMAILS_PREFIX, editor.UserExplorationEmailsHandler, ), get_redirect_route( r'/createhandler/snapshots/<exploration_id>', editor.ExplorationSnapshotsHandler, ), get_redirect_route( r'/createhandler/statistics/<exploration_id>', editor.ExplorationStatisticsHandler, ), get_redirect_route( r'/createhandler/state_interaction_stats/<exploration_id>/<state_name>', editor.StateInteractionStatsHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.EXPLORATION_STATE_ANSWER_STATS_PREFIX, editor.StateAnswerStatisticsHandler, ), get_redirect_route( r'/createhandler/started_tutorial_event/<exploration_id>', editor.StartedTutorialEventHandler, ), get_redirect_route( r'/createhandler/started_translation_tutorial_event/<exploration_id>', voice_artist.StartedTranslationTutorialEventHandler, ), get_redirect_route( r'/createhandler/autosave_draft/<exploration_id>', editor.EditorAutosaveHandler, ), get_redirect_route( r'/createhandler/get_top_unresolved_answers/<exploration_id>', editor.TopUnresolvedAnswersHandler, ), get_redirect_route( r'%s/<entity_type>/<entity_id>' % feconf.LEARNER_ANSWER_INFO_HANDLER_URL, editor.LearnerAnswerInfoHandler, ), get_redirect_route( r'/entity_translations_bulk_handler/<entity_type>/<entity_id>/<entity_version>', # pylint: disable=line-too-long editor.EntityTranslationsBulkHandler, ), get_redirect_route( r'%s' % feconf.RECENT_COMMITS_DATA_URL, recent_commits.RecentCommitsHandler, ), get_redirect_route( r'%s' % feconf.RECENT_FEEDBACK_MESSAGES_DATA_URL, feedback.RecentFeedbackMessagesHandler, ), get_redirect_route( r'%s/<thread_id>' % feconf.FEEDBACK_THREAD_VIEW_EVENT_URL, feedback.FeedbackThreadViewEventHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.FEEDBACK_THREADLIST_URL_PREFIX, feedback.ThreadListHandler, ), get_redirect_route( r'%s/<topic_id>' % feconf.FEEDBACK_THREADLIST_URL_PREFIX_FOR_TOPICS, feedback.ThreadListHandlerForTopicsHandler, ), get_redirect_route( r'%s/<thread_id>' % feconf.FEEDBACK_THREAD_URL_PREFIX, feedback.ThreadHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.FEEDBACK_STATS_URL_PREFIX, feedback.FeedbackStatsHandler, ), get_redirect_route( r'%s' % feconf.MY_FEEDBACK_URL, general_feedback.MyFeedbackListHandler, ), get_redirect_route( r'%s/<feedback_id>' % feconf.MY_FEEDBACK_URL, general_feedback.MyFeedbackDetailHandler, ), get_redirect_route( r'%s' % feconf.LESSON_FEEDBACK_URL, general_feedback.LessonFeedbackSubmitHandler, ), get_redirect_route( r'%s/<exploration_id>/<feedback_id>' % feconf.LESSON_FEEDBACK_URL, general_feedback.LessonFeedbackDetailHandler, ), get_redirect_route( r'%s/<exploration_id>' % feconf.LESSON_FEEDBACK_URL, general_feedback.LessonFeedbackListHandler, ), get_redirect_route( r'%s' % feconf.PLATFORM_FEEDBACK_URL, general_feedback.PlatformFeedbackSubmitHandler, ), get_redirect_route( r'%s/<dashboard>/<dashboard_id>/<report_id>' % feconf.PLATFORM_FEEDBACK_URL, general_feedback.PlatformFeedbackDetailHandler, ), get_redirect_route( r'%s/<dashboard>/<dashboard_id>' % feconf.PLATFORM_FEEDBACK_URL, general_feedback.PlatformFeedbackListHandler, ), get_redirect_route( r'%s' % feconf.GENERAL_FEEDBACK_CAPTCHA_CONFIG_URL, general_feedback.GeneralFeedbackCaptchaConfigHandler, ), get_redirect_route( r'%s/' % feconf.SUGGESTION_URL_PREFIX, suggestion.SuggestionHandler ), get_redirect_route( r'%s/<suggestion_id>' % feconf.UPDATE_TRANSLATION_SUGGESTION_URL_PREFIX, suggestion.UpdateTranslationSuggestionHandler, ), get_redirect_route( r'%s/<suggestion_id>' % feconf.UPDATE_QUESTION_SUGGESTION_URL_PREFIX, suggestion.UpdateQuestionSuggestionHandler, ), get_redirect_route( r'%s' % feconf.QUESTIONS_URL_PREFIX, reader.QuestionPlayerHandler ), get_redirect_route( r'%s/exploration/<target_id>/<suggestion_id>' % feconf.SUGGESTION_ACTION_URL_PREFIX, suggestion.SuggestionToExplorationActionHandler, ), get_redirect_route( r'%s/resubmit/<suggestion_id>' % feconf.SUGGESTION_ACTION_URL_PREFIX, suggestion.ResubmitSuggestionHandler, ), get_redirect_route( r'%s/skill/<target_id>/<suggestion_id>' % feconf.SUGGESTION_ACTION_URL_PREFIX, suggestion.SuggestionToSkillActionHandler, ), get_redirect_route( r'%s' % feconf.SUGGESTION_LIST_URL_PREFIX, suggestion.SuggestionListHandler, ), get_redirect_route( r'/getreviewablesuggestions/<target_type>/<suggestion_type>', suggestion.ReviewableSuggestionsHandler, ), get_redirect_route( r'/getsubmittedsuggestions/<target_type>/<suggestion_type>', suggestion.UserSubmittedSuggestionsHandler, ), get_redirect_route( r'%s' % feconf.SUBSCRIBE_URL_PREFIX, subscriptions.SubscribeHandler ), get_redirect_route( r'%s' % feconf.UNSUBSCRIBE_URL_PREFIX, subscriptions.UnsubscribeHandler ), get_redirect_route( r'%s/<exploration_id>' % feconf.FLAG_EXPLORATION_URL_PREFIX, reader.FlagExplorationHandler, ), get_redirect_route( r'%s/<collection_id>' % feconf.COLLECTION_DATA_URL_PREFIX, collection_viewer.CollectionDataHandler, ), get_redirect_route( r'%s/<collection_id>' % feconf.COLLECTION_EDITOR_DATA_URL_PREFIX, collection_editor.EditableCollectionDataHandler, ), get_redirect_route( r'%s/<collection_id>' % feconf.COLLECTION_RIGHTS_PREFIX, collection_editor.CollectionRightsHandler, ), get_redirect_route( r'%s/<collection_id>' % feconf.COLLECTION_PUBLISH_PREFIX, collection_editor.CollectionPublishHandler, ), get_redirect_route( r'%s/<collection_id>' % feconf.COLLECTION_UNPUBLISH_PREFIX, collection_editor.CollectionUnpublishHandler, ), get_redirect_route( r'%s/<topic_id>' % feconf.TOPIC_EDITOR_DATA_URL_PREFIX, topic_editor.EditableTopicDataHandler, ), get_redirect_route( r'%s/<topic_id>/<subtopic_id>' % feconf.SUBTOPIC_PAGE_EDITOR_DATA_URL_PREFIX, topic_editor.EditableSubtopicPageDataHandler, ), get_redirect_route( r'%s/<topic_id>/<subtopic_id>' % feconf.STUDY_GUIDE_EDITOR_DATA_URL_PREFIX, topic_editor.EditableStudyGuideDataHandler, ), get_redirect_route( r'%s/<topic_id>' % feconf.TOPIC_RIGHTS_URL_PREFIX, topic_editor.TopicRightsHandler, ), get_redirect_route( r'%s/<topic_id>' % feconf.TOPIC_STATUS_URL_PREFIX, topic_editor.TopicPublishHandler, ), get_redirect_route( r'%s/<topic_id>' % feconf.TOPIC_SEND_MAIL_URL_PREFIX, topic_editor.TopicPublishSendMailHandler, ), get_redirect_route( r'%s/' % feconf.TOPIC_ID_TO_TOPIC_NAME, topic_editor.TopicIdToTopicNameHandler, ), get_redirect_route( r'%s/<selected_skill_ids>' % feconf.CONCEPT_CARD_DATA_URL_PREFIX, concept_card_viewer.ConceptCardDataHandler, ), get_redirect_route( r'%s/<question_id>' % feconf.QUESTION_SKILL_LINK_URL_PREFIX, question_editor.QuestionSkillLinkHandler, ), get_redirect_route( r'%s/<comma_separated_skill_ids>' % feconf.SKILL_DATA_URL_PREFIX, skill_editor.SkillDataHandler, ), get_redirect_route( r'%s' % feconf.FETCH_SKILLS_URL_PREFIX, skill_editor.FetchSkillsHandler ), get_redirect_route( r'%s/can_access_skill_editor/<skill_id>' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.SkillEditorPageAccessValidationHandler, ), get_redirect_route( r'%s/<skill_id>' % feconf.SKILL_EDITOR_DATA_URL_PREFIX, skill_editor.EditableSkillDataHandler, ), get_redirect_route( r'%s' % feconf.SKILL_MASTERY_DATA_URL, skill_mastery.SkillMasteryDataHandler, ), get_redirect_route( r'%s/<skill_id>' % feconf.SKILL_RIGHTS_URL_PREFIX, skill_editor.SkillRightsHandler, ), get_redirect_route( r'%s' % feconf.SUBTOPIC_MASTERY_DATA_URL, skill_mastery.SubtopicMasteryDataHandler, ), get_redirect_route( r'%s/<story_id>' % feconf.STORY_EDITOR_DATA_URL_PREFIX, story_editor.EditableStoryDataHandler, ), get_redirect_route( r'%s/<story_id>' % feconf.STORY_PUBLISH_HANDLER, story_editor.StoryPublishHandler, ), get_redirect_route( r'%s/<story_id>' % feconf.VALIDATE_STORY_EXPLORATIONS_URL_PREFIX, story_editor.ValidateExplorationsHandler, ), get_redirect_route( r'%s' % feconf.EXPLORATION_METADATA_SEARCH_URL, collection_editor.ExplorationMetadataSearchHandler, ), get_redirect_route( r'/explorationdataextractionhandler', admin.DataExtractionQueryHandler ), get_redirect_route( r'/senddummymailtoadminhandler', admin.SendDummyMailToAdminHandler ), get_redirect_route(r'/updateusernamehandler', admin.UpdateUsernameHandler), get_redirect_route( r'/numberofdeletionrequestshandler', admin.NumberOfDeletionRequestsHandler, ), get_redirect_route( r'/verifyusermodelsdeletedhandler', admin.VerifyUserModelsDeletedHandler ), get_redirect_route(r'/deleteuserhandler', admin.DeleteUserHandler), get_redirect_route( r'/interactions', admin.InteractionsByExplorationIdHandler ), get_redirect_route(r'/frontend_errors', FrontendErrorHandler), get_redirect_route(r'/session_begin', base.SessionBeginHandler), get_redirect_route(r'/session_end', base.SessionEndHandler), get_redirect_route( r'%s/%s/<exploration_id>' % ( feconf.IMPROVEMENTS_URL_PREFIX, constants.TASK_ENTITY_TYPE_EXPLORATION, ), improvements.ExplorationImprovementsHandler, ), get_redirect_route( r'%s/%s/<exploration_id>' % ( feconf.IMPROVEMENTS_HISTORY_URL_PREFIX, constants.TASK_ENTITY_TYPE_EXPLORATION, ), improvements.ExplorationImprovementsHistoryHandler, ), get_redirect_route( r'%s/%s/<exploration_id>' % ( feconf.IMPROVEMENTS_CONFIG_URL_PREFIX, constants.TASK_ENTITY_TYPE_EXPLORATION, ), improvements.ExplorationImprovementsConfigHandler, ), get_redirect_route( r'%s' % feconf.BLOG_ADMIN_ROLE_HANDLER_URL, blog_admin.BlogAdminRolesHandler, ), get_redirect_route(r'/blogadminhandler', blog_admin.BlogAdminHandler), get_redirect_route('/beam_job', beam_jobs.BeamJobHandler), get_redirect_route('/beam_job_run', beam_jobs.BeamJobRunHandler), get_redirect_route( '/beam_job_run_result', beam_jobs.BeamJobRunResultHandler ), get_redirect_route( r'%s/<blog_post_id>' % feconf.BLOG_EDITOR_DATA_URL_PREFIX, blog_dashboard.BlogPostHandler, ), get_redirect_route( r'%s/<blog_post_id>' % feconf.BLOG_TITLE_HANDLER, blog_dashboard.BlogPostTitleHandler, ), get_redirect_route( r'%s' % feconf.BLOG_DASHBOARD_DATA_URL, blog_dashboard.BlogDashboardDataHandler, ), get_redirect_route( r'/issuesdatahandler/<exploration_id>', editor.FetchIssuesHandler ), get_redirect_route( r'/playthroughdatahandler/<exploration_id>/<playthrough_id>', editor.FetchPlaythroughHandler, ), get_redirect_route( r'/resolveissuehandler/<exploration_id>', editor.ResolveIssueHandler ), get_redirect_route(r'%s' % feconf.CSRF_HANDLER_URL, base.CsrfTokenHandler), get_redirect_route( r'/feature_flags_evaluation_handler', feature_flag.FeatureFlagsEvaluationHandler, ), get_redirect_route( r'/feature_flag_dummy_handler', feature_flag.FeatureFlagDummyHandler ), get_redirect_route( r'%s' % (android_validation_constants.INCOMING_ANDROID_FEEDBACK_REPORT_URL), incoming_app_feedback_report.IncomingAndroidFeedbackReportHandler, ), get_redirect_route( r'/voice_artist_management_handler/<entity_type>/<entity_id>', voice_artist.VoiceArtistManagementHandler, ), get_redirect_route( r'/topics_and_skills_dashboard/categorized_and_untriaged_skills_data', topics_and_skills_dashboard.CategorizedAndUntriagedSkillsDataHandler, ), get_redirect_route( r'/entity_translations_handler/<entity_type>/<entity_id>/' r'<entity_version>/<language_code>', reader.EntityTranslationHandler, ), get_redirect_route( r'/create_learner_group_handler', learner_group.CreateLearnerGroupHandler, ), get_redirect_route( r'/update_learner_group_handler/<learner_group_id>', learner_group.LearnerGroupHandler, ), get_redirect_route( r'/delete_learner_group_handler/<learner_group_id>', learner_group.LearnerGroupHandler, ), get_redirect_route( r'%s' % feconf.FACILITATOR_DASHBOARD_HANDLER, learner_group.FacilitatorDashboardHandler, ), get_redirect_route( r'%s' % feconf.LEARNER_DASHBOARD_LEARNER_GROUPS_HANDLER, learner_group.LearnerDashboardLearnerGroupsHandler, ), get_redirect_route( r'/view_learner_group_info_handler/<learner_group_id>', learner_group.ViewLearnerGroupInfoHandler, ), get_redirect_route( r'/learner_group_search_syllabus_handler', learner_group.LearnerGroupSearchSyllabusHandler, ), get_redirect_route( r'/learner_group_syllabus_handler/<learner_group_id>', learner_group.LearnerGroupSyllabusHandler, ), get_redirect_route( r'/learner_group_user_progress_handler/<learner_group_id>', learner_group.LearnerGroupLearnerProgressHandler, ), get_redirect_route( r'/learner_group_learner_specific_progress_handler/<learner_group_id>', learner_group.LearnerGroupLearnerSpecificProgressHandler, ), get_redirect_route( r'/learner_group_search_learner_handler', learner_group.LearnerGroupSearchLearnerHandler, ), get_redirect_route( r'/learner_group_learners_info_handler/<learner_group_id>', learner_group.LearnerGroupLearnersInfoHandler, ), get_redirect_route( r'/learner_group_learner_invitation_handler/<learner_group_id>', learner_group.LearnerGroupLearnerInvitationHandler, ), get_redirect_route( r'/learner_group_progress_sharing_permission_handler/<learner_group_id>', # pylint: disable=line-too-long learner_group.LearnerGroupProgressSharingPermissionHandler, ), get_redirect_route( r'/exit_learner_group_handler/<learner_group_id>', learner_group.ExitLearnerGroupHandler, ), get_redirect_route( r'/user_progress_in_stories_chapters_handler/<username>', learner_group.LearnerStoriesChaptersProgressHandler, ), get_redirect_route( '/learner_groups_feature_status_handler', learner_group.LearnerGroupsFeatureStatusHandler, ), get_redirect_route( r'/android_platform_parameters', android.AndroidPlatformParametersHandler, ), get_redirect_route( r'/android_feature_flags', android.AndroidFeatureFlagsHandler ), get_redirect_route('/android_data', android.AndroidActivityHandler), get_redirect_route( '/automatic_voiceover_regeneration_record', voiceover.AutomaticVoiceoverRegenerationRecordHandler, ), get_redirect_route( r'/exploration_voiceover_regeneration_status_url/<exploration_id>', voiceover.VoiceoverRegenerationRequestToCloudTaskHandler, ), get_redirect_route( feconf.CERTIFICATE_ASSESSMENT_OFFERING_HANDLER, certificate_assessment.CertificateAssessmentOfferingHandler, ), get_redirect_route( feconf.CERTIFICATE_ASSESSMENT_OFFERING_BY_ID_HANDLER, certificate_assessment.CertificateAssessmentOfferingByIdHandler, ), get_redirect_route( feconf.VALIDATE_CERTIFICATE_ASSESSMENT_OFFERING_HANDLER, certificate_assessment.ValidateCertificateAssessmentOfferingHandler, ), get_redirect_route( feconf.CERTIFICATE_ASSESSMENT_OFFERINGS_FOR_CLASSROOM_HANDLER, certificate_assessment.CertificateAssessmentOfferingsForClassroomHandler, ), get_redirect_route( feconf.START_CERTIFICATE_ASSESSMENT_HANDLER, certificate_assessment.StartCertificateAssessmentHandler, ), get_redirect_route( feconf.SUBMIT_CERTIFICATE_ASSESSMENT_HANDLER, certificate_assessment.SubmitCertificateAssessmentHandler, ), get_redirect_route( feconf.CERTIFICATE_ASSESSMENT_RESULT_HANDLER, certificate_assessment.CertificateAssessmentResultHandler, ), get_redirect_route( feconf.CERTIFICATE_ASSESSMENT_ATTEMPTS_HANDLER, certificate_assessment.CertificateAssessmentAttemptsHandler, ), ] # Adding redirects for topic landing pages. for subject, topics in constants.AVAILABLE_LANDING_PAGES.items(): for topic in topics: URLS.append( get_redirect_route( r'/%s/%s' % (subject, topic), oppia_root.OppiaRootPage ) ) if constants.DEV_MODE: URLS.append( get_redirect_route( r'/initialize_android_test_data', android.InitializeAndroidTestDataHandler, ) ) # Adding redirects for all stewards landing pages. for stewards_route in constants.STEWARDS_LANDING_PAGE['ROUTES']: URLS.append( get_redirect_route(r'/%s' % stewards_route, oppia_root.OppiaRootPage) ) # Redirect all routes handled using angular router to the oppia root page. for page in constants.PAGES_REGISTERED_WITH_FRONTEND.values(): if not 'MANUALLY_REGISTERED_WITH_BACKEND' in page: URLS.append( get_redirect_route(r'/%s' % page['ROUTE'], oppia_root.OppiaRootPage) ) # Manually redirect routes with url fragments to the oppia root page. URLS.extend( ( get_redirect_route(r'/profile/<username>', oppia_root.OppiaRootPage), get_redirect_route( r'/collection/<collection_id>', oppia_root.OppiaRootPage ), get_redirect_route( r'%s/story/<story_url_fragment>' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'/learn/<classroom_url_fragment>', oppia_root.OppiaRootPage ), get_redirect_route( r'%s/<exploration_id>' % feconf.EXPLORATION_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/<exploration_id>' % feconf.EXPLORATION_URL_EMBED_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s%s' % ( feconf.TOPIC_VIEWER_URL_PREFIX, feconf.PRACTICE_SESSION_URL_PREFIX, ), oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/practice/<node_id>' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/test/arc/<arc_id>' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/mastery-challenge' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/story' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/studyguide' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/studyguide/<subtopic_url_fragment>' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/practice' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage ), get_redirect_route( r'%s/<blog_post_url>' % feconf.BLOG_HOMEPAGE_URL, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/<topic_id>' % feconf.TOPIC_EDITOR_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/<author_username>' % feconf.BLOG_AUTHOR_PROFILE_PAGE_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/<exploration_id>' % feconf.EDITOR_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/<skill_id>' % feconf.SKILL_EDITOR_URL_PREFIX, oppia_root.OppiaRootPage, ), get_redirect_route( r'%s/<story_id>' % feconf.STORY_EDITOR_URL_PREFIX, oppia_root.OppiaRootPage, ), ) ) # Add cron urls. Note that cron URLs MUST start with /cron for them to work # in production (see dispatch() in base.py). URLS.extend( ( get_redirect_route( r'/cron/models/cleanup', cron.CronModelsCleanupHandler ), get_redirect_route( r'/cron/users/user_deletion', cron.CronUserDeletionHandler ), get_redirect_route( r'/cron/users/fully_complete_user_deletion', cron.CronFullyCompleteUserDeletionHandler, ), get_redirect_route( r'/cron/mail/admins/contributor_dashboard_bottlenecks', cron.CronMailAdminContributorDashboardBottlenecksHandler, ), get_redirect_route( r'/cron/mail/reviewers/contributor_dashboard_suggestions', cron.CronMailReviewersContributorDashboardSuggestionsHandler, ), get_redirect_route( r'/cron/app_feedback_report/scrub_expiring_reports', cron.CronAppFeedbackReportsScrubberHandlerPage, ), get_redirect_route( r'/cron/explorations/recommendations', cron.CronExplorationRecommendationsHandler, ), get_redirect_route( r'/cron/explorations/search_rank', cron.CronActivitySearchRankHandler, ), get_redirect_route( r'/cron/blog_posts/search_rank', cron.CronBlogPostSearchRankHandler ), get_redirect_route( r'/cron/cloud_task/mark_stale_cloud_task_run_as_failed', cron.CronMarkStaleCloudTaskRunModelsAsFailedHandler, ), get_redirect_route( r'/cron/cloud_task/mark_stale_voiceover_regeneration_content_as_failed', cron.CronMarkStaleVoiceoverRegenerationContentAsFailedHandler, ), get_redirect_route( r'/cron/users/dashboard_stats', cron.CronDashboardStatsHandler ), get_redirect_route( r'/cron/mail/curriculum_admins/chapter_publication_notfications', cron.CronMailChapterPublicationsNotificationsHandler, ), get_redirect_route( r'/cron/mail/reviewers/new_contributor_dashboard_suggestions', cron.CronMailReviewerNewSuggestionsHandler, ), ) ) # Add tasks urls. URLS.extend( ( get_redirect_route( r'%s' % feconf.TASK_URL_FEEDBACK_MESSAGE_EMAILS, tasks.UnsentFeedbackEmailHandler, ), get_redirect_route( r'%s' % ( feconf.TASK_URL_CONTRIBUTOR_DASHBOARD_ACHIEVEMENT_NOTIFICATION_EMAILS ), tasks.ContributorDashboardAchievementEmailHandler, ), get_redirect_route( r'%s' % feconf.TASK_URL_FLAG_EXPLORATION_EMAILS, tasks.FlagExplorationEmailHandler, ), get_redirect_route( r'%s' % feconf.TASK_URL_INSTANT_FEEDBACK_EMAILS, tasks.InstantFeedbackMessageEmailHandler, ), get_redirect_route( r'%s' % feconf.TASK_URL_FEEDBACK_STATUS_EMAILS, tasks.FeedbackThreadStatusChangeEmailHandler, ), get_redirect_route( r'%s' % feconf.TASK_URL_RETRY_FAILED_EMAIL, tasks.RetryEmailHandler, ), get_redirect_route( r'%s' % feconf.TASK_URL_DEFERRED, tasks.DeferredTasksHandler ), ) ) # Add URLs to static resources that differ between deployments. URLS.extend( ( get_redirect_route('/favicon.ico', resources.FaviconHandler), get_redirect_route('/robots.txt', resources.RobotsTxtHandler), get_redirect_route( '/assets/copyrighted-images/<folder>/<filename>', resources.CopyrightImagesHandler, ), ) ) # 404 error handler (Needs to be at the end of the URLS list). URLS.append(get_redirect_route(r'/<:.*>', base.Error404Handler)) class NdbWsgiMiddleware: """Wraps the WSGI application into the NDB client context.""" def __init__(self, wsgi_app: webapp2.WSGIApplication) -> None: self.wsgi_app = wsgi_app def __call__( self, environ: Dict[str, str], start_response: webapp2.Response ) -> webapp2.Response: global_cache = datastore_services.RedisCache( cache_services.get_cloud_ndb_redis_client() ) with datastore_services.get_ndb_context(global_cache=global_cache): return self.wsgi_app(environ, start_response) app_without_context = webapp2.WSGIApplication(URLS, debug=feconf.DEBUG) app = NdbWsgiMiddleware(app_without_context) # Only establish Firebase connection when not running backend tests. This allows # test discovery and collection without requiring Google Cloud credentials. if 'pytest' not in __import__('sys').modules: firebase_auth_services.establish_firebase_connection()