mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	user_profile: Remove 'tutorial_status' field.
The 'tutorial_status' field on 'UserProfile' model is no longer used to show onboarding tutorial. This commit removes the 'tutorial_status' field, 'POST users/me/tutorial_status' endpoint, and 'needs_tutorial' parameter in 'page_params'. Fixes part of zulip#30043.
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							ee806c49b9
						
					
				
				
					commit
					52a9846cdf
				
			@@ -20,6 +20,12 @@ format used by the Zulip server that they are interacting with.
 | 
			
		||||
 | 
			
		||||
## Changes in Zulip 10.0
 | 
			
		||||
 | 
			
		||||
**Feature level 282**
 | 
			
		||||
 | 
			
		||||
* `POST users/me/tutorial_status`: Removed this undocumented endpoint,
 | 
			
		||||
  as the state that it maintained has been replaced by a cleaner
 | 
			
		||||
  `onboarding_steps` implementation.
 | 
			
		||||
 | 
			
		||||
**Feature level 281**
 | 
			
		||||
 | 
			
		||||
* [`GET /events`](/api/get-events), [`POST /register`](/api/register-queue):
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
 | 
			
		||||
# new level means in api_docs/changelog.md, as well as "**Changes**"
 | 
			
		||||
# entries in the endpoint's documentation in `zulip.yaml`.
 | 
			
		||||
 | 
			
		||||
API_FEATURE_LEVEL = 281  # Last bumped for realm_can_delete_any_message_group
 | 
			
		||||
API_FEATURE_LEVEL = 282  # Last bumped for removing "POST users/me/tutorial_status"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,6 @@ const home_params_schema = default_params_schema
 | 
			
		||||
        narrow: z.optional(z.array(narrow_term_schema)),
 | 
			
		||||
        narrow_stream: z.optional(z.string()),
 | 
			
		||||
        narrow_topic: z.optional(z.string()),
 | 
			
		||||
        needs_tutorial: z.boolean(),
 | 
			
		||||
        promote_sponsoring_zulip: z.boolean(),
 | 
			
		||||
        // `realm_rendered_description` is only sent for spectators, because
 | 
			
		||||
        // it isn't displayed for logged-in users and requires markdown
 | 
			
		||||
 
 | 
			
		||||
@@ -2,27 +2,18 @@ import $ from "jquery";
 | 
			
		||||
 | 
			
		||||
import * as blueslip from "./blueslip";
 | 
			
		||||
import * as loading from "./loading";
 | 
			
		||||
import {page_params} from "./page_params";
 | 
			
		||||
import * as util from "./util";
 | 
			
		||||
 | 
			
		||||
export let page_load_time: number | undefined;
 | 
			
		||||
 | 
			
		||||
// Miscellaneous early setup.
 | 
			
		||||
$(() => {
 | 
			
		||||
    if (util.is_mobile()) {
 | 
			
		||||
        // Disable the tutorial; it's ugly on mobile.
 | 
			
		||||
        page_params.needs_tutorial = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    page_load_time = Date.now();
 | 
			
		||||
 | 
			
		||||
    // Display loading indicator.  This disappears after the first
 | 
			
		||||
    // get_events completes.
 | 
			
		||||
    if (!page_params.needs_tutorial) {
 | 
			
		||||
        loading.make_indicator($("#page_loading_indicator"), {
 | 
			
		||||
            abs_positioned: true,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    loading.make_indicator($("#page_loading_indicator"), {
 | 
			
		||||
        abs_positioned: true,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $.fn.get_offset_to_window = function () {
 | 
			
		||||
        return this[0]!.getBoundingClientRect();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
import * as channel from "./channel";
 | 
			
		||||
import {page_params} from "./page_params";
 | 
			
		||||
 | 
			
		||||
function set_tutorial_status(status, callback) {
 | 
			
		||||
    return channel.post({
 | 
			
		||||
        url: "/json/users/me/tutorial_status",
 | 
			
		||||
        data: {status},
 | 
			
		||||
        success: callback,
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function initialize() {
 | 
			
		||||
    if (page_params.needs_tutorial) {
 | 
			
		||||
        set_tutorial_status("started");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -132,7 +132,6 @@ import * as tippyjs from "./tippyjs";
 | 
			
		||||
import * as topic_list from "./topic_list";
 | 
			
		||||
import * as topic_popover from "./topic_popover";
 | 
			
		||||
import * as transmit from "./transmit";
 | 
			
		||||
import * as tutorial from "./tutorial";
 | 
			
		||||
import * as typeahead_helper from "./typeahead_helper";
 | 
			
		||||
import * as typing from "./typing";
 | 
			
		||||
import * as unread from "./unread";
 | 
			
		||||
@@ -642,9 +641,6 @@ export function initialize_everything(state_data) {
 | 
			
		||||
            );
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
    // This needs to happen after activity_ui.initialize, so that user_filter
 | 
			
		||||
    // is defined. Also, must happen after people.initialize()
 | 
			
		||||
    tutorial.initialize();
 | 
			
		||||
 | 
			
		||||
    // All overlays, and also activity_ui, must be initialized before hashchange.js
 | 
			
		||||
    hashchange.initialize();
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,6 @@ def bulk_create_users(
 | 
			
		||||
            tos_version,
 | 
			
		||||
            timezone,
 | 
			
		||||
            default_language=realm.default_language,
 | 
			
		||||
            tutorial_status=UserProfile.TUTORIAL_FINISHED,
 | 
			
		||||
            email_address_visibility=email_address_visibility,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -92,7 +92,6 @@ def create_user_profile(
 | 
			
		||||
    tos_version: str | None,
 | 
			
		||||
    timezone: str,
 | 
			
		||||
    default_language: str,
 | 
			
		||||
    tutorial_status: str = UserProfile.TUTORIAL_WAITING,
 | 
			
		||||
    force_id: int | None = None,
 | 
			
		||||
    force_date_joined: datetime | None = None,
 | 
			
		||||
    *,
 | 
			
		||||
@@ -122,7 +121,6 @@ def create_user_profile(
 | 
			
		||||
        is_mirror_dummy=is_mirror_dummy,
 | 
			
		||||
        tos_version=tos_version,
 | 
			
		||||
        timezone=timezone,
 | 
			
		||||
        tutorial_status=tutorial_status,
 | 
			
		||||
        default_language=default_language,
 | 
			
		||||
        delivery_email=email,
 | 
			
		||||
        email_address_visibility=email_address_visibility,
 | 
			
		||||
 
 | 
			
		||||
@@ -140,7 +140,6 @@ def build_page_params_for_home_page_load(
 | 
			
		||||
    narrow: list[NarrowTerm],
 | 
			
		||||
    narrow_stream: Stream | None,
 | 
			
		||||
    narrow_topic_name: str | None,
 | 
			
		||||
    needs_tutorial: bool,
 | 
			
		||||
) -> tuple[int, dict[str, object]]:
 | 
			
		||||
    """
 | 
			
		||||
    This function computes page_params for when we load the home page.
 | 
			
		||||
@@ -211,7 +210,6 @@ def build_page_params_for_home_page_load(
 | 
			
		||||
        corporate_enabled=settings.CORPORATE_ENABLED,
 | 
			
		||||
        ## Misc. extra data.
 | 
			
		||||
        language_list=get_language_list(),
 | 
			
		||||
        needs_tutorial=needs_tutorial,
 | 
			
		||||
        furthest_read_time=furthest_read_time,
 | 
			
		||||
        bot_types=get_bot_types(user_profile),
 | 
			
		||||
        two_fa_enabled=two_fa_enabled,
 | 
			
		||||
 
 | 
			
		||||
@@ -85,8 +85,3 @@ def copy_onboarding_steps(source_profile: UserProfile, target_profile: UserProfi
 | 
			
		||||
            onboarding_step=onboarding_step.onboarding_step,
 | 
			
		||||
            timestamp=onboarding_step.timestamp,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # TODO: The 'tutorial_status' field of 'UserProfile' model
 | 
			
		||||
    # is no longer used. Remove it.
 | 
			
		||||
    target_profile.tutorial_status = source_profile.tutorial_status
 | 
			
		||||
    target_profile.save(update_fields=["tutorial_status"])
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								zerver/migrations/0569_remove_userprofile_tutorial_status.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								zerver/migrations/0569_remove_userprofile_tutorial_status.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
# Generated by Django 5.0.6 on 2024-07-25 06:59
 | 
			
		||||
 | 
			
		||||
from django.db import migrations
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("zerver", "0568_mark_narrow_to_dm_with_welcome_bot_new_user_as_read"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name="userprofile",
 | 
			
		||||
            name="tutorial_status",
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -591,22 +591,6 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings):
 | 
			
		||||
    # us, pre-thumbnailing.
 | 
			
		||||
    avatar_hash = models.CharField(null=True, max_length=64)
 | 
			
		||||
 | 
			
		||||
    # TODO: TUTORIAL_STATUS was originally an optimization designed to
 | 
			
		||||
    # allow us to skip querying the OnboardingStep table when loading
 | 
			
		||||
    # /. This optimization is no longer effective, so it's possible we
 | 
			
		||||
    # should delete it.
 | 
			
		||||
    TUTORIAL_WAITING = "W"
 | 
			
		||||
    TUTORIAL_STARTED = "S"
 | 
			
		||||
    TUTORIAL_FINISHED = "F"
 | 
			
		||||
    TUTORIAL_STATES = (
 | 
			
		||||
        (TUTORIAL_WAITING, "Waiting"),
 | 
			
		||||
        (TUTORIAL_STARTED, "Started"),
 | 
			
		||||
        (TUTORIAL_FINISHED, "Finished"),
 | 
			
		||||
    )
 | 
			
		||||
    tutorial_status = models.CharField(
 | 
			
		||||
        default=TUTORIAL_WAITING, choices=TUTORIAL_STATES, max_length=1
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    zoom_token = models.JSONField(default=None, null=True)
 | 
			
		||||
 | 
			
		||||
    objects = UserManager()
 | 
			
		||||
 
 | 
			
		||||
@@ -1116,7 +1116,6 @@ class TestHumanUsersOnlyDecorator(ZulipTestCase):
 | 
			
		||||
            "/api/v1/users/me/android_gcm_reg_id",
 | 
			
		||||
            "/api/v1/users/me/onboarding_steps",
 | 
			
		||||
            "/api/v1/users/me/presence",
 | 
			
		||||
            "/api/v1/users/me/tutorial_status",
 | 
			
		||||
        ]
 | 
			
		||||
        for endpoint in post_endpoints:
 | 
			
		||||
            result = self.api_post(default_bot, endpoint)
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,6 @@ class HomeTest(ZulipTestCase):
 | 
			
		||||
        "login_page",
 | 
			
		||||
        "narrow",
 | 
			
		||||
        "narrow_stream",
 | 
			
		||||
        "needs_tutorial",
 | 
			
		||||
        "no_event_queue",
 | 
			
		||||
        "page_type",
 | 
			
		||||
        "promote_sponsoring_zulip",
 | 
			
		||||
@@ -361,7 +360,6 @@ class HomeTest(ZulipTestCase):
 | 
			
		||||
            "language_cookie_name",
 | 
			
		||||
            "language_list",
 | 
			
		||||
            "login_page",
 | 
			
		||||
            "needs_tutorial",
 | 
			
		||||
            "no_event_queue",
 | 
			
		||||
            "page_type",
 | 
			
		||||
            "promote_sponsoring_zulip",
 | 
			
		||||
 
 | 
			
		||||
@@ -2823,7 +2823,6 @@ class UserSignUpTest(ZulipTestCase):
 | 
			
		||||
        hamlet_in_zulip.emojiset = "twitter"
 | 
			
		||||
        hamlet_in_zulip.high_contrast_mode = True
 | 
			
		||||
        hamlet_in_zulip.enter_sends = True
 | 
			
		||||
        hamlet_in_zulip.tutorial_status = UserProfile.TUTORIAL_FINISHED
 | 
			
		||||
        hamlet_in_zulip.email_address_visibility = UserProfile.EMAIL_ADDRESS_VISIBILITY_EVERYONE
 | 
			
		||||
        hamlet_in_zulip.save()
 | 
			
		||||
 | 
			
		||||
@@ -2845,7 +2844,6 @@ class UserSignUpTest(ZulipTestCase):
 | 
			
		||||
        self.assertEqual(hamlet.high_contrast_mode, False)
 | 
			
		||||
        self.assertEqual(hamlet.enable_stream_audible_notifications, False)
 | 
			
		||||
        self.assertEqual(hamlet.enter_sends, False)
 | 
			
		||||
        self.assertEqual(hamlet.tutorial_status, UserProfile.TUTORIAL_WAITING)
 | 
			
		||||
 | 
			
		||||
    def test_signup_with_user_settings_from_another_realm(self) -> None:
 | 
			
		||||
        hamlet_in_zulip = self.example_user("hamlet")
 | 
			
		||||
@@ -2863,7 +2861,6 @@ class UserSignUpTest(ZulipTestCase):
 | 
			
		||||
        hamlet_in_zulip.emojiset = "twitter"
 | 
			
		||||
        hamlet_in_zulip.high_contrast_mode = True
 | 
			
		||||
        hamlet_in_zulip.enter_sends = True
 | 
			
		||||
        hamlet_in_zulip.tutorial_status = UserProfile.TUTORIAL_FINISHED
 | 
			
		||||
        hamlet_in_zulip.email_address_visibility = UserProfile.EMAIL_ADDRESS_VISIBILITY_EVERYONE
 | 
			
		||||
        hamlet_in_zulip.save()
 | 
			
		||||
 | 
			
		||||
@@ -2909,7 +2906,6 @@ class UserSignUpTest(ZulipTestCase):
 | 
			
		||||
        self.assertEqual(hamlet_in_lear.high_contrast_mode, True)
 | 
			
		||||
        self.assertEqual(hamlet_in_lear.enter_sends, True)
 | 
			
		||||
        self.assertEqual(hamlet_in_lear.enable_stream_audible_notifications, False)
 | 
			
		||||
        self.assertEqual(hamlet_in_lear.tutorial_status, UserProfile.TUTORIAL_FINISHED)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            hamlet_in_lear.email_address_visibility, UserProfile.EMAIL_ADDRESS_VISIBILITY_NOBODY
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ from typing_extensions import override
 | 
			
		||||
from zerver.actions.message_send import internal_send_private_message
 | 
			
		||||
from zerver.lib.test_classes import ZulipTestCase
 | 
			
		||||
from zerver.lib.test_helpers import message_stream_count, most_recent_message
 | 
			
		||||
from zerver.models import UserProfile
 | 
			
		||||
from zerver.models.users import get_system_bot
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -28,21 +27,6 @@ class TutorialTests(ZulipTestCase):
 | 
			
		||||
            disable_external_notifications=True,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_tutorial_status(self) -> None:
 | 
			
		||||
        user = self.example_user("hamlet")
 | 
			
		||||
        self.login_user(user)
 | 
			
		||||
 | 
			
		||||
        cases = [
 | 
			
		||||
            ("started", UserProfile.TUTORIAL_STARTED),
 | 
			
		||||
            ("finished", UserProfile.TUTORIAL_FINISHED),
 | 
			
		||||
        ]
 | 
			
		||||
        for incoming_status, expected_db_status in cases:
 | 
			
		||||
            params = dict(status=incoming_status)
 | 
			
		||||
            result = self.client_post("/json/users/me/tutorial_status", params)
 | 
			
		||||
            self.assert_json_success(result)
 | 
			
		||||
            user = self.example_user("hamlet")
 | 
			
		||||
            self.assertEqual(user.tutorial_status, expected_db_status)
 | 
			
		||||
 | 
			
		||||
    def test_response_to_pm_for_app(self) -> None:
 | 
			
		||||
        user = self.example_user("hamlet")
 | 
			
		||||
        bot = get_system_bot(settings.WELCOME_BOT, user.realm_id)
 | 
			
		||||
 
 | 
			
		||||
@@ -215,13 +215,6 @@ def home_real(request: HttpRequest) -> HttpResponse:
 | 
			
		||||
 | 
			
		||||
    narrow, narrow_stream, narrow_topic_name = detect_narrowed_window(request, user_profile)
 | 
			
		||||
 | 
			
		||||
    if user_profile is not None:
 | 
			
		||||
        needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING
 | 
			
		||||
 | 
			
		||||
    else:
 | 
			
		||||
        # The current tutorial doesn't super make sense for logged-out users.
 | 
			
		||||
        needs_tutorial = False
 | 
			
		||||
 | 
			
		||||
    queue_id, page_params = build_page_params_for_home_page_load(
 | 
			
		||||
        request=request,
 | 
			
		||||
        user_profile=user_profile,
 | 
			
		||||
@@ -230,7 +223,6 @@ def home_real(request: HttpRequest) -> HttpResponse:
 | 
			
		||||
        narrow=narrow,
 | 
			
		||||
        narrow_stream=narrow_stream,
 | 
			
		||||
        narrow_topic_name=narrow_topic_name,
 | 
			
		||||
        needs_tutorial=needs_tutorial,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    log_data = RequestNotes.get_notes(request).log_data
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
from typing import Literal
 | 
			
		||||
 | 
			
		||||
from django.http import HttpRequest, HttpResponse
 | 
			
		||||
 | 
			
		||||
from zerver.decorator import human_users_only
 | 
			
		||||
from zerver.lib.response import json_success
 | 
			
		||||
from zerver.lib.typed_endpoint import typed_endpoint
 | 
			
		||||
from zerver.models import UserProfile
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@human_users_only
 | 
			
		||||
@typed_endpoint
 | 
			
		||||
def set_tutorial_status(
 | 
			
		||||
    request: HttpRequest, user_profile: UserProfile, *, status: Literal["started", "finished"]
 | 
			
		||||
) -> HttpResponse:
 | 
			
		||||
    if status == "started":
 | 
			
		||||
        user_profile.tutorial_status = UserProfile.TUTORIAL_STARTED
 | 
			
		||||
    elif status == "finished":
 | 
			
		||||
        user_profile.tutorial_status = UserProfile.TUTORIAL_FINISHED
 | 
			
		||||
    user_profile.save(update_fields=["tutorial_status"])
 | 
			
		||||
 | 
			
		||||
    return json_success(request)
 | 
			
		||||
@@ -176,7 +176,6 @@ from zerver.views.streams import (
 | 
			
		||||
)
 | 
			
		||||
from zerver.views.submessage import process_submessage
 | 
			
		||||
from zerver.views.thumbnail import backend_serve_thumbnail
 | 
			
		||||
from zerver.views.tutorial import set_tutorial_status
 | 
			
		||||
from zerver.views.typing import send_notification_backend
 | 
			
		||||
from zerver.views.unsubscribe import email_unsubscribe
 | 
			
		||||
from zerver.views.upload import (
 | 
			
		||||
@@ -426,16 +425,6 @@ v1_api_and_json_patterns = [
 | 
			
		||||
            {"intentionally_undocumented"},
 | 
			
		||||
        ),
 | 
			
		||||
    ),
 | 
			
		||||
    # users/me/tutorial_status -> zerver.views.tutorial
 | 
			
		||||
    rest_path(
 | 
			
		||||
        "users/me/tutorial_status",
 | 
			
		||||
        POST=(
 | 
			
		||||
            set_tutorial_status,
 | 
			
		||||
            # This is a relic of an old Zulip tutorial model and
 | 
			
		||||
            # should be deleted.
 | 
			
		||||
            {"intentionally_undocumented"},
 | 
			
		||||
        ),
 | 
			
		||||
    ),
 | 
			
		||||
    # settings -> zerver.views.user_settings
 | 
			
		||||
    rest_path("settings", PATCH=json_change_settings),
 | 
			
		||||
    # These next two are legacy aliases for /settings, from before
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user