示例#1
0
def send_suggestion_email(exploration_title, exploration_id, author_id, recipient_list):
    """Send emails to notify the given recipients about new suggestion.

    Each recipient will only be emailed if their email preferences allow for
    incoming feedback message emails.

    Args:
        exploration_title: str. Title of the exploration with the new
            suggestion.
        exploration_id: str. The ID of the exploration with the new suggestion.
        author_id: str. The user ID of the author of the suggestion.
        recipient_list: list(str). The user IDs of the email recipients.
    """

    email_subject = 'New suggestion for "%s"' % exploration_title

    email_body_template = (
        "Hi %s,<br>"
        "%s has submitted a new suggestion for your Oppia exploration, "
        '<a href="https://www.oppia.org/create/%s">"%s"</a>.<br>'
        "You can accept or reject this suggestion by visiting the "
        '<a href="https://www.oppia.org/create/%s#/feedback">feedback page</a> '
        "for your exploration.<br>"
        "<br>"
        "Thanks!<br>"
        "- The Oppia Team<br>"
        "<br>%s"
    )

    if not feconf.CAN_SEND_EMAILS:
        log_new_error("This app cannot send emails to users.")
        return

    if not feconf.CAN_SEND_FEEDBACK_MESSAGE_EMAILS:
        log_new_error("This app cannot send feedback message emails to users.")
        return

    author_settings = user_services.get_user_settings(author_id)
    for recipient_id in recipient_list:
        recipient_user_settings = user_services.get_user_settings(recipient_id)
        recipient_preferences = user_services.get_email_preferences(recipient_id)

        if recipient_preferences["can_receive_feedback_message_email"]:
            # Send email only if recipient wants to receive.
            email_body = email_body_template % (
                recipient_user_settings.username,
                author_settings.username,
                exploration_id,
                exploration_title,
                exploration_id,
                EMAIL_FOOTER.value,
            )
            _send_email(
                recipient_id,
                feconf.SYSTEM_COMMITTER_ID,
                feconf.EMAIL_INTENT_SUGGESTION_NOTIFICATION,
                email_subject,
                email_body,
                feconf.NOREPLY_EMAIL_ADDRESS,
            )
示例#2
0
    def test_contribution_msec_updates_on_published_explorations(self):
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, self.admin_id, end_state_name='End')
        init_state_name = exploration.init_state_name
        exp_services.publish_exploration_and_update_user_profiles(
            self.admin_id, self.EXP_ID)

        # Test all owners and editors of exploration after publication have
        # updated first contribution times in msecs.
        self.assertIsNotNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)

        # Test editor of published exploration has updated contribution time.
        rights_manager.release_ownership_of_exploration(
            self.admin_id, self.EXP_ID)

        exp_services.update_exploration(
            self.editor_id, self.EXP_ID, [{
                'cmd': 'edit_state_property',
                'state_name': init_state_name,
                'property_name': 'widget_id',
                'new_value': 'MultipleChoiceInput'
            }], 'commit')

        self.assertIsNotNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)
    def test_last_exp_edit_time_gets_updated(self):
        exp_services.update_exploration(self.editor_id, self.EXP_ID, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')

        # Decrease last exploration edited time by 13 hours.
        user_settings = user_services.get_user_settings(self.editor_id)
        user_settings.last_edited_an_exploration = (
            user_settings.last_edited_an_exploration -
            datetime.timedelta(hours=13))
        user_services._save_user_settings(user_settings) # pylint: disable=protected-access

        editor_settings = user_services.get_user_settings(self.editor_id)
        previous_last_edited_an_exploration = (
            editor_settings.last_edited_an_exploration)
        self.assertIsNotNone(previous_last_edited_an_exploration)

        # The editor edits the exploration 13 hours after it was created.
        exp_services.update_exploration(self.editor_id, self.EXP_ID, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'new objective'
        }], 'Test edit 2')

        # Make sure last exploration edited time gets updated.
        editor_settings = user_services.get_user_settings(self.editor_id)
        self.assertGreater(
            (editor_settings.last_edited_an_exploration),
            previous_last_edited_an_exploration)
示例#4
0
    def test_contribution_msec_updates_on_published_collections(self):
        self.save_new_valid_collection(
            self.COL_ID, self.admin_id, title=self.COLLECTION_TITLE,
            category=self.COLLECTION_CATEGORY,
            objective=self.COLLECTION_OBJECTIVE,
            exploration_id=self.EXP_ID)

        collection_services.publish_collection_and_update_user_profiles(
            self.admin_id, self.COL_ID)
        exp_services.publish_exploration_and_update_user_profiles(
            self.admin_id, self.EXP_ID)

        # Test all owners and editors of collection after publication have
        # updated first contribution times.
        self.assertIsNotNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)

        # Test editor of published collection has updated
        # first contribution time.
        rights_manager.release_ownership_of_collection(
            self.admin_id, self.COL_ID)

        collection_services.update_collection(
            self.editor_id, self.COL_ID, [{
                'cmd': 'edit_collection_property',
                'property_name': 'title',
                'new_value': 'Some new title'
            }], 'Changed the title')

        self.assertIsNotNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)
    def setUp(self):
        super(UserQueryJobOneOffTests, self).setUp()
        # User A has no created or edited explorations
        # User B has one created exploration
        # User C has one edited exploration
        # User D has created an exploration and then edited it.
        # User E has created an exploration 10 days before.
        # Submitter is the user who submits the query.
        self.signup(self.USER_A_EMAIL, self.USER_A_USERNAME)
        self.user_a_id = self.get_user_id_from_email(self.USER_A_EMAIL)
        self.signup(self.USER_B_EMAIL, self.USER_B_USERNAME)
        self.user_b_id = self.get_user_id_from_email(self.USER_B_EMAIL)
        self.signup(self.USER_C_EMAIL, self.USER_C_USERNAME)
        self.user_c_id = self.get_user_id_from_email(self.USER_C_EMAIL)
        self.signup(self.USER_D_EMAIL, self.USER_D_USERNAME)
        self.user_d_id = self.get_user_id_from_email(self.USER_D_EMAIL)
        self.signup(self.USER_E_EMAIL, self.USER_E_USERNAME)
        self.user_e_id = self.get_user_id_from_email(self.USER_E_EMAIL)
        self.signup(self.USER_SUBMITTER_EMAIL, self.USER_SUBMITTER_USERNAME)
        self.submitter_id = self.get_user_id_from_email(
            self.USER_SUBMITTER_EMAIL)

        self.save_new_valid_exploration(
            self.EXP_ID_1, self.user_b_id, end_state_name='End')

        exp_services.update_exploration(self.user_c_id, self.EXP_ID_1, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')

        self.save_new_valid_exploration(
            self.EXP_ID_2, self.user_d_id, end_state_name='End')

        exp_services.update_exploration(self.user_d_id, self.EXP_ID_2, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')

        self.save_new_valid_exploration(
            self.EXP_ID_3, self.user_e_id, end_state_name='End')
        user_e_settings = user_services.get_user_settings(self.user_e_id)
        user_e_settings.last_created_an_exploration = (
            user_e_settings.last_created_an_exploration -
            datetime.timedelta(days=10))
        # Last edited time also changes when user creates an explorationan.
        user_e_settings.last_edited_an_exploration = (
            datetime.datetime.utcnow() - datetime.timedelta(days=10))
        user_e_settings.last_logged_in = (
            user_e_settings.last_logged_in - datetime.timedelta(days=10))
        user_services._save_user_settings(user_e_settings) # pylint: disable=protected-access

        user_a_settings = user_services.get_user_settings(self.user_a_id)
        user_a_settings.last_logged_in = (
            user_a_settings.last_logged_in - datetime.timedelta(days=3))
        user_services._save_user_settings(user_a_settings) # pylint: disable=protected-access

        # Set tmpsuperadm1n as admin in ADMIN_USERNAMES config property.
        self.set_admins(['tmpsuperadm1n'])
示例#6
0
def send_moderator_action_email(
        sender_id, recipient_id, intent, exploration_title, email_body):
    """Sends a email immediately following a moderator action (publicize,
    unpublish, delete) to the given user.

    The caller is responsible for ensuring that emails are allowed to be sent
    to users (i.e. feconf.CAN_SEND_EMAILS_TO_USERS is True).
    """
    require_moderator_email_prereqs_are_satisfied()
    email_config = feconf.VALID_MODERATOR_ACTIONS[intent]

    recipient_user_settings = user_services.get_user_settings(recipient_id)
    sender_user_settings = user_services.get_user_settings(sender_id)
    email_subject = feconf.VALID_MODERATOR_ACTIONS[intent]['email_subject_fn'](
        exploration_title)
    email_salutation_html = email_config['email_salutation_html_fn'](
        recipient_user_settings.username)
    email_signoff_html = email_config['email_signoff_html_fn'](
        sender_user_settings.username)

    full_email_content = (
        '%s<br><br>%s<br><br>%s<br><br>%s' % (
            email_salutation_html, email_body, email_signoff_html,
            EMAIL_FOOTER.value))
    _send_email(
        recipient_id, sender_id, intent, email_subject, full_email_content,
        bcc_admin=True)
    def test_contribution_msec_updates_on_published_explorations(self):
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, self.admin_id, end_state_name='End')
        init_state_name = exploration.init_state_name

        # Test that no contribution time is set.
        job_id = (
            user_jobs_one_off.UserFirstContributionMsecOneOffJob.create_new())
        user_jobs_one_off.UserFirstContributionMsecOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        self.assertIsNone(
            user_services.get_user_settings(
                self.admin_id).first_contribution_msec)

        # Test all owners and editors of exploration after publication have
        # updated times.
        exp_services.publish_exploration_and_update_user_profiles(
            self.admin_id, self.EXP_ID)
        rights_manager.release_ownership_of_exploration(
            self.admin_id, self.EXP_ID)
        exp_services.update_exploration(
            self.editor_id, self.EXP_ID, [{
                'cmd': 'edit_state_property',
                'state_name': init_state_name,
                'property_name': 'widget_id',
                'new_value': 'MultipleChoiceInput'
            }], 'commit')
        job_id = (
            user_jobs_one_off.UserFirstContributionMsecOneOffJob.create_new())
        user_jobs_one_off.UserFirstContributionMsecOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        self.assertIsNotNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)
        self.assertIsNotNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)
    def test_that_last_edited_time_is_updated(self):
        self.login(self.OWNER_EMAIL)
        self.save_new_valid_exploration(
            self.exp_id, self.owner_id, end_state_name='End')
        self.logout()
        self.login(self.EDITOR_EMAIL)
        exp_services.update_exploration(self.editor_id, self.exp_id, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')
        self.logout()

        user_settings = user_services.get_user_settings(self.editor_id)
        user_settings.last_edited_an_exploration = None
        user_services._save_user_settings(user_settings)  # pylint: disable=protected-access

        editor_settings = user_services.get_user_settings(self.editor_id)

        self.assertIsNone(editor_settings.last_created_an_exploration)
        self.assertIsNone(editor_settings.last_edited_an_exploration)

        self._run_one_off_job()

        editor_settings = user_services.get_user_settings(self.editor_id)

        self.assertIsNotNone(editor_settings.last_edited_an_exploration)
        self.assertIsNone(editor_settings.last_created_an_exploration)
示例#9
0
def send_moderator_action_email(sender_id, recipient_id, intent, exploration_title, email_body):
    """Sends a email immediately following a moderator action (publicize,
    unpublish, delete) to the given user.

    Raises an exception if emails are not allowed to be sent to users (i.e.
    feconf.CAN_SEND_EMAILS is False).

    Args:
        sender_id: str. User ID of the sender.
        recipient_id: str. User ID of the recipient.
        intent: str. The intent string (cause/purpose) of the email.
        exploration_title: str. The title of the exploration on which the
            moderator action was taken.
        email_body: str. The email content/message.
    """

    require_moderator_email_prereqs_are_satisfied()
    email_config = feconf.VALID_MODERATOR_ACTIONS[intent]

    recipient_user_settings = user_services.get_user_settings(recipient_id)
    sender_user_settings = user_services.get_user_settings(sender_id)
    email_subject = feconf.VALID_MODERATOR_ACTIONS[intent]["email_subject_fn"](exploration_title)
    email_salutation_html = email_config["email_salutation_html_fn"](recipient_user_settings.username)
    email_signoff_html = email_config["email_signoff_html_fn"](sender_user_settings.username)

    full_email_content = "%s<br><br>%s<br><br>%s<br><br>%s" % (
        email_salutation_html,
        email_body,
        email_signoff_html,
        EMAIL_FOOTER.value,
    )
    _send_email(
        recipient_id, sender_id, intent, email_subject, full_email_content, feconf.SYSTEM_EMAIL_ADDRESS, bcc_admin=True
    )
示例#10
0
    def test_last_exp_edit_time_gets_updated(self):
        self.save_new_valid_exploration(
            self.EXP_ID_A, self.owner_id, end_state_name='End')

        # Decrease last exploration created time by 13 hours.
        user_settings = user_services.get_user_settings(self.owner_id)
        user_settings.last_created_an_exploration = (
            user_settings.last_created_an_exploration -
            datetime.timedelta(hours=13))
        user_services._save_user_settings(user_settings) # pylint: disable=protected-access

        owner_settings = user_services.get_user_settings(self.owner_id)
        previous_last_created_an_exploration = (
            owner_settings.last_created_an_exploration)
        self.assertIsNotNone(previous_last_created_an_exploration)

        # The creator creates another exploration 13 hours later.
        self.save_new_valid_exploration(
            self.EXP_ID_B, self.owner_id, end_state_name='End')

        # Make sure that last exploration created time gets updated.
        owner_settings = user_services.get_user_settings(self.owner_id)
        self.assertGreater(
            (owner_settings.last_created_an_exploration),
            previous_last_created_an_exploration)
示例#11
0
def send_instant_feedback_message_email(
    recipient_id, sender_id, message, email_subject, exploration_title, exploration_id, thread_title
):
    """Send an email when a new message is posted to a feedback thread, or when
    the thread's status is changed.

    Args:
        recipient_id: str. The user ID of the recipient.
        sender_id: str. The user ID of the sender.
        message: str. The message text or status change text from the sender.
        email_subject: str. The subject line to be sent in the email.
        exploration_title: str. The title of the exploration.
        exploration_id: str. ID of the exploration the feedback thread is about.
        thread_title: str. The title of the feedback thread.
    """

    email_body_template = (
        "Hi %s,<br><br>"
        'New update to thread "%s" on '
        '<a href="https://www.oppia.org/create/%s#/feedback">%s</a>:<br>'
        "<ul><li>%s: %s<br></li></ul>"
        "(You received this message because you are a "
        "participant in this thread.)<br><br>"
        "Best wishes,<br>"
        "The Oppia team<br>"
        "<br>%s"
    )

    if not feconf.CAN_SEND_EMAILS:
        log_new_error("This app cannot send emails to users.")
        return

    if not feconf.CAN_SEND_FEEDBACK_MESSAGE_EMAILS:
        log_new_error("This app cannot send feedback message emails to users.")
        return

    sender_settings = user_services.get_user_settings(sender_id)
    recipient_settings = user_services.get_user_settings(recipient_id)
    recipient_preferences = user_services.get_email_preferences(recipient_id)

    if recipient_preferences["can_receive_feedback_message_email"]:
        email_body = email_body_template % (
            recipient_settings.username,
            thread_title,
            exploration_id,
            exploration_title,
            sender_settings.username,
            message,
            EMAIL_FOOTER.value,
        )
        _send_email(
            recipient_id,
            feconf.SYSTEM_COMMITTER_ID,
            feconf.EMAIL_INTENT_FEEDBACK_MESSAGE_NOTIFICATION,
            email_subject,
            email_body,
            feconf.NOREPLY_EMAIL_ADDRESS,
        )
示例#12
0
    def test_contribution_msec_does_not_change_if_no_contribution_to_exp(self):
        self.save_new_valid_exploration(
            self.EXP_ID, self.admin_id, end_state_name='End')
        rights_manager.assign_role_for_exploration(
            self.admin_id, self.EXP_ID, self.editor_id, 'editor')
        exp_services.publish_exploration_and_update_user_profiles(
            self.admin_id, self.EXP_ID)

        # Test that contribution time is not given to an editor that has not
        # contributed.
        self.assertIsNotNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)
        self.assertIsNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)
示例#13
0
    def test_contribution_msec_does_not_update_until_exploration_is_published(self):
        self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME)
        self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL)

        self.signup(self.EDITOR_EMAIL, self.EDITOR_USERNAME)
        self.editor_id = self.get_user_id_from_email(self.EDITOR_EMAIL)

        exploration = self.save_new_valid_exploration(
            self.EXP_ID, self.admin_id, end_state_name='End')
        self.init_state_name = exploration.init_state_name

        # Test that saving an exploration does not update first contribution
        # time.
        self.assertIsNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)

        # Test that commit to unpublished exploration does not update
        # contribution time.
        exp_services.update_exploration(
            self.admin_id, self.EXP_ID, [{
                'cmd': 'edit_state_property',
                'state_name': self.init_state_name,
                'property_name': 'widget_id',
                'new_value': 'MultipleChoiceInput'
            }], '')
        self.assertIsNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)

        # Test that another user who commits to unpublished exploration does not
        # have updated first contribution time.
        rights_manager.assign_role_for_exploration(
            self.admin_id, self.EXP_ID, self.editor_id, 'editor')
        exp_services.update_exploration(
            self.editor_id, self.EXP_ID, [{
            'cmd': 'rename_state',
            'old_state_name': feconf.DEFAULT_INIT_STATE_NAME,
            'new_state_name': u'¡Hola! αβγ',
            }], '')
        self.assertIsNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)

        # Test that after an exploration is published, all contributors have
        # updated first contribution time.
        exp_services.publish_exploration_and_update_user_profiles(
            self.admin_id, self.EXP_ID)
        self.assertIsNotNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)
        self.assertIsNotNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)
示例#14
0
    def test_contribution_msec_does_not_update_until_collection_is_published(self):
        self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME)
        self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL)

        collection = self.save_new_valid_collection(
            self.COL_ID, self.admin_id, title=self.COLLECTION_TITLE,
            category=self.COLLECTION_CATEGORY,
            objective=self.COLLECTION_OBJECTIVE,
            exploration_id=self.EXP_ID)

        # Test that saving a collection does not update first contribution
        # time.
        self.assertIsNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)

        # Test that commit to unpublished collection does not update
        # contribution time.
        collection_services.update_collection(
            self.admin_id, self.COL_ID, [{
                'cmd': 'edit_collection_property',
                'property_name': 'title',
                'new_value': 'Some new title'
            }], '')
        self.assertIsNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)

        # Test that another user who commits to unpublished collection does not
        # have updated first contribution time.
        self.signup(self.EDITOR_EMAIL, self.EDITOR_USERNAME)
        self.editor_id = self.get_user_id_from_email(self.EDITOR_EMAIL)
        rights_manager.assign_role_for_collection(
            self.admin_id, self.COL_ID, self.editor_id, 'editor')
        collection_services.update_collection(
                self.editor_id, self.COL_ID, [{
                'cmd': 'edit_collection_property',
                'property_name': 'category',
                'new_value': 'Some new category'
            }], '')
        self.assertIsNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)

        # Test that after an collection is published, all contributors have
        # updated first contribution times.
        collection_services.publish_collection_and_update_user_profiles(
            self.admin_id, self.COL_ID)
        self.assertIsNotNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)
        self.assertIsNotNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)
示例#15
0
def send_flag_exploration_email(
        exploration_title, exploration_id, reporter_id, report_text):
    email_subject = 'Exploration flagged by user: "******"' % exploration_title

    email_body_template = (
        'Hello Moderator,<br>'
        '%s has flagged exploration "%s" on the following '
        'grounds: <br>'
        '%s .<br>'
        'You can modify the exploration by clicking '
        '<a href="https://www.oppia.org/create/%s">here</a>.<br>'
        '<br>'
        'Thanks!<br>'
        '- The Oppia Team<br>'
        '<br>%s')

    if not feconf.CAN_SEND_EMAILS:
        log_new_error('This app cannot send emails to users.')
        return

    email_body = email_body_template % (
        user_services.get_user_settings(reporter_id).username,
        exploration_title, report_text, exploration_id,
        EMAIL_FOOTER.value)

    recipient_list = config_domain.MODERATOR_IDS.value
    for recipient_id in recipient_list:
        _send_email(
            recipient_id, feconf.SYSTEM_COMMITTER_ID,
            feconf.EMAIL_INTENT_REPORT_BAD_CONTENT,
            email_subject, email_body, feconf.NOREPLY_EMAIL_ADDRESS)
示例#16
0
文件: profile.py 项目: miyucy/oppia
 def get(self):
     """Handles GET requests."""
     user_settings = user_services.get_user_settings(self.user_id)
     self.render_json({
         'has_agreed_to_terms': bool(user_settings.last_agreed_to_terms),
         'username': user_settings.username,
     })
示例#17
0
    def get(self):
        """Handles GET requests."""
        # TODO(sll): Figure out what to do about explorations in categories
        # other than those explicitly listed.

        query_string = self.request.get('q')
        search_cursor = self.request.get('cursor', None)
        exp_ids, search_cursor = (
            exp_services.get_exploration_ids_matching_query(
                query_string, cursor=search_cursor))

        explorations_list = (
            summary_services.get_displayable_exp_summary_dicts_matching_ids(
                exp_ids))

        if len(explorations_list) == feconf.DEFAULT_QUERY_LIMIT:
            logging.error(
                '%s explorations were fetched to load the gallery page. '
                'You may be running up against the default query limits.'
                % feconf.DEFAULT_QUERY_LIMIT)

        preferred_language_codes = [feconf.DEFAULT_LANGUAGE_CODE]
        if self.user_id:
            user_settings = user_services.get_user_settings(self.user_id)
            preferred_language_codes = user_settings.preferred_language_codes

        self.values.update({
            'explorations_list': explorations_list,
            'preferred_language_codes': preferred_language_codes,
            'search_cursor': search_cursor,
        })
        self.render_json(self.values)
示例#18
0
    def test_contribution_msec_does_not_update_on_unpublished_explorations(self):
        self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME)
        self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL)
        self.set_admins([self.ADMIN_EMAIL])

        self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME)
        self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL)

        exploration = self.save_new_valid_exploration(
            self.EXP_ID, self.owner_id, end_state_name='End')
        self.init_state_name = exploration.init_state_name
        exp_services.publish_exploration_and_update_user_profiles(
            self.owner_id, self.EXP_ID)
        # We now manually reset the user's first_contribution_msec to None.
        # This is to test that the one off job skips over the unpublished
        # exploration and does not reset the user's first_contribution_msec.
        user_services._update_first_contribution_msec(
            self.owner_id, None)
        rights_manager.unpublish_exploration(self.admin_id, self.EXP_ID)

        # Test that first contribution time is not set for unpublished
        # explorations.
        job_id = user_jobs_one_off.UserFirstContributionMsecOneOffJob.create_new()
        user_jobs_one_off.UserFirstContributionMsecOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        self.assertIsNone(user_services.get_user_settings(
            self.owner_id).first_contribution_msec)
示例#19
0
def send_query_completion_email(recipient_id, query_id):
    """Send an email to the initiator of a bulk email query with a link to view
    the query results.

    Args:
        recipient_id: str. The recipient ID.
        query_id: str. The query ID.
    """
    email_subject = 'Query %s has successfully completed' % query_id

    email_body_template = (
        'Hi %s,<br>'
        'Your query with id %s has succesfully completed its '
        'execution. Visit the result page '
        '<a href="https://www.oppia.org/emaildashboardresult/%s">here</a> '
        'to see result of your query.<br><br>'
        'Thanks!<br>'
        '<br>'
        'Best wishes,<br>'
        'The Oppia Team<br>'
        '<br>%s')

    recipient_user_settings = user_services.get_user_settings(recipient_id)
    email_body = email_body_template % (
        recipient_user_settings.username, query_id, query_id,
        EMAIL_FOOTER.value)
    _send_email(
        recipient_id, feconf.SYSTEM_COMMITTER_ID,
        feconf.EMAIL_INTENT_QUERY_STATUS_NOTIFICATION, email_subject,
        email_body, feconf.NOREPLY_EMAIL_ADDRESS)
示例#20
0
文件: profile.py 项目: MaryamZi/oppia
 def get(self):
     """Handles GET requests."""
     user_settings = user_services.get_user_settings(self.user_id)
     self.values.update({
         'profile_picture_data_url': user_settings.profile_picture_data_url
     })
     self.render_json(self.values)
示例#21
0
    def get(self):
        """Handles GET requests."""
        # TODO(sll): Figure out what to do about explorations in categories
        # other than those explicitly listed.

        query_string = self.request.get('q')
        search_cursor = self.request.get('cursor', None)
        exp_summaries_list, search_cursor = (
            exp_services.get_exploration_summaries_matching_query(
                query_string, cursor=search_cursor))

        # TODO(msl): Store 'is_editable' in exploration summary to avoid O(n)
        # individual lookups. Note that this will depend on user_id.
        explorations_list = [{
            'id':
            exp_summary.id,
            'title':
            exp_summary.title,
            'category':
            exp_summary.category,
            'objective':
            exp_summary.objective,
            'language_code':
            exp_summary.language_code,
            'last_updated':
            utils.get_time_in_millisecs(
                exp_summary.exploration_model_last_updated),
            'status':
            exp_summary.status,
            'community_owned':
            exp_summary.community_owned,
            'thumbnail_image_url':
            exp_summary.thumbnail_image_url,
            'is_editable':
            exp_services.is_exp_summary_editable(
                exp_summary, user_id=self.user_id),
            'ratings':
            exp_summary.ratings
        } for exp_summary in exp_summaries_list]

        if len(explorations_list) == feconf.DEFAULT_QUERY_LIMIT:
            logging.error(
                '%s explorations were fetched to load the gallery page. '
                'You may be running up against the default query limits.' %
                feconf.DEFAULT_QUERY_LIMIT)

        preferred_language_codes = [feconf.DEFAULT_LANGUAGE_CODE]
        if self.user_id:
            user_settings = user_services.get_user_settings(self.user_id)
            preferred_language_codes = user_settings.preferred_language_codes

        self.values.update({
            'explorations_list':
            explorations_list,
            'preferred_language_codes':
            preferred_language_codes,
            'search_cursor':
            search_cursor,
        })
        self.render_json(self.values)
示例#22
0
def send_post_signup_email(user_id):
    """Sends a post-signup email to the given user.

    Raises an exception if emails are not allowed to be sent to users (i.e.
    feconf.CAN_SEND_EMAILS is False).

    Args:
        user_id: str. User ID of the user that signed up.
    """

    for key, content in SIGNUP_EMAIL_CONTENT.value.iteritems():
        if content == SIGNUP_EMAIL_CONTENT.default_value[key]:
            log_new_error(
                'Please ensure that the value for the admin config property '
                'SIGNUP_EMAIL_CONTENT is set, before allowing post-signup '
                'emails to be sent.')
            return

    user_settings = user_services.get_user_settings(user_id)
    email_subject = SIGNUP_EMAIL_CONTENT.value['subject']
    email_body = 'Hi %s,<br><br>%s<br><br>%s' % (
        user_settings.username,
        SIGNUP_EMAIL_CONTENT.value['html_body'],
        EMAIL_FOOTER.value)

    _send_email(
        user_id, feconf.SYSTEM_COMMITTER_ID, feconf.EMAIL_INTENT_SIGNUP,
        email_subject, email_body, feconf.NOREPLY_EMAIL_ADDRESS)
    def test_that_last_edited_and_created_time_are_not_updated(self):
        user_settings = user_services.get_user_settings(self.owner_id)
        user_settings.last_created_an_exploration = None
        user_settings.last_edited_an_exploration = None
        user_services._save_user_settings(user_settings)  # pylint: disable=protected-access

        owner_settings = user_services.get_user_settings(self.owner_id)

        self.assertIsNone(owner_settings.last_created_an_exploration)
        self.assertIsNone(owner_settings.last_edited_an_exploration)

        self._run_one_off_job()

        owner_settings = user_services.get_user_settings(self.owner_id)
        self.assertIsNone(owner_settings.last_created_an_exploration)
        self.assertIsNone(owner_settings.last_edited_an_exploration)
示例#24
0
 def get(self, username):
     user_id = user_services.get_user_id_from_username(username)
     if user_id is None:
         raise self.PageNotFoundException
     user_settings = user_services.get_user_settings(user_id)
     self.values.update({"profile_picture_data_url_for_username": user_settings.profile_picture_data_url})
     self.render_json(self.values)
示例#25
0
文件: library.py 项目: MaryamZi/oppia
    def get(self):
        """Handles GET requests."""
        # TODO(sll): Support index pages for other language codes.
        summary_dicts_by_category = summary_services.get_library_groups([
            feconf.DEFAULT_LANGUAGE_CODE])
        featured_activity_summary_dicts = (
            summary_services.get_featured_exploration_summary_dicts(
                [feconf.DEFAULT_LANGUAGE_CODE]))

        preferred_language_codes = [feconf.DEFAULT_LANGUAGE_CODE]
        if self.user_id:
            user_settings = user_services.get_user_settings(self.user_id)
            preferred_language_codes = user_settings.preferred_language_codes

        if featured_activity_summary_dicts:
            summary_dicts_by_category.insert(0, {
                'activity_summary_dicts': featured_activity_summary_dicts,
                'categories': [],
                'header': feconf.LIBRARY_CATEGORY_FEATURED_EXPLORATIONS,
            })

        self.values.update({
            'activity_summary_dicts_by_category': (
                summary_dicts_by_category),
            'preferred_language_codes': preferred_language_codes,
        })
        self.render_json(self.values)
示例#26
0
    def get(self):
        """Handles GET requests."""
        # TODO(sll): Figure out what to do about explorations in categories
        # other than those explicitly listed.

        language_codes_to_short_descs = {
            lc['code']: utils.get_short_language_description(lc['description'])
            for lc in feconf.ALL_LANGUAGE_CODES
        }

        query_string = self.request.get('q')
        search_cursor = self.request.get('cursor', None)
        exp_summaries_list, search_cursor = (
            exp_services.get_exploration_summaries_matching_query(
                query_string, cursor=search_cursor))

        def _get_intro_card_color(category):
            return (
                feconf.CATEGORIES_TO_COLORS[category] if
                category in feconf.CATEGORIES_TO_COLORS else
                feconf.DEFAULT_COLOR)

        # TODO(msl): Store 'is_editable' in exploration summary to avoid O(n)
        # individual lookups. Note that this will depend on user_id.
        explorations_list = [{
            'id': exp_summary.id,
            'title': exp_summary.title,
            'category': exp_summary.category,
            'objective': exp_summary.objective,
            'language_code': exp_summary.language_code,
            'last_updated': utils.get_time_in_millisecs(
                exp_summary.exploration_model_last_updated),
            'status': exp_summary.status,
            'community_owned': exp_summary.community_owned,
            'thumbnail_image_url': (
                '/images/gallery/exploration_background_%s_small.png' %
                _get_intro_card_color(exp_summary.category)),
            'is_editable': exp_services.is_exp_summary_editable(
                exp_summary,
                user_id=self.user_id)
        } for exp_summary in exp_summaries_list]

        if len(explorations_list) == feconf.DEFAULT_QUERY_LIMIT:
            logging.error(
                '%s explorations were fetched to load the gallery page. '
                'You may be running up against the default query limits.'
                % feconf.DEFAULT_QUERY_LIMIT)

        preferred_language_codes = [feconf.DEFAULT_LANGUAGE_CODE]
        if self.user_id:
            user_settings = user_services.get_user_settings(self.user_id)
            preferred_language_codes = user_settings.preferred_language_codes

        self.values.update({
            'explorations_list': explorations_list,
            'preferred_language_codes': preferred_language_codes,
            'search_cursor': search_cursor,
        })
        self.render_json(self.values)
示例#27
0
    def test_legacy_user(self):
        """Test the case of a user who are creating exploration for first time
        after the last edited time check was introduced.
        """
        # Set up a 'previous-generation' user.
        user_settings = user_services.get_user_settings(self.owner_id)
        user_settings.last_created_an_exploration = None
        user_services._save_user_settings(user_settings)  # pylint: disable=protected-access

        owner_settings = user_services.get_user_settings(self.owner_id)
        self.assertIsNone(owner_settings.last_created_an_exploration)

        self.save_new_valid_exploration(
            self.EXP_ID_A, self.owner_id, end_state_name='End')

        owner_settings = user_services.get_user_settings(self.owner_id)
        self.assertIsNotNone(owner_settings.last_created_an_exploration)
示例#28
0
    def test_contribution_msec_does_not_change_if_no_contribution_to_collection(self):
        self.save_new_valid_collection(
            self.COL_ID, self.admin_id, title=self.COLLECTION_TITLE,
            category=self.COLLECTION_CATEGORY,
            objective=self.COLLECTION_OBJECTIVE,
            exploration_id=self.EXP_ID)
        rights_manager.assign_role_for_collection(
            self.admin_id, self.COL_ID, self.editor_id, 'editor')
        collection_services.publish_collection_and_update_user_profiles(
            self.admin_id, self.COL_ID)

        # Test that contribution time is not given to an editor that has not
        # contributed.
        self.assertIsNotNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)
        self.assertIsNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)
示例#29
0
    def test_last_logged_in_only_updated_if_enough_time_has_elapsed(self):
        # The last logged-in time has already been set when the user
        # registered.
        previous_last_logged_in_datetime = (
            user_services.get_user_settings(self.viewer_id).last_logged_in)
        self.assertIsNotNone(previous_last_logged_in_datetime)

        original_datetime_type = datetime.datetime
        current_datetime = datetime.datetime.utcnow()

        # Without explicitly defining the type of the patched datetimes, NDB
        # validation checks for datetime.datetime instances fail.
        class PatchedDatetimeType(type):
            def __instancecheck__(cls, other):
                return isinstance(other, original_datetime_type)

        class PatchedDatetime11Hours(datetime.datetime):
            __metaclass__ = PatchedDatetimeType

            @classmethod
            def utcnow(cls):
                return current_datetime + datetime.timedelta(hours=11)

        class PatchedDatetime13Hours(datetime.datetime):
            __metaclass__ = PatchedDatetimeType

            @classmethod
            def utcnow(cls):
                return current_datetime + datetime.timedelta(hours=13)

        with self.swap(datetime, 'datetime', PatchedDatetime11Hours):
            self.login(self.VIEWER_EMAIL)
            self.testapp.get(feconf.LIBRARY_INDEX_URL)
            self.assertEqual(
                user_services.get_user_settings(self.viewer_id).last_logged_in,
                previous_last_logged_in_datetime)
            self.logout()

        with self.swap(datetime, 'datetime', PatchedDatetime13Hours):
            self.login(self.VIEWER_EMAIL)
            self.testapp.get(feconf.LIBRARY_INDEX_URL)
            self.assertGreater(
                user_services.get_user_settings(self.viewer_id).last_logged_in,
                previous_last_logged_in_datetime)
            self.logout()
示例#30
0
    def test_legacy_user(self):
        """Test the case of a user who existed in the system before the
        last-login check was introduced.
        """
        # Set up a 'previous-generation' user.
        user_settings = user_services.get_user_settings(self.viewer_id)
        user_settings.last_logged_in = None
        user_services._save_user_settings(user_settings)  # pylint: disable=protected-access

        self.assertIsNone(
            user_services.get_user_settings(self.viewer_id).last_logged_in)
        # After logging in and requesting a URL, the last_logged_in property is
        # set.
        self.login(self.VIEWER_EMAIL)
        self.testapp.get(feconf.LIBRARY_INDEX_URL)
        self.assertIsNotNone(
            user_services.get_user_settings(self.viewer_id).last_logged_in)
        self.logout()
示例#31
0
    def test_contribution_msec_does_not_change_if_exp_unpublished(self):
        self.save_new_valid_exploration(
            self.EXP_ID, self.owner_id, end_state_name='End')

        exp_services.publish_exploration_and_update_user_profiles(
            self.owner, self.EXP_ID)
        rights_manager.unpublish_exploration(self.admin, self.EXP_ID)

        # Test that contribution time is not eliminated if exploration is
        # unpublished.
        self.assertIsNotNone(user_services.get_user_settings(
            self.owner_id).first_contribution_msec)
示例#32
0
 def get(self):
     if self.user_id and user_services.has_fully_registered_account(
             self.user_id):
         user_settings = user_services.get_user_settings(
             self.user_id)
         default_dashboard = user_settings.default_dashboard
         if default_dashboard == constants.DASHBOARD_TYPE_CREATOR:
             self.redirect(feconf.CREATOR_DASHBOARD_URL)
         else:
             self.redirect(feconf.LEARNER_DASHBOARD_URL)
     else:
         self.render_template('splash-page.mainpage.html')
示例#33
0
    def test_legacy_user(self):
        """Test the case of a user who are editing exploration for first time
        after the last edited time check was introduced.
        """
        # Set up a 'previous-generation' user.
        user_settings = user_services.get_user_settings(self.editor_id)
        user_settings.last_edited_an_exploration = None
        user_services._save_user_settings(user_settings)  # pylint: disable=protected-access

        editor_settings = user_services.get_user_settings(self.editor_id)
        self.assertIsNone(editor_settings.last_edited_an_exploration)

        exp_services.update_exploration(self.editor_id, self.EXP_ID,
                                        [{
                                            'cmd': 'edit_exploration_property',
                                            'property_name': 'objective',
                                            'new_value': 'the objective'
                                        }], 'Test edit')

        editor_settings = user_services.get_user_settings(self.editor_id)
        self.assertIsNotNone(editor_settings.last_edited_an_exploration)
示例#34
0
    def test_contribution_msec_does_not_change_if_no_contribution_to_collection(
            self):
        self.save_new_valid_collection(self.COL_ID,
                                       self.admin_id,
                                       title=self.COLLECTION_TITLE,
                                       category=self.COLLECTION_CATEGORY,
                                       objective=self.COLLECTION_OBJECTIVE,
                                       exploration_id=self.EXP_ID)
        rights_manager.assign_role_for_collection(self.admin, self.COL_ID,
                                                  self.editor_id, 'editor')
        collection_services.publish_collection_and_update_user_profiles(
            self.admin, self.COL_ID)

        # Test that contribution time is not given to an editor that has not
        # contributed.
        self.assertIsNotNone(
            user_services.get_user_settings(
                self.admin_id).first_contribution_msec)
        self.assertIsNone(
            user_services.get_user_settings(
                self.editor_id).first_contribution_msec)
示例#35
0
 def get(self):
     """Handles GET requests."""
     user_settings = user_services.get_user_settings(self.user_id)
     self.render_json({
         'has_agreed_to_latest_terms': (
             user_settings.last_agreed_to_terms and
             user_settings.last_agreed_to_terms >=
             feconf.REGISTRATION_PAGE_LAST_UPDATED_UTC),
         'has_ever_registered': bool(
             user_settings.username and user_settings.last_agreed_to_terms),
         'username': user_settings.username,
     })
示例#36
0
    def get(self, exploration_id):
        """Populates the data on the individual exploration page.

        Args:
            exploration_id: str. The ID of the exploration.
        """
        version = self.request.get('v')
        version = int(version) if version else None

        exploration = exp_fetchers.get_exploration_by_id(exploration_id,
                                                         strict=False,
                                                         version=version)
        if exploration is None:
            raise self.PageNotFoundException()

        exploration_rights = rights_manager.get_exploration_rights(
            exploration_id, strict=False)
        user_settings = user_services.get_user_settings(self.user_id)

        preferred_audio_language_code = None
        preferred_language_codes = None

        if user_settings is not None:
            preferred_audio_language_code = (
                user_settings.preferred_audio_language_code)
            preferred_language_codes = (user_settings.preferred_language_codes)

        self.values.update({
            'can_edit':
            (rights_manager.check_can_edit_activity(self.user,
                                                    exploration_rights)),
            'exploration':
            exploration.to_player_dict(),
            'exploration_id':
            exploration_id,
            'is_logged_in':
            bool(self.user_id),
            'session_id':
            utils.generate_new_session_id(),
            'version':
            exploration.version,
            'preferred_audio_language_code':
            preferred_audio_language_code,
            'preferred_language_codes':
            preferred_language_codes,
            'auto_tts_enabled':
            exploration.auto_tts_enabled,
            'correctness_feedback_enabled':
            (exploration.correctness_feedback_enabled),
            'record_playthrough_probability':
            (config_domain.RECORD_PLAYTHROUGH_PROBABILITY.value),
        })
        self.render_json(self.values)
示例#37
0
    def test_new_profile_picture_is_generated_if_it_does_not_exist(self):
        user_services.update_profile_picture_data_url(self.owner_id, None)

        # Before the job runs, the data URL is None.
        user_settings = user_services.get_user_settings(self.owner_id)
        self.assertIsNone(user_settings.profile_picture_data_url)

        job_id = (
            user_jobs_one_off.UserProfilePictureOneOffJob.create_new())
        user_jobs_one_off.UserProfilePictureOneOffJob.enqueue(job_id)

        def _mock_fetch_gravatar(unused_email):
            return self.FETCHED_GRAVATAR

        with self.swap(user_services, 'fetch_gravatar', _mock_fetch_gravatar):
            self.process_and_flush_pending_tasks()

        # After the job runs, the data URL has been updated.
        new_user_settings = user_services.get_user_settings(self.owner_id)
        self.assertEqual(
            new_user_settings.profile_picture_data_url, self.FETCHED_GRAVATAR)
示例#38
0
    def test_default_dashboard_for_new_users(self):
        self.login(self.EDITOR_EMAIL)
        response = self.testapp.get(feconf.SIGNUP_URL)
        csrf_token = self.get_csrf_token_from_response(response)

        # This user should have the creator dashboard as default.
        self.post_json(
            feconf.SIGNUP_DATA_URL, {
                'agreed_to_terms': True,
                'username': '******',
                'default_dashboard': constants.DASHBOARD_TYPE_CREATOR,
                'can_receive_email_updates': None
            }, csrf_token)

        user_id = user_services.get_user_id_from_username('creatoruser')
        user_settings = user_services.get_user_settings(user_id)
        self.assertEqual(user_settings.default_dashboard,
                         constants.DASHBOARD_TYPE_CREATOR)

        self.logout()

        self.login(self.VIEWER_EMAIL)
        response = self.testapp.get(feconf.SIGNUP_URL)
        csrf_token = self.get_csrf_token_from_response(response)

        # This user should have the learner dashboard as default.
        self.post_json(
            feconf.SIGNUP_DATA_URL, {
                'agreed_to_terms': True,
                'username': '******',
                'default_dashboard': constants.DASHBOARD_TYPE_LEARNER,
                'can_receive_email_updates': None
            }, csrf_token)

        user_id = user_services.get_user_id_from_username('learneruser')
        user_settings = user_services.get_user_settings(user_id)
        self.assertEqual(user_settings.default_dashboard,
                         constants.DASHBOARD_TYPE_LEARNER)

        self.logout()
示例#39
0
def send_suggestion_email(exploration_title, exploration_id, author_id,
                          recipient_list):
    email_subject = 'New suggestion for "%s"' % exploration_title

    email_body_template = (
        'Hi %s,<br>'
        '%s has submitted a new suggestion for your Oppia exploration, '
        '<a href="https://www.oppia.org/create/%s">"%s"</a>.<br>'
        'You can accept or reject this suggestion by visiting the '
        '<a href="https://www.oppia.org/create/%s#/feedback">feedback page</a> '
        'for your exploration.<br>'
        '<br>'
        'Thanks!<br>'
        '- The Oppia Team<br>'
        '<br>%s')

    if not feconf.CAN_SEND_EMAILS:
        log_new_error('This app cannot send emails to users.')
        return

    if not feconf.CAN_SEND_FEEDBACK_MESSAGE_EMAILS:
        log_new_error('This app cannot send feedback message emails to users.')
        return

    author_settings = user_services.get_user_settings(author_id)
    for recipient_id in recipient_list:
        recipient_user_settings = user_services.get_user_settings(recipient_id)
        recipient_preferences = (
            user_services.get_email_preferences(recipient_id))

        if recipient_preferences['can_receive_feedback_message_email']:
            # Send email only if recipient wants to receive.
            email_body = email_body_template % (
                recipient_user_settings.username, author_settings.username,
                exploration_id, exploration_title, exploration_id,
                EMAIL_FOOTER.value)
            _send_email(recipient_id, feconf.SYSTEM_COMMITTER_ID,
                        feconf.EMAIL_INTENT_SUGGESTION_NOTIFICATION,
                        email_subject, email_body,
                        feconf.NOREPLY_EMAIL_ADDRESS)
示例#40
0
def send_feedback_message_email(recipient_id, feedback_messages):
    """Sends an email when creator receives feedback message to an exploration.

    Args:
    - recipient_id: id of recipient user.
    - feedback_messages: dictionary containing feedback messages.
    """

    email_subject = (
        'You\'ve received %s new message%s on your explorations' %
        (len(feedback_messages), 's' if len(feedback_messages) > 1 else ''))

    email_body_template = (
        'Hi %s,<br>'
        '<br>'
        'You\'ve received %s new message%s on your Oppia explorations:<br>'
        '<ul>%s</ul>'
        'You can view and reply to your messages from your '
        '<a href="https://www.oppia.org/dashboard">dashboard</a>.'
        '<br>'
        'Thanks, and happy teaching!<br>'
        '<br>'
        'Best wishes,<br>'
        'The Oppia Team<br>'
        '<br>%s')

    if not feconf.CAN_SEND_EMAILS:
        log_new_error('This app cannot send emails to users.')
        return

    if not feconf.CAN_SEND_FEEDBACK_MESSAGE_EMAILS:
        log_new_error('This app cannot send feedback message emails to users.')
        return

    if not feedback_messages:
        return

    recipient_user_settings = user_services.get_user_settings(recipient_id)

    messages_html = ''
    for _, reference in feedback_messages.iteritems():
        for message in reference['messages']:
            messages_html += ('<li>%s: %s<br></li>' %
                              (reference['title'], message))

    email_body = email_body_template % (
        recipient_user_settings.username, len(feedback_messages), 's' if
        len(feedback_messages) > 1 else '', messages_html, EMAIL_FOOTER.value)

    _send_email(recipient_id, feconf.SYSTEM_COMMITTER_ID,
                feconf.EMAIL_INTENT_FEEDBACK_MESSAGE_NOTIFICATION,
                email_subject, email_body, feconf.NOREPLY_EMAIL_ADDRESS)
示例#41
0
 def test_record_user_started_state_translation_tutorial(self):
     # Testing of the user translation tutorial firsttime state storage.
     user_id = 'someUser'
     username = '******'
     user_services.create_new_user(user_id, '*****@*****.**')
     user_services.set_username(user_id, username)
     user_services.record_user_started_state_translation_tutorial(user_id)
     user_settings = user_services.get_user_settings(user_id)
     self.assertIsInstance(
         user_settings.last_started_state_translation_tutorial,
         datetime.datetime)
     self.assertTrue(
         user_settings.last_started_state_translation_tutorial is not None)
示例#42
0
    def test_default_dashboard_for_new_users(self):
        self.login(self.EDITOR_EMAIL)
        self.get_html_response(feconf.SIGNUP_URL + '?return_url=/')
        csrf_token = self.get_new_csrf_token()

        # This user should have the creator dashboard as default.
        self.post_json(
            feconf.SIGNUP_DATA_URL,
            {'agreed_to_terms': True, 'username': self.EDITOR_USERNAME,
             'default_dashboard': constants.DASHBOARD_TYPE_CREATOR,
             'can_receive_email_updates': None},
            csrf_token=csrf_token)

        user_id = user_services.get_user_id_from_username(self.EDITOR_USERNAME)
        user_settings = user_services.get_user_settings(user_id)
        self.assertEqual(
            user_settings.default_dashboard, constants.DASHBOARD_TYPE_CREATOR)

        self.logout()

        user_services.create_new_user(
            self.get_auth_id_from_email(self.VIEWER_EMAIL), self.VIEWER_EMAIL)
        self.login(self.VIEWER_EMAIL)
        csrf_token = self.get_new_csrf_token()

        # This user should have the learner dashboard as default.
        self.post_json(
            feconf.SIGNUP_DATA_URL,
            {'agreed_to_terms': True, 'username': self.VIEWER_USERNAME,
             'default_dashboard': constants.DASHBOARD_TYPE_LEARNER,
             'can_receive_email_updates': None},
            csrf_token=csrf_token)

        user_id = user_services.get_user_id_from_username(self.VIEWER_USERNAME)
        user_settings = user_services.get_user_settings(user_id)
        self.assertEqual(
            user_settings.default_dashboard, constants.DASHBOARD_TYPE_LEARNER)

        self.logout()
示例#43
0
    def test_profile_picture_is_not_regenerated_if_it_already_exists(self):
        user_services.update_profile_picture_data_url(
            self.owner_id, 'manually_added_data_url')

        # Before the job runs, the data URL is the manually-added one.
        user_settings = user_services.get_user_settings(self.owner_id)
        self.assertEqual(user_settings.profile_picture_data_url,
                         'manually_added_data_url')

        job_id = (user_jobs_one_off.UserProfilePictureOneOffJob.create_new())
        user_jobs_one_off.UserProfilePictureOneOffJob.enqueue(job_id)

        def _mock_fetch_gravatar(unused_email):
            return self.FETCHED_GRAVATAR

        with self.swap(user_services, 'fetch_gravatar', _mock_fetch_gravatar):
            self.process_and_flush_pending_tasks()

        # After the job runs, the data URL is still the manually-added one.
        new_user_settings = user_services.get_user_settings(self.owner_id)
        self.assertEqual(new_user_settings.profile_picture_data_url,
                         'manually_added_data_url')
示例#44
0
def send_moderator_action_email(sender_id, recipient_id, intent,
                                exploration_title, email_body):
    """Sends a email immediately following a moderator action (unpublish,
    delete) to the given user.

    Raises an exception if emails are not allowed to be sent to users (i.e.
    feconf.CAN_SEND_EMAILS is False).

    Args:
        sender_id: str. User ID of the sender.
        recipient_id: str. User ID of the recipient.
        intent: str. The intent string (cause/purpose) of the email.
        exploration_title: str. The title of the exploration on which the
            moderator action was taken.
        email_body: str. The email content/message.
    """

    require_moderator_email_prereqs_are_satisfied()
    email_config = feconf.VALID_MODERATOR_ACTIONS[intent]

    recipient_user_settings = user_services.get_user_settings(recipient_id)
    sender_user_settings = user_services.get_user_settings(sender_id)
    email_subject = feconf.VALID_MODERATOR_ACTIONS[intent]['email_subject_fn'](
        exploration_title)
    email_salutation_html = email_config['email_salutation_html_fn'](
        recipient_user_settings.username)
    email_signoff_html = email_config['email_signoff_html_fn'](
        sender_user_settings.username)

    full_email_content = ('%s<br><br>%s<br><br>%s<br><br>%s' %
                          (email_salutation_html, email_body,
                           email_signoff_html, EMAIL_FOOTER.value))
    _send_email(recipient_id,
                sender_id,
                intent,
                email_subject,
                full_email_content,
                feconf.SYSTEM_EMAIL_ADDRESS,
                bcc_admin=True)
示例#45
0
    def get(self):
        """Handles GET requests."""
        user_settings = user_services.get_user_settings(self.user_id)
        user_email_preferences = user_services.get_email_preferences(
            self.user_id)

        creators_subscribed_to = subscription_services.get_all_creators_subscribed_to(  # pylint: disable=line-too-long
            self.user_id)
        creators_settings = user_services.get_users_settings(
            creators_subscribed_to)
        subscription_list = []

        for index, creator_settings in enumerate(creators_settings):
            subscription_summary = {
                'creator_picture_data_url':
                (creator_settings.profile_picture_data_url),
                'creator_username':
                creator_settings.username,
                'creator_impact': (user_services.get_user_impact_score(
                    creators_subscribed_to[index]))
            }

            subscription_list.append(subscription_summary)

        self.values.update({
            'preferred_language_codes':
            user_settings.preferred_language_codes,
            'preferred_site_language_code':
            (user_settings.preferred_site_language_code),
            'preferred_audio_language_code':
            (user_settings.preferred_audio_language_code),
            'profile_picture_data_url':
            user_settings.profile_picture_data_url,
            'default_dashboard':
            user_settings.default_dashboard,
            'user_bio':
            user_settings.user_bio,
            'subject_interests':
            user_settings.subject_interests,
            'can_receive_email_updates':
            (user_email_preferences.can_receive_email_updates),
            'can_receive_editor_role_email':
            (user_email_preferences.can_receive_editor_role_email),
            'can_receive_feedback_message_email':
            (user_email_preferences.can_receive_feedback_message_email),
            'can_receive_subscription_email':
            (user_email_preferences.can_receive_subscription_email),
            'subscription_list':
            subscription_list
        })
        self.render_json(self.values)
示例#46
0
    def test_contribution_msec_updates_on_published_explorations(self):
        exploration = self.save_new_valid_exploration(self.EXP_ID,
                                                      self.admin_id,
                                                      end_state_name='End')
        init_state_name = exploration.init_state_name

        # Test that no contribution time is set.
        job_id = (
            user_jobs_one_off.UserFirstContributionMsecOneOffJob.create_new())
        user_jobs_one_off.UserFirstContributionMsecOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        self.assertIsNone(
            user_services.get_user_settings(
                self.admin_id).first_contribution_msec)

        # Test all owners and editors of exploration after publication have
        # updated times.
        exp_services.publish_exploration_and_update_user_profiles(
            self.admin_id, self.EXP_ID)
        rights_manager.release_ownership_of_exploration(
            self.admin_id, self.EXP_ID)
        exp_services.update_exploration(self.editor_id, self.EXP_ID,
                                        [{
                                            'cmd': 'edit_state_property',
                                            'state_name': init_state_name,
                                            'property_name': 'widget_id',
                                            'new_value': 'MultipleChoiceInput'
                                        }], 'commit')
        job_id = (
            user_jobs_one_off.UserFirstContributionMsecOneOffJob.create_new())
        user_jobs_one_off.UserFirstContributionMsecOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        self.assertIsNotNone(
            user_services.get_user_settings(
                self.admin_id).first_contribution_msec)
        self.assertIsNotNone(
            user_services.get_user_settings(
                self.editor_id).first_contribution_msec)
示例#47
0
    def test_contribution_msec_does_not_change_if_no_contribution_to_exploration(
            self):
        self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME)
        self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL)
        self.signup(self.EDITOR_EMAIL, self.EDITOR_USERNAME)
        self.editor_id = self.get_user_id_from_email(self.EDITOR_EMAIL)

        exploration = self.save_new_valid_exploration(self.EXP_ID,
                                                      self.admin_id,
                                                      end_state_name='End')
        rights_manager.assign_role_for_exploration(self.admin_id, self.EXP_ID,
                                                   self.editor_id, 'editor')
        exp_services.publish_exploration_and_update_user_profiles(
            self.admin_id, self.EXP_ID)

        # Test that contribution time is not given to an editor that has not
        # contributed.
        self.assertIsNotNone(
            user_services.get_user_settings(
                self.admin_id).first_contribution_msec)
        self.assertIsNone(
            user_services.get_user_settings(
                self.editor_id).first_contribution_msec)
示例#48
0
    def get(self):
        """Handles GET requests."""
        return_url = str(self.request.get('return_url', self.request.uri))

        user_settings = user_services.get_user_settings(self.user_id)
        if user_settings.last_agreed_to_terms and user_settings.username:
            self.redirect(return_url)
            return

        self.values.update({
            'nav_mode': feconf.NAV_MODE_PROFILE,
            'SITE_NAME': pages.SITE_NAME.value,
        })
        self.render_template('profile/signup.html')
示例#49
0
文件: library.py 项目: pydevsg/oppia
    def get(self):
        """Handles GET requests."""
        # TODO(sll): Support index pages for other language codes.
        summary_dicts_by_category = summary_services.get_library_groups(
            [constants.DEFAULT_LANGUAGE_CODE])
        top_rated_activity_summary_dicts = (
            summary_services.get_top_rated_exploration_summary_dicts(
                [constants.DEFAULT_LANGUAGE_CODE],
                feconf.NUMBER_OF_TOP_RATED_EXPLORATIONS_FOR_LIBRARY_PAGE))
        featured_activity_summary_dicts = (
            summary_services.get_featured_activity_summary_dicts(
                [constants.DEFAULT_LANGUAGE_CODE]))

        preferred_language_codes = [constants.DEFAULT_LANGUAGE_CODE]
        if self.user_id:
            user_settings = user_services.get_user_settings(self.user_id)
            preferred_language_codes = user_settings.preferred_language_codes

        if top_rated_activity_summary_dicts:
            summary_dicts_by_category.insert(
                0, {
                    'activity_summary_dicts':
                    top_rated_activity_summary_dicts,
                    'categories': [],
                    'header_i18n_id':
                    (feconf.LIBRARY_CATEGORY_TOP_RATED_EXPLORATIONS),
                    'has_full_results_page':
                    True,
                    'full_results_url':
                    feconf.LIBRARY_TOP_RATED_URL,
                    'protractor_id':
                    'top-rated',
                })
        if featured_activity_summary_dicts:
            summary_dicts_by_category.insert(
                0, {
                    'activity_summary_dicts': featured_activity_summary_dicts,
                    'categories': [],
                    'header_i18n_id':
                    (feconf.LIBRARY_CATEGORY_FEATURED_ACTIVITIES),
                    'has_full_results_page': False,
                    'full_results_url': None,
                })

        self.values.update({
            'activity_summary_dicts_by_category': (summary_dicts_by_category),
            'preferred_language_codes':
            preferred_language_codes,
        })
        self.render_json(self.values)
示例#50
0
    def get(self):
        """Handles GET requests for group pages."""
        # TODO(sll): Support index pages for other language codes.
        group_name = self.request.get('group_name')
        activity_list = []
        header_i18n_id = ''

        if group_name == feconf.LIBRARY_GROUP_RECENTLY_PUBLISHED:
            recently_published_summary_dicts = (
                summary_services.get_recently_published_exp_summary_dicts(
                    feconf.RECENTLY_PUBLISHED_QUERY_LIMIT_FULL_PAGE))
            if recently_published_summary_dicts:
                activity_list = recently_published_summary_dicts
                header_i18n_id = feconf.LIBRARY_CATEGORY_RECENTLY_PUBLISHED

        elif group_name == feconf.LIBRARY_GROUP_TOP_RATED:
            top_rated_activity_summary_dicts = (
                summary_services.get_top_rated_exploration_summary_dicts(
                    [feconf.DEFAULT_LANGUAGE_CODE],
                    feconf.NUMBER_OF_TOP_RATED_EXPLORATIONS_FULL_PAGE))
            if top_rated_activity_summary_dicts:
                activity_list = top_rated_activity_summary_dicts
                header_i18n_id = feconf.LIBRARY_CATEGORY_TOP_RATED_EXPLORATIONS

        # TODO: create a Splash controller and implement this properly.
        elif group_name == feconf.LIBRARY_CATEGORY_SPLASH_PAGE_FEATURED:
            splash_page_featured_exploration_ids = [
                '0', 'yvqBFOQNDz5e', 'BvpDpLSmO2Iu', 'gC4_ggkWar-L'
            ]
            activity_list = (summary_services.
                             get_displayable_exp_summary_dicts_matching_ids(
                                 splash_page_featured_exploration_ids))

        else:
            return self.PageNotFoundException

        preferred_language_codes = [feconf.DEFAULT_LANGUAGE_CODE]
        if self.user_id:
            user_settings = user_services.get_user_settings(self.user_id)
            preferred_language_codes = user_settings.preferred_language_codes

        self.values.update({
            'activity_list':
            activity_list,
            'header_i18n_id':
            header_i18n_id,
            'preferred_language_codes':
            preferred_language_codes,
        })
        self.render_json(self.values)
示例#51
0
def is_feature_enabled(feature_name, user_id):
    """A short-form method for server-side usage. This method evaluates and
    returns the values of the feature flag, using context from the server only.

    Args:
        feature_name: str. The name of the feature flag that needs to
            be evaluated.
        user_id: str. The id of the user with which to evaluate the flag.

    Returns:
        bool. The value of the feature flag, True if it's enabled.
    """
    user_settings = user_services.get_user_settings(user_id)
    return _evaluate_feature_flag_value_for_server(feature_name, user_settings)
示例#52
0
    def test_contribution_msec_does_not_change_if_collection_unpublished(self):
        self.save_new_valid_collection(
            self.COL_ID, self.owner_id, title=self.COLLECTION_TITLE,
            category=self.COLLECTION_CATEGORY,
            objective=self.COLLECTION_OBJECTIVE,
            exploration_id=self.EXP_ID)
        collection_services.publish_collection_and_update_user_profiles(
            self.owner_id, self.COL_ID)
        rights_manager.unpublish_collection(self.admin_id, self.COL_ID)

        # Test that first contribution msec is not eliminated if collection is
        # unpublished.
        self.assertIsNotNone(user_services.get_user_settings(
            self.owner_id).first_contribution_msec)
示例#53
0
    def test_contribution_msec_updates_on_published_collections(self):
        self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME)
        self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL)

        collection = self.save_new_valid_collection(
            self.COL_ID,
            self.admin_id,
            title=self.COLLECTION_TITLE,
            category=self.COLLECTION_CATEGORY,
            objective=self.COLLECTION_OBJECTIVE,
            exploration_id=self.EXP_ID)

        collection_services.publish_collection_and_update_user_profiles(
            self.admin_id, self.COL_ID)

        # Test all owners and editors of collection after publication have
        # updated first contribution times.
        self.assertIsNotNone(
            user_services.get_user_settings(
                self.admin_id).first_contribution_msec)

        # Test editor of published collection has updated first contribution time.
        rights_manager.release_ownership_of_collection(self.admin_id,
                                                       self.COL_ID)
        self.signup(self.EDITOR_EMAIL, self.EDITOR_USERNAME)
        self.editor_id = self.get_user_id_from_email(self.EDITOR_EMAIL)

        collection_services.update_collection(
            self.editor_id, self.COL_ID, [{
                'cmd': 'edit_collection_property',
                'property_name': 'title',
                'new_value': 'Some new title'
            }], 'Changed the title')

        self.assertIsNotNone(
            user_services.get_user_settings(
                self.editor_id).first_contribution_msec)
示例#54
0
    def to_dict(self):
        """Returns a dict-representation of the task.

        Returns:
            dict. Contains the following keys:
                entity_type: str. The type of entity the task entry refers to.
                    For example, "exploration".
                entity_id: str. The ID of the entity the task entry refers to.
                    For example, an exploration ID.
                entity_version: int. The version of the entity the task entry
                    refers to. For example, an exploration's version.
                task_type: str. The type of task the task entry tracks.
                target_type: str. The type of sub-entity the task entry refers
                    to. For example, "state" when entity type is "exploration".
                target_id: str. The ID of the sub-entity the task entry refers
                    to. For example, the state name of an exploration.
                issue_description: str. The sentence generated by Oppia to
                    describe why the task was created.
                status: str. Tracks the state/progress of the task entry.
                resolver_username: str. Username of the user who resolved the
                    task when status is resolved. Otherwise None.
                resolver_profile_picture_data_url: str. Profile picture URL of
                    the user who resolved the task when status is resolved.
                    Otherwise None.
                resolved_on_msecs: float. Time in milliseconds since epoch at
                    which the task was resolved when status is resolved.
                    Otherwise None.
        """
        resolver_settings = (
            self.resolver_id and
            user_services.get_user_settings(self.resolver_id, strict=True))
        return {
            'entity_type': self.entity_type,
            'entity_id': self.entity_id,
            'entity_version': self.entity_version,
            'task_type': self.task_type,
            'target_type': self.target_type,
            'target_id': self.target_id,
            'issue_description': self.issue_description,
            'status': self.status,
            'resolver_username': (
                resolver_settings and resolver_settings.username),
            'resolver_profile_picture_data_url': (
                resolver_settings and
                resolver_settings.profile_picture_data_url),
            'resolved_on_msecs': (
                self.resolved_on and
                utils.get_time_in_millisecs(self.resolved_on)),
        }
示例#55
0
 def get(self):
     """Handles GET requests."""
     user_settings = user_services.get_user_settings(self.user_id)
     self.values.update({
         'preferred_language_codes':
         user_settings.preferred_language_codes,
         'profile_picture_data_url':
         user_settings.profile_picture_data_url,
         'user_bio':
         user_settings.user_bio,
         'can_receive_email_updates':
         user_services.get_email_preferences(
             self.user_id)['can_receive_email_updates'],
     })
     self.render_json(self.values)
示例#56
0
def send_instant_feedback_message_email(
        recipient_id, sender_id, message, email_subject, exploration_title,
        exploration_id, thread_title):

    email_body_template = (
        'Hi %s,<br><br>'
        'New update to thread "%s" on '
        '<a href="https://www.oppia.org/%s">%s</a>:<br>'
        '<ul><li>%s: %s<br></li></ul>'
        '(You received this message because you are a '
        'participant in this thread.)<br><br>'
        'Best wishes,<br>'
        'The Oppia team<br>'
        '<br>%s')

    if not feconf.CAN_SEND_EMAILS:
        log_new_error('This app cannot send emails to users.')
        return

    if not feconf.CAN_SEND_FEEDBACK_MESSAGE_EMAILS:
        log_new_error('This app cannot send feedback message emails to users.')
        return

    sender_settings = user_services.get_user_settings(sender_id)
    recipient_settings = user_services.get_user_settings(recipient_id)
    recipient_preferences = user_services.get_email_preferences(recipient_id)

    if recipient_preferences['can_receive_feedback_message_email']:
        email_body = email_body_template % (
            recipient_settings.username, thread_title, exploration_id,
            exploration_title, sender_settings.username, message,
            EMAIL_FOOTER.value)
        _send_email(
            recipient_id, feconf.SYSTEM_COMMITTER_ID,
            feconf.EMAIL_INTENT_FEEDBACK_MESSAGE_NOTIFICATION, email_subject,
            email_body, feconf.NOREPLY_EMAIL_ADDRESS)
示例#57
0
    def get(self):
        """Handles GET requests."""
        # TODO(sll): Support index pages for other language codes.
        summary_dicts_by_category = summary_services.get_library_groups(
            [feconf.DEFAULT_LANGUAGE_CODE])
        recently_published_summary_dicts = (
            summary_services.get_recently_published_exploration_summary_dicts(
            ))
        top_rated_activity_summary_dicts = (
            summary_services.get_top_rated_exploration_summary_dicts(
                [feconf.DEFAULT_LANGUAGE_CODE]))
        featured_activity_summary_dicts = (
            summary_services.get_featured_activity_summary_dicts(
                [feconf.DEFAULT_LANGUAGE_CODE]))

        preferred_language_codes = [feconf.DEFAULT_LANGUAGE_CODE]
        if self.user_id:
            user_settings = user_services.get_user_settings(self.user_id)
            preferred_language_codes = user_settings.preferred_language_codes

        if recently_published_summary_dicts:
            summary_dicts_by_category.insert(
                0, {
                    'activity_summary_dicts': recently_published_summary_dicts,
                    'categories': [],
                    'header': feconf.LIBRARY_CATEGORY_RECENTLY_PUBLISHED,
                })
        if top_rated_activity_summary_dicts:
            summary_dicts_by_category.insert(
                0, {
                    'activity_summary_dicts': top_rated_activity_summary_dicts,
                    'categories': [],
                    'header': feconf.LIBRARY_CATEGORY_TOP_RATED_EXPLORATIONS,
                })
        if featured_activity_summary_dicts:
            summary_dicts_by_category.insert(
                0, {
                    'activity_summary_dicts': featured_activity_summary_dicts,
                    'categories': [],
                    'header': feconf.LIBRARY_CATEGORY_FEATURED_ACTIVITIES,
                })

        self.values.update({
            'activity_summary_dicts_by_category': (summary_dicts_by_category),
            'preferred_language_codes':
            preferred_language_codes,
        })
        self.render_json(self.values)
示例#58
0
 def test_get_profile_picture_with_updated_value(self):
     self.get_json('/preferenceshandler/profile_picture',
                   expected_status_int=401)
     self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME)
     owner_id = self.get_user_id_from_email(self.OWNER_EMAIL)
     self.login(self.OWNER_EMAIL)
     user_settings = user_services.get_user_settings(owner_id)
     response = self.get_json('/preferenceshandler/profile_picture')
     self.assertEqual(response['profile_picture_data_url'],
                      user_settings.profile_picture_data_url)
     user_services.update_profile_picture_data_url(owner_id,
                                                   'new_profile_picture')
     response = self.get_json('/preferenceshandler/profile_picture')
     self.assertEqual(response['profile_picture_data_url'],
                      'new_profile_picture')
     self.logout()
示例#59
0
def send_mail_to_onboard_new_reviewers(user_id, category):
    """Sends an email to users who have crossed the threshold score.

    Args:
        user_id: str. The ID of the user who is being offered to become a
            reviewer.
        category: str. The category that the user is being offered to review.
    """

    email_subject = 'Invitation to review suggestions'

    email_body_template = (
        'Hi %s,<br><br>'
        'Thank you for actively contributing high-quality suggestions for '
        'Oppia\'s lessons in %s, and for helping to make these lessons better '
        'for students around the world!<br><br>'
        'In recognition of your contributions, we would like to invite you to '
        'become one of Oppia\'s reviewers. As a reviewer, you will be able to '
        'review suggestions in %s, and contribute to helping ensure that any '
        'edits made to lessons preserve the lessons\' quality and are '
        'beneficial for students.<br><br>'
        'If you\'d like to help out as a reviewer, please visit your '
        '<a href="https://www.oppia.org/creator_dashboard/">dashboard</a>. '
        'and set your review preferences accordingly. Note that, if you accept,'
        'you will receive occasional emails inviting you to review incoming '
        'suggestions by others.<br><br>'
        'Again, thank you for your contributions to the Oppia community!<br>'
        '- The Oppia Team<br>'
        '<br>%s')

    if not feconf.CAN_SEND_EMAILS:
        log_new_error('This app cannot send emails to users.')
        return

    recipient_user_settings = user_services.get_user_settings(user_id)
    can_user_receive_email = user_services.get_email_preferences(
        user_id).can_receive_email_updates

    if can_user_receive_email:
        # Send email only if recipient wants to receive.
        email_body = email_body_template % (
            recipient_user_settings.username, category, category,
            EMAIL_FOOTER.value)
        _send_email(
            user_id, feconf.SYSTEM_COMMITTER_ID,
            feconf.EMAIL_INTENT_ONBOARD_REVIEWER,
            email_subject, email_body, feconf.NOREPLY_EMAIL_ADDRESS)
示例#60
0
def pre_delete_user(user_id):
    """Prepare user for the full deletion.
        1. Mark all the activities that are private and solely owned by the user
           being deleted as deleted.
        2. Disable all the email preferences.
        3. Mark the user as to be deleted.
        4. Create PendingDeletionRequestModel for the user.

    Args:
        user_id: str. The id of the user to be deleted. If the user_id
            corresponds to a profile user then only that profile is deleted.
            For a full user, all of its associated profile users are deleted
            too.
    """
    pending_deletion_requests = []
    user_settings = user_services.get_user_settings(user_id, strict=True)

    linked_profile_user_ids = [
        user.user_id for user in
        user_services.get_all_profiles_auth_details_by_parent_user_id(user_id)
    ]
    profile_users_settings_list = user_services.get_users_settings(
        linked_profile_user_ids)
    for profile_user_settings in profile_users_settings_list:
        profile_id = profile_user_settings.user_id
        user_services.mark_user_for_deletion(profile_id)
        pending_deletion_requests.append(
            wipeout_domain.PendingDeletionRequest.create_default(
                profile_id, profile_user_settings.email,
                profile_user_settings.role))
    if user_settings.role != feconf.ROLE_ID_LEARNER:
        taskqueue_services.defer(
            taskqueue_services.FUNCTION_ID_REMOVE_USER_FROM_RIGHTS_MODELS,
            taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS,
            user_id,
        )
        # Set all the user's email preferences to False in order to disable all
        # ordinary emails that could be sent to the users.
        user_services.update_email_preferences(user_id, False, False, False,
                                               False)

    user_services.mark_user_for_deletion(user_id)
    pending_deletion_requests.append(
        wipeout_domain.PendingDeletionRequest.create_default(
            user_id, user_settings.email, user_settings.role))

    save_pending_deletion_requests(pending_deletion_requests)