(backend) extend analytics module to support feature flags

Extend the AnalyticsBackend class to support
user feature flags, without a breaking change to the
current implementation.

A flag value is considered to be a bool or a string.
This commit is contained in:
Florent Chehab
2026-07-06 21:38:13 +02:00
parent b00c4e5d6d
commit 8edcb6e973
3 changed files with 34 additions and 1 deletions
+8
View File
@@ -19,6 +19,7 @@ from django.utils.module_loading import import_string
from .base import AnalyticsBackend, NoOpAnalytics
from .events import AnalyticsEvent
from .user_feature_flags import UserFeatureFlag
__all__ = [
"get_analytics",
@@ -26,6 +27,8 @@ __all__ = [
"capture",
"AnalyticsBackend",
"AnalyticsEvent",
"is_user_feature_flag_enabled",
"UserFeatureFlag",
]
@@ -57,3 +60,8 @@ def capture(
) -> None:
"""Record an event performed by an identified user."""
analytics_instance.capture(user, event, properties)
def is_user_feature_flag_enabled(user, feature_name: UserFeatureFlag) -> bool:
"""Check if a feature is enabled at the user level."""
return analytics_instance.is_user_feature_enabled(user, feature_name)
+17 -1
View File
@@ -1,10 +1,11 @@
"""Analytics backend protocol and default no-op implementation."""
from abc import ABC, abstractmethod
from typing import Any
from typing import Any, Mapping
from ..models import User
from .events import AnalyticsEvent
from .user_feature_flags import UserFeatureFlag
class AnalyticsBackend(ABC):
@@ -35,6 +36,21 @@ class AnalyticsBackend(ABC):
def shutdown(self) -> None:
"""Flush pending events. Called on process exit."""
def get_user_feature_flags(
self,
user: User, # pylint: disable=unused-argument
) -> Mapping[UserFeatureFlag, bool | str | None]:
"""Return a dict of feature flags for the given user."""
# We return an empty dict here by default to avoid a breaking change
# By making this method abstract.
return {}
def is_user_feature_enabled(
self, user: User, feature_name: UserFeatureFlag
) -> bool:
"""Check if a feature is enabled at the user level."""
return self.get_user_feature_flags(user).get(feature_name, False) is True
class NoOpAnalytics(AnalyticsBackend):
"""Default backend: silently discards everything."""
@@ -0,0 +1,9 @@
"""Catalog of all analytics feature flags used by the backend."""
from enum import StrEnum
class UserFeatureFlag(StrEnum):
"""All feature flags configured in the app."""
TRANSCRIPT_SUMMARY_ENABLED = "summary-enabled"