mirror of
https://github.com/suitenumerique/meet.git
synced 2026-07-27 12:19:10 +00:00
8edcb6e973
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.
66 lines
2.1 KiB
Python
66 lines
2.1 KiB
Python
"""Analytics backend protocol and default no-op implementation."""
|
|
|
|
from abc import ABC, abstractmethod
|
|
from typing import Any, Mapping
|
|
|
|
from ..models import User
|
|
from .events import AnalyticsEvent
|
|
from .user_feature_flags import UserFeatureFlag
|
|
|
|
|
|
class AnalyticsBackend(ABC):
|
|
"""
|
|
Interface every analytics backend must implement.
|
|
|
|
Backends are instantiated once (singleton) with the kwargs declared in
|
|
settings.ANALYTICS_BACKEND_SETTINGS, e.g.:
|
|
|
|
ANALYTICS_BACKEND = "core.analytics.posthog.PostHogAnalytics"
|
|
ANALYTICS_BACKEND_SETTINGS = {"api_key": "...", "host": "..."}
|
|
"""
|
|
|
|
@abstractmethod
|
|
def identify(self, user: User, properties: dict[str, Any] | None = None) -> None:
|
|
"""Associate traits (email, name, ...) with an identified user."""
|
|
|
|
@abstractmethod
|
|
def capture(
|
|
self,
|
|
user: User,
|
|
event: AnalyticsEvent,
|
|
properties: dict[str, Any] | None = None,
|
|
) -> None:
|
|
"""Record an event performed by an identified user."""
|
|
|
|
@abstractmethod
|
|
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."""
|
|
|
|
def identify(self, user: User, properties=None) -> None:
|
|
"""No-op: discards identify calls."""
|
|
|
|
def capture(self, user, event, properties=None) -> None:
|
|
"""No-op: discards captured events."""
|
|
|
|
def shutdown(self) -> None:
|
|
"""No-op: nothing to flush."""
|