# 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 from core import feconf from core.constants import constants from core.controllers import access_validators from core.controllers import acl_decorators from core.controllers import admin from core.controllers import android from core.controllers import base from core.controllers import beam_jobs from core.controllers import blog_admin from core.controllers import blog_dashboard from core.controllers import blog_homepage from core.controllers import classifier from core.controllers import classroom from core.controllers import collection_editor from core.controllers import collection_viewer from core.controllers import concept_card_viewer from core.controllers import contributor_dashboard from core.controllers import contributor_dashboard_admin from core.controllers import creator_dashboard from core.controllers import cron from core.controllers import custom_landing_pages from core.controllers import diagnostic_test_player from core.controllers import editor from core.controllers import email_dashboard from core.controllers import feature_flag from core.controllers import features from core.controllers import feedback from core.controllers import feedback_updates from core.controllers import firebase from core.controllers import improvements from core.controllers import incoming_app_feedback_report from core.controllers import learner_dashboard from core.controllers import learner_goals from core.controllers import learner_group from core.controllers import learner_playlist from core.controllers import library from core.controllers import moderator from core.controllers import oppia_root from core.controllers import pages from core.controllers import practice_sessions from core.controllers import profile from core.controllers import question_editor from core.controllers import questions_list from core.controllers import reader from core.controllers import recent_commits from core.controllers import release_coordinator from core.controllers import resources from core.controllers import review_tests from core.controllers import skill_editor from core.controllers import skill_mastery from core.controllers import story_editor from core.controllers import story_viewer from core.controllers import subscriptions from core.controllers import subtopic_viewer from core.controllers import suggestion from core.controllers import tasks from core.controllers import topic_editor from core.controllers import topic_viewer from core.controllers import topics_and_skills_dashboard from core.controllers import voice_artist from core.controllers import voiceover from core.platform import models from core.platform.auth import firebase_auth_services import google.cloud.logging from typing import Dict, Optional, Type, TypeVar import webapp2 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 rtrieves 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.FirebaseProxyPage ), get_redirect_route(r'/_ah/warmup', WarmupPage), 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/' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.CollectionEditorAccessValidationPage ), get_redirect_route( r'%s/can_access_story_editor_page/' % 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( r'%s/can_access_subtopic_viewer_page///revision/' % # 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/' % 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/does_profile_exist/' % 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/' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.EditLearnerGroupPageAccessValidationHandler ), get_redirect_route( r'%s/does_learner_group_exist/' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ViewLearnerGroupPageAccessValidationHandler), get_redirect_route( r'%s/can_access_collection_player_page/' % 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_topic_viewer_page/' r'/' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.TopicViewerPageAccessValidationHandler ), get_redirect_route( r'%s/can_access_topic_viewer_page/' r'//story' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.TopicViewerPageAccessValidationHandler ), get_redirect_route( r'%s/can_access_topic_viewer_page/' r'//revision' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.TopicViewerPageAccessValidationHandler ), get_redirect_route( r'%s/can_access_topic_viewer_page/' r'//practice' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.TopicViewerPageAccessValidationHandler ), 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'/updateblogpostdatahandler', admin.UpdateBlogPostHandler), get_redirect_route( r'%s' % feconf.REGENERATE_TOPIC_SUMMARIES_URL, admin.RegenerateTopicSummariesHandler), get_redirect_route( r'/contributionrightshandler/', contributor_dashboard_admin.ContributionRightsHandler), get_redirect_route( r'/getcontributorusershandler/', contributor_dashboard_admin.ContributorUsersListHandler), get_redirect_route( r'/contributionrightsdatahandler', contributor_dashboard_admin.ContributionRightsDataHandler), get_redirect_route( r'%s//' % ( 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' % feconf.CONTRIBUTOR_DASHBOARD_URL, contributor_dashboard.ContributorDashboardPage), get_redirect_route( r'%s///' % ( feconf.CONTRIBUTOR_STATS_SUMMARIES_URL), contributor_dashboard.ContributorStatsSummariesHandler), get_redirect_route( r'%s/' % feconf.CONTRIBUTOR_ALL_STATS_SUMMARIES_URL, contributor_dashboard.ContributorAllStatsSummariesHandler), get_redirect_route( r'%s//' % ( 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/', custom_landing_pages.TopicLandingRedirectPage), get_redirect_route( r'%s/' % feconf.CONTRIBUTOR_OPPORTUNITIES_DATA_URL, contributor_dashboard.ContributionOpportunitiesHandler), 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.PINNED_OPPORTUNITIES_URL, contributor_dashboard.LessonsPinningHandler, ), get_redirect_route( r'/gettranslatabletexthandler', contributor_dashboard.TranslatableTextHandler), 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'%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/' % feconf.QUESTIONS_LIST_URL_PREFIX, questions_list.QuestionsListHandler), get_redirect_route( r'%s/' % feconf.QUESTION_COUNT_URL_PREFIX, questions_list.QuestionCountDataHandler), get_redirect_route( r'%s/practice/session' % feconf.TOPIC_VIEWER_URL_PREFIX, practice_sessions.PracticeSessionsPage), get_redirect_route( r'%s//' % feconf.PRACTICE_SESSION_DATA_URL_PREFIX, practice_sessions.PracticeSessionsPageDataHandler), get_redirect_route( r'%s//' r'/' % feconf.REVIEW_TEST_DATA_URL_PREFIX, review_tests.ReviewTestsPageDataHandler), get_redirect_route( r'%s/can_access_review_tests_page/' r'//' % feconf.ACCESS_VALIDATION_HANDLER_PREFIX, access_validators.ReviewTestsPageAccessValidationHandler), get_redirect_route( r'%s//' r'/' % feconf.STORY_DATA_HANDLER, story_viewer.StoryPageDataHandler), get_redirect_route( r'%s/' % feconf.STORY_URL_FRAGMENT_HANDLER, story_editor.StoryUrlFragmentHandler), get_redirect_route( r'%s/' % feconf.TOPIC_NAME_HANDLER, topic_editor.TopicNameHandler), get_redirect_route( r'%s/' % feconf.TOPIC_URL_FRAGMENT_HANDLER, topic_editor.TopicUrlFragmentHandler), get_redirect_route( r'%s/' % feconf.SKILL_DESCRIPTION_HANDLER, skill_editor.SkillDescriptionHandler), get_redirect_route( r'%s/' % 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/' % 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/' % feconf.CLASSROOM_HANDLER_URL, classroom.ClassroomHandler), get_redirect_route( r'%s/' % feconf.CLASSROOM_URL_FRAGMENT_HANDLER, classroom.ClassroomUrlFragmentHandler), get_redirect_route( r'%s/' % 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'%s' % feconf.VOICE_ARTIST_METADATA_HANDLER, voiceover.VoiceArtistMetadataHandler ), get_redirect_route( r'%s//' % feconf.GET_SAMPLE_VOICEOVERS_FOR_VOICE_ARTIST, voiceover.GetSampleVoiceoversForGivenVoiceArtistHandler ), get_redirect_route( r'/entity_voiceovers_bulk_handler///' r'/', voiceover.EntityVoiceoversBulkHandler ), get_redirect_route( r'%s//' r'//' % feconf.STORY_PROGRESS_URL_PREFIX, story_viewer.StoryProgressHandler), get_redirect_route( r'%s//' r'/' % feconf.SUBTOPIC_DATA_HANDLER, subtopic_viewer.SubtopicPageDataHandler), get_redirect_route( r'%s/' % feconf.TOPIC_EDITOR_STORY_URL, topic_editor.TopicEditorStoryHandler), get_redirect_route( r'%s//' % feconf.TOPIC_DATA_HANDLER, topic_viewer.TopicPageDataHandler), get_redirect_route( r'%s/' % 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/' % feconf.FEEDBACK_UPDATES_THREAD_DATA_URL, feedback_updates.FeedbackThreadHandler), get_redirect_route( r'%s' % feconf.TOPICS_AND_SKILLS_DASHBOARD_URL, topics_and_skills_dashboard.TopicsAndSkillsDashboardPage), 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/' % 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//' % feconf.LEARNER_INCOMPLETE_ACTIVITY_DATA_URL, reader.LearnerIncompleteActivityHandler), get_redirect_route( r'%s//' % feconf.LEARNER_GOALS_DATA_URL, learner_goals.LearnerGoalsHandler), get_redirect_route( r'%s//' % feconf.LEARNER_PLAYLIST_DATA_URL, learner_playlist.LearnerPlaylistHandler), get_redirect_route( r'%s/' % feconf.BLOG_HOMEPAGE_DATA_URL, blog_homepage.BlogPostDataHandler), get_redirect_route( r'%s/' % 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///' 'assets//', resources.AssetDevHandler), get_redirect_route( r'/value_generator_handler/', 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/', profile.ProfileHandler), get_redirect_route( r'/mailinglistsubscriptionhandler', profile.MailingListSubscriptionHandler), get_redirect_route( r'%s/' % 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/' % feconf.EXPLORATION_URL_PREFIX, reader.ExplorationPage), get_redirect_route( r'%s/' % feconf.EXPLORATION_URL_EMBED_PREFIX, reader.ExplorationEmbedPage), get_redirect_route( r'%s/' % feconf.EXPLORATION_INIT_URL_PREFIX, reader.ExplorationHandler), get_redirect_route( r'%s/' % feconf.EXPLORATION_PRETESTS_URL_PREFIX, reader.PretestHandler), get_redirect_route( r'%s/' % feconf.EXPLORATION_FEATURES_PREFIX, features.ExplorationFeaturesHandler), get_redirect_route( '/explorehandler/exploration_start_event/', reader.ExplorationStartEventHandler), get_redirect_route( '/explorehandler/exploration_actual_start_event/', reader.ExplorationActualStartEventHandler), get_redirect_route( '/explorehandler/solution_hit_event/', reader.SolutionHitEventHandler), get_redirect_route( '/sync_logged_out_and_logged_in_progress/', reader.SyncLoggedOutLearnerProgressHandler), get_redirect_route( r'/explorehandler/state_hit_event/', reader.StateHitEventHandler), get_redirect_route( r'/explorehandler/state_complete_event/', reader.StateCompleteEventHandler), get_redirect_route( r'/explorehandler/checkpoint_reached_by_logged_out_user/', # pylint: disable=line-too-long reader.SaveTransientCheckpointProgressHandler), get_redirect_route( '/progress/', reader.TransientCheckpointUrlPage), get_redirect_route( r'/explorehandler/leave_for_refresher_exp_event/', reader.LeaveForRefresherExpEventHandler), get_redirect_route( r'/explorehandler/answer_submitted_event/', reader.AnswerSubmittedEventHandler), get_redirect_route( r'/explorehandler/checkpoint_reached/', reader.CheckpointReachedEventHandler), get_redirect_route( r'/explorehandler/restart/', reader.ExplorationRestartEventHandler), get_redirect_route( r'/explorehandler/give_feedback/', reader.ReaderFeedbackHandler), get_redirect_route( r'/explorehandler/exploration_complete_event/', reader.ExplorationCompleteEventHandler), get_redirect_route( r'/explorehandler/exploration_maybe_leave_event/', reader.ExplorationMaybeLeaveHandler), get_redirect_route( r'/explorehandler/stats_events/', reader.StatsEventsHandler), get_redirect_route( r'/explorehandler/store_playthrough/', reader.StorePlaythroughHandler), get_redirect_route( r'/explorehandler/rating/', reader.RatingHandler), get_redirect_route( r'/explorehandler/recommendations/', reader.RecommendationsHandler), get_redirect_route( r'%s//' % ( feconf.LEARNER_ANSWER_DETAILS_SUBMIT_URL), reader.LearnerAnswerDetailsSubmissionHandler), get_redirect_route( r'%s///' % ( feconf.STATE_VERSION_HISTORY_URL_PREFIX ), reader.StateVersionHistoryHandler), get_redirect_route( r'%s//' % ( feconf.METADATA_VERSION_HISTORY_URL_PREFIX ), reader.MetadataVersionHistoryHandler), get_redirect_route( r'%s/' % feconf.QUESTION_EDITOR_DATA_URL_PREFIX, question_editor.EditableQuestionDataHandler), get_redirect_route( r'%s/' % feconf.EDITOR_URL_PREFIX, editor.ExplorationPage), get_redirect_route( r'%s/' % feconf.EXPLORATION_DATA_PREFIX, editor.ExplorationHandler), get_redirect_route( r'/editsallowedhandler/', editor.ExplorationEditsAllowedHandler), get_redirect_route( r'/createhandler/download/', editor.ExplorationFileDownloader), get_redirect_route( r'%s//' % ( feconf.EXPLORATION_IMAGE_UPLOAD_PREFIX), editor.ImageUploadHandler), get_redirect_route( r'/createhandler/audioupload/', voice_artist.AudioUploadHandler), get_redirect_route( r'/createhandler/state_yaml/', editor.StateYamlHandler), get_redirect_route( r'/createhandler/check_revert_valid//', editor.ExplorationCheckRevertValidHandler), get_redirect_route( r'/createhandler/revert/', editor.ExplorationRevertHandler), get_redirect_route( r'%s/' % feconf.EXPLORATION_RIGHTS_PREFIX, editor.ExplorationRightsHandler), get_redirect_route( r'%s/' % feconf.USER_PERMISSIONS_URL_PREFIX, editor.UserExplorationPermissionsHandler), get_redirect_route( r'%s/' % feconf.EXPLORATION_STATUS_PREFIX, editor.ExplorationStatusHandler), get_redirect_route( r'/createhandler/moderatorrights/', editor.ExplorationModeratorRightsHandler), get_redirect_route( r'%s/' % feconf.USER_EXPLORATION_EMAILS_PREFIX, editor.UserExplorationEmailsHandler), get_redirect_route( r'/createhandler/snapshots/', editor.ExplorationSnapshotsHandler), get_redirect_route( r'/createhandler/statistics/', editor.ExplorationStatisticsHandler), get_redirect_route( r'/createhandler/state_interaction_stats//', editor.StateInteractionStatsHandler), get_redirect_route( r'%s/' % feconf.EXPLORATION_STATE_ANSWER_STATS_PREFIX, editor.StateAnswerStatisticsHandler), get_redirect_route( r'/createhandler/started_tutorial_event/', editor.StartedTutorialEventHandler), get_redirect_route( r'/createhandler/started_translation_tutorial_event/', voice_artist.StartedTranslationTutorialEventHandler), get_redirect_route( r'/createhandler/autosave_draft/', editor.EditorAutosaveHandler), get_redirect_route( r'/createhandler/get_top_unresolved_answers/', editor.TopUnresolvedAnswersHandler), get_redirect_route( r'%s//' % feconf.LEARNER_ANSWER_INFO_HANDLER_URL, editor.LearnerAnswerInfoHandler), get_redirect_route( r'/entity_translations_bulk_handler///', # 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/' % feconf.FEEDBACK_THREAD_VIEW_EVENT_URL, feedback.FeedbackThreadViewEventHandler), get_redirect_route( r'%s/' % feconf.FEEDBACK_THREADLIST_URL_PREFIX, feedback.ThreadListHandler), get_redirect_route( r'%s/' % feconf.FEEDBACK_THREADLIST_URL_PREFIX_FOR_TOPICS, feedback.ThreadListHandlerForTopicsHandler), get_redirect_route( r'%s/' % feconf.FEEDBACK_THREAD_URL_PREFIX, feedback.ThreadHandler), get_redirect_route( r'%s/' % feconf.FEEDBACK_STATS_URL_PREFIX, feedback.FeedbackStatsHandler), get_redirect_route( r'%s/' % feconf.SUGGESTION_URL_PREFIX, suggestion.SuggestionHandler), get_redirect_route( r'%s/' % feconf.UPDATE_TRANSLATION_SUGGESTION_URL_PREFIX, suggestion.UpdateTranslationSuggestionHandler), get_redirect_route( r'%s/' % 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//' % feconf.SUGGESTION_ACTION_URL_PREFIX, suggestion.SuggestionToExplorationActionHandler), get_redirect_route( r'%s/resubmit/' % feconf.SUGGESTION_ACTION_URL_PREFIX, suggestion.ResubmitSuggestionHandler), get_redirect_route( r'%s/skill//' % feconf.SUGGESTION_ACTION_URL_PREFIX, suggestion.SuggestionToSkillActionHandler), get_redirect_route( r'%s' % feconf.SUGGESTION_LIST_URL_PREFIX, suggestion.SuggestionListHandler), get_redirect_route( r'/getreviewablesuggestions//', suggestion.ReviewableSuggestionsHandler), get_redirect_route( r'/getsubmittedsuggestions//', 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/' % feconf.FLAG_EXPLORATION_URL_PREFIX, reader.FlagExplorationHandler), get_redirect_route( r'%s/' % feconf.COLLECTION_DATA_URL_PREFIX, collection_viewer.CollectionDataHandler), get_redirect_route( r'%s/' % feconf.COLLECTION_EDITOR_DATA_URL_PREFIX, collection_editor.EditableCollectionDataHandler), get_redirect_route( r'%s/' % feconf.COLLECTION_RIGHTS_PREFIX, collection_editor.CollectionRightsHandler), get_redirect_route( r'%s/' % feconf.COLLECTION_PUBLISH_PREFIX, collection_editor.CollectionPublishHandler), get_redirect_route( r'%s/' % feconf.COLLECTION_UNPUBLISH_PREFIX, collection_editor.CollectionUnpublishHandler), get_redirect_route( r'%s/' % feconf.TOPIC_EDITOR_URL_PREFIX, topic_editor.TopicEditorPage), get_redirect_route( r'%s/' % feconf.TOPIC_EDITOR_DATA_URL_PREFIX, topic_editor.EditableTopicDataHandler), get_redirect_route( r'%s//' % feconf.SUBTOPIC_PAGE_EDITOR_DATA_URL_PREFIX, topic_editor.EditableSubtopicPageDataHandler), get_redirect_route( r'%s/' % feconf.TOPIC_RIGHTS_URL_PREFIX, topic_editor.TopicRightsHandler), get_redirect_route( r'%s/' % feconf.TOPIC_STATUS_URL_PREFIX, topic_editor.TopicPublishHandler), get_redirect_route( r'%s/' % 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/' % feconf.CONCEPT_CARD_DATA_URL_PREFIX, concept_card_viewer.ConceptCardDataHandler), get_redirect_route( r'%s/' % feconf.QUESTION_SKILL_LINK_URL_PREFIX, question_editor.QuestionSkillLinkHandler), get_redirect_route( r'%s/' % 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/' % feconf.SKILL_EDITOR_URL_PREFIX, skill_editor.SkillEditorPage), get_redirect_route( r'%s/' % 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/' % 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/' % feconf.STORY_EDITOR_DATA_URL_PREFIX, story_editor.EditableStoryDataHandler), get_redirect_route( r'%s/' % feconf.STORY_PUBLISH_HANDLER, story_editor.StoryPublishHandler), get_redirect_route( r'%s/' % feconf.VALIDATE_STORY_EXPLORATIONS_URL_PREFIX, story_editor.ValidateExplorationsHandler), get_redirect_route( r'/emaildashboarddatahandler', email_dashboard.EmailDashboardDataHandler), get_redirect_route( r'/querystatuscheck', email_dashboard.QueryStatusCheckHandler), get_redirect_route( r'/emaildashboardcancelresult/', email_dashboard.EmailDashboardCancelEmailHandler), get_redirect_route( r'/emaildashboardtestbulkemailhandler/', email_dashboard.EmailDashboardTestBulkEmailHandler), 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/' % ( feconf.IMPROVEMENTS_URL_PREFIX, constants.TASK_ENTITY_TYPE_EXPLORATION), improvements.ExplorationImprovementsHandler), get_redirect_route( r'%s/%s/' % ( feconf.IMPROVEMENTS_HISTORY_URL_PREFIX, constants.TASK_ENTITY_TYPE_EXPLORATION), improvements.ExplorationImprovementsHistoryHandler), get_redirect_route( r'%s/%s/' % ( 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/' % feconf.BLOG_EDITOR_DATA_URL_PREFIX, blog_dashboard.BlogPostHandler), get_redirect_route( r'%s/' % 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/', editor.FetchIssuesHandler), get_redirect_route( r'/ml/trainedclassifierhandler', classifier.TrainedClassifierHandler), get_redirect_route( r'/ml/nextjobhandler', classifier.NextJobHandler), get_redirect_route( r'/playthroughdatahandler//', editor.FetchPlaythroughHandler), get_redirect_route( r'/resolveissuehandler/', 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//', 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///' r'/', reader.EntityTranslationHandler), get_redirect_route( r'/create_learner_group_handler', learner_group.CreateLearnerGroupHandler), get_redirect_route( r'/update_learner_group_handler/', learner_group.LearnerGroupHandler), get_redirect_route( r'/delete_learner_group_handler/', 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.ViewLearnerGroupInfoHandler), get_redirect_route( r'/learner_group_search_syllabus_handler', learner_group.LearnerGroupSearchSyllabusHandler), get_redirect_route( r'/learner_group_syllabus_handler/', learner_group.LearnerGroupSyllabusHandler), get_redirect_route( r'/learner_group_user_progress_handler/', learner_group.LearnerGroupLearnerProgressHandler), get_redirect_route( r'/learner_group_learner_specific_progress_handler/', 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.LearnerGroupLearnersInfoHandler), get_redirect_route( r'/learner_group_learner_invitation_handler/', learner_group.LearnerGroupLearnerInvitationHandler), get_redirect_route( r'/learner_group_progress_sharing_permission_handler/', # pylint: disable=line-too-long learner_group.LearnerGroupProgressSharingPermissionHandler), get_redirect_route( r'/exit_learner_group_handler/', learner_group.ExitLearnerGroupHandler), get_redirect_route( r'/user_progress_in_stories_chapters_handler/', learner_group.LearnerStoriesChaptersProgressHandler), get_redirect_route( '/learner_groups_feature_status_handler', learner_group.LearnerGroupsFeatureStatusHandler), get_redirect_route('/android_data', android.AndroidActivityHandler) ] # 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: if 'LIGHTWEIGHT' in page: URLS.append( get_redirect_route( r'/%s' % page['ROUTE'], oppia_root.OppiaLightweightRootPage ) ) else: 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/', oppia_root.OppiaRootPage), get_redirect_route( r'%s/story/' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage), get_redirect_route( r'/learn/', oppia_root.OppiaRootPage ), get_redirect_route( r'%s/story' % feconf.TOPIC_VIEWER_URL_PREFIX, oppia_root.OppiaRootPage ), get_redirect_route( r'%s/revision' % 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/' % feconf.BLOG_HOMEPAGE_URL, oppia_root.OppiaRootPage ), get_redirect_route( r'%s/' % feconf.BLOG_AUTHOR_PROFILE_PAGE_URL_PREFIX, oppia_root.OppiaRootPage ), get_redirect_route( r'%s/' % 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/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_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//', 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.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) firebase_auth_services.establish_firebase_connection()