mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	analytics: Migrate to @typed_endpoint.
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							3ea3f48338
						
					
				
				
					commit
					00a54099a2
				
			@@ -1,7 +1,7 @@
 | 
			
		||||
import logging
 | 
			
		||||
from collections import defaultdict
 | 
			
		||||
from datetime import datetime, timedelta, timezone
 | 
			
		||||
from typing import Any, Optional, TypeAlias, TypeVar, cast
 | 
			
		||||
from typing import Annotated, Any, Optional, TypeAlias, TypeVar, cast
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db.models import QuerySet
 | 
			
		||||
@@ -10,6 +10,7 @@ from django.shortcuts import render
 | 
			
		||||
from django.utils import translation
 | 
			
		||||
from django.utils.timezone import now as timezone_now
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
from pydantic import BeforeValidator, Json, NonNegativeInt
 | 
			
		||||
 | 
			
		||||
from analytics.lib.counts import COUNT_STATS, CountStat
 | 
			
		||||
from analytics.lib.time_utils import time_range
 | 
			
		||||
@@ -30,11 +31,10 @@ from zerver.decorator import (
 | 
			
		||||
)
 | 
			
		||||
from zerver.lib.exceptions import JsonableError
 | 
			
		||||
from zerver.lib.i18n import get_and_set_request_language, get_language_translation_data
 | 
			
		||||
from zerver.lib.request import REQ, has_request_variables
 | 
			
		||||
from zerver.lib.response import json_success
 | 
			
		||||
from zerver.lib.streams import access_stream_by_id
 | 
			
		||||
from zerver.lib.timestamp import convert_to_UTC
 | 
			
		||||
from zerver.lib.validator import to_non_negative_int
 | 
			
		||||
from zerver.lib.typed_endpoint import PathOnly, typed_endpoint
 | 
			
		||||
from zerver.models import Client, Realm, Stream, UserProfile
 | 
			
		||||
from zerver.models.realms import get_realm
 | 
			
		||||
 | 
			
		||||
@@ -111,8 +111,8 @@ def stats(request: HttpRequest) -> HttpResponse:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@require_server_admin
 | 
			
		||||
@has_request_variables
 | 
			
		||||
def stats_for_realm(request: HttpRequest, realm_str: str) -> HttpResponse:
 | 
			
		||||
@typed_endpoint
 | 
			
		||||
def stats_for_realm(request: HttpRequest, *, realm_str: PathOnly[str]) -> HttpResponse:
 | 
			
		||||
    try:
 | 
			
		||||
        realm = get_realm(realm_str)
 | 
			
		||||
    except Realm.DoesNotExist:
 | 
			
		||||
@@ -127,9 +127,9 @@ def stats_for_realm(request: HttpRequest, realm_str: str) -> HttpResponse:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@require_server_admin
 | 
			
		||||
@has_request_variables
 | 
			
		||||
@typed_endpoint
 | 
			
		||||
def stats_for_remote_realm(
 | 
			
		||||
    request: HttpRequest, remote_server_id: int, remote_realm_id: int
 | 
			
		||||
    request: HttpRequest, *, remote_server_id: PathOnly[int], remote_realm_id: PathOnly[int]
 | 
			
		||||
) -> HttpResponse:
 | 
			
		||||
    assert settings.ZILENCER_ENABLED
 | 
			
		||||
    server = RemoteZulipServer.objects.get(id=remote_server_id)
 | 
			
		||||
@@ -142,22 +142,45 @@ def stats_for_remote_realm(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@require_server_admin_api
 | 
			
		||||
@has_request_variables
 | 
			
		||||
@typed_endpoint
 | 
			
		||||
def get_chart_data_for_realm(
 | 
			
		||||
    request: HttpRequest, /, user_profile: UserProfile, realm_str: str, **kwargs: Any
 | 
			
		||||
    request: HttpRequest,
 | 
			
		||||
    user_profile: UserProfile,
 | 
			
		||||
    /,
 | 
			
		||||
    *,
 | 
			
		||||
    realm_str: PathOnly[str],
 | 
			
		||||
    chart_name: str,
 | 
			
		||||
    min_length: Json[NonNegativeInt] | None = None,
 | 
			
		||||
    start: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
    end: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
) -> HttpResponse:
 | 
			
		||||
    try:
 | 
			
		||||
        realm = get_realm(realm_str)
 | 
			
		||||
    except Realm.DoesNotExist:
 | 
			
		||||
        raise JsonableError(_("Invalid organization"))
 | 
			
		||||
 | 
			
		||||
    return get_chart_data(request, user_profile, realm=realm, **kwargs)
 | 
			
		||||
    return do_get_chart_data(
 | 
			
		||||
        request,
 | 
			
		||||
        user_profile,
 | 
			
		||||
        realm=realm,
 | 
			
		||||
        chart_name=chart_name,
 | 
			
		||||
        min_length=min_length,
 | 
			
		||||
        start=start,
 | 
			
		||||
        end=end,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@require_non_guest_user
 | 
			
		||||
@has_request_variables
 | 
			
		||||
@typed_endpoint
 | 
			
		||||
def get_chart_data_for_stream(
 | 
			
		||||
    request: HttpRequest, /, user_profile: UserProfile, stream_id: int
 | 
			
		||||
    request: HttpRequest,
 | 
			
		||||
    user_profile: UserProfile,
 | 
			
		||||
    *,
 | 
			
		||||
    stream_id: PathOnly[int],
 | 
			
		||||
    chart_name: str,
 | 
			
		||||
    min_length: Json[NonNegativeInt] | None = None,
 | 
			
		||||
    start: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
    end: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
) -> HttpResponse:
 | 
			
		||||
    stream, ignored_sub = access_stream_by_id(
 | 
			
		||||
        user_profile,
 | 
			
		||||
@@ -166,28 +189,43 @@ def get_chart_data_for_stream(
 | 
			
		||||
        allow_realm_admin=True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    return get_chart_data(request, user_profile, stream=stream)
 | 
			
		||||
    return do_get_chart_data(
 | 
			
		||||
        request,
 | 
			
		||||
        user_profile,
 | 
			
		||||
        stream=stream,
 | 
			
		||||
        chart_name=chart_name,
 | 
			
		||||
        min_length=min_length,
 | 
			
		||||
        start=start,
 | 
			
		||||
        end=end,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@require_server_admin_api
 | 
			
		||||
@has_request_variables
 | 
			
		||||
@typed_endpoint
 | 
			
		||||
def get_chart_data_for_remote_realm(
 | 
			
		||||
    request: HttpRequest,
 | 
			
		||||
    /,
 | 
			
		||||
    user_profile: UserProfile,
 | 
			
		||||
    remote_server_id: int,
 | 
			
		||||
    remote_realm_id: int,
 | 
			
		||||
    **kwargs: Any,
 | 
			
		||||
    /,
 | 
			
		||||
    *,
 | 
			
		||||
    remote_server_id: PathOnly[int],
 | 
			
		||||
    remote_realm_id: PathOnly[int],
 | 
			
		||||
    chart_name: str,
 | 
			
		||||
    min_length: Json[NonNegativeInt] | None = None,
 | 
			
		||||
    start: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
    end: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
) -> HttpResponse:
 | 
			
		||||
    assert settings.ZILENCER_ENABLED
 | 
			
		||||
    server = RemoteZulipServer.objects.get(id=remote_server_id)
 | 
			
		||||
    return get_chart_data(
 | 
			
		||||
    return do_get_chart_data(
 | 
			
		||||
        request,
 | 
			
		||||
        user_profile,
 | 
			
		||||
        server=server,
 | 
			
		||||
        remote=True,
 | 
			
		||||
        remote_realm_id=int(remote_realm_id),
 | 
			
		||||
        **kwargs,
 | 
			
		||||
        remote_realm_id=remote_realm_id,
 | 
			
		||||
        chart_name=chart_name,
 | 
			
		||||
        min_length=min_length,
 | 
			
		||||
        start=start,
 | 
			
		||||
        end=end,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -210,47 +248,89 @@ def stats_for_remote_installation(request: HttpRequest, remote_server_id: int) -
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@require_server_admin_api
 | 
			
		||||
@has_request_variables
 | 
			
		||||
@typed_endpoint
 | 
			
		||||
def get_chart_data_for_installation(
 | 
			
		||||
    request: HttpRequest, /, user_profile: UserProfile, chart_name: str = REQ(), **kwargs: Any
 | 
			
		||||
    request: HttpRequest,
 | 
			
		||||
    user_profile: UserProfile,
 | 
			
		||||
    /,
 | 
			
		||||
    *,
 | 
			
		||||
    chart_name: str,
 | 
			
		||||
    min_length: Json[NonNegativeInt] | None = None,
 | 
			
		||||
    start: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
    end: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
) -> HttpResponse:
 | 
			
		||||
    return get_chart_data(request, user_profile, for_installation=True, **kwargs)
 | 
			
		||||
    return do_get_chart_data(
 | 
			
		||||
        request,
 | 
			
		||||
        user_profile,
 | 
			
		||||
        for_installation=True,
 | 
			
		||||
        chart_name=chart_name,
 | 
			
		||||
        min_length=min_length,
 | 
			
		||||
        start=start,
 | 
			
		||||
        end=end,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@require_server_admin_api
 | 
			
		||||
@has_request_variables
 | 
			
		||||
@typed_endpoint
 | 
			
		||||
def get_chart_data_for_remote_installation(
 | 
			
		||||
    request: HttpRequest,
 | 
			
		||||
    /,
 | 
			
		||||
    user_profile: UserProfile,
 | 
			
		||||
    remote_server_id: int,
 | 
			
		||||
    chart_name: str = REQ(),
 | 
			
		||||
    **kwargs: Any,
 | 
			
		||||
    /,
 | 
			
		||||
    *,
 | 
			
		||||
    remote_server_id: PathOnly[int],
 | 
			
		||||
    chart_name: str,
 | 
			
		||||
    min_length: Json[NonNegativeInt] | None = None,
 | 
			
		||||
    start: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
    end: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
) -> HttpResponse:
 | 
			
		||||
    assert settings.ZILENCER_ENABLED
 | 
			
		||||
    server = RemoteZulipServer.objects.get(id=remote_server_id)
 | 
			
		||||
    return get_chart_data(
 | 
			
		||||
    return do_get_chart_data(
 | 
			
		||||
        request,
 | 
			
		||||
        user_profile,
 | 
			
		||||
        for_installation=True,
 | 
			
		||||
        remote=True,
 | 
			
		||||
        server=server,
 | 
			
		||||
        **kwargs,
 | 
			
		||||
        chart_name=chart_name,
 | 
			
		||||
        min_length=min_length,
 | 
			
		||||
        start=start,
 | 
			
		||||
        end=end,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@require_non_guest_user
 | 
			
		||||
@has_request_variables
 | 
			
		||||
@typed_endpoint
 | 
			
		||||
def get_chart_data(
 | 
			
		||||
    request: HttpRequest,
 | 
			
		||||
    user_profile: UserProfile,
 | 
			
		||||
    chart_name: str = REQ(),
 | 
			
		||||
    min_length: int | None = REQ(converter=to_non_negative_int, default=None),
 | 
			
		||||
    start: datetime | None = REQ(converter=to_utc_datetime, default=None),
 | 
			
		||||
    end: datetime | None = REQ(converter=to_utc_datetime, default=None),
 | 
			
		||||
    # These last several parameters are only used by functions
 | 
			
		||||
    # wrapping get_chart_data; the callers are responsible for
 | 
			
		||||
    # parsing/validation/authorization for them.
 | 
			
		||||
    *,
 | 
			
		||||
    chart_name: str,
 | 
			
		||||
    min_length: Json[NonNegativeInt] | None = None,
 | 
			
		||||
    start: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
    end: Annotated[datetime | None, BeforeValidator(to_utc_datetime)] = None,
 | 
			
		||||
) -> HttpResponse:
 | 
			
		||||
    return do_get_chart_data(
 | 
			
		||||
        request,
 | 
			
		||||
        user_profile,
 | 
			
		||||
        chart_name=chart_name,
 | 
			
		||||
        min_length=min_length,
 | 
			
		||||
        start=start,
 | 
			
		||||
        end=end,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@require_non_guest_user
 | 
			
		||||
def do_get_chart_data(
 | 
			
		||||
    request: HttpRequest,
 | 
			
		||||
    user_profile: UserProfile,
 | 
			
		||||
    *,
 | 
			
		||||
    # Common parameters supported by all stats endpoints.
 | 
			
		||||
    chart_name: str,
 | 
			
		||||
    min_length: NonNegativeInt | None = None,
 | 
			
		||||
    start: datetime | None = None,
 | 
			
		||||
    end: datetime | None = None,
 | 
			
		||||
    # The following parameters are only used by wrapping functions for
 | 
			
		||||
    # various contexts; the callers are responsible for validating them.
 | 
			
		||||
    realm: Realm | None = None,
 | 
			
		||||
    for_installation: bool = False,
 | 
			
		||||
    remote: bool = False,
 | 
			
		||||
 
 | 
			
		||||
@@ -955,7 +955,7 @@ def internal_api_view(
 | 
			
		||||
    return _wrapped_view_func
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_utc_datetime(var_name: str, timestamp: str) -> datetime:
 | 
			
		||||
def to_utc_datetime(timestamp: str) -> datetime:
 | 
			
		||||
    return timestamp_to_datetime(float(timestamp))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user