generator.views.events_stream_view

Class to describe an events_stream view.

  1"""Class to describe an `events_stream` view."""
  2
  3from __future__ import annotations
  4
  5from copy import deepcopy
  6from typing import Any, Iterator, Optional
  7
  8from . import lookml_utils
  9from .lookml_utils import DEFAULT_MAX_SUGGEST_PERSIST_FOR
 10from .view import View, ViewDict
 11
 12
 13class EventsStreamView(View):
 14    """A view for querying `events_stream` data, with one row per event."""
 15
 16    type: str = "events_stream_view"
 17
 18    default_measures: list[dict[str, str]] = [
 19        {
 20            "name": "event_count",
 21            "type": "count",
 22            "description": "The number of times the event(s) occurred.",
 23        },
 24        # GleanPingViews were previously generated for some `events_stream` views, and those had
 25        # `ping_count` measures, so we generate the same measures here to avoid breaking anything.
 26        # TODO: Remove this once dashboards have been migrated to use the proper `event_count` measures.
 27        {
 28            "name": "ping_count",
 29            "type": "count",
 30            "hidden": "yes",
 31        },
 32    ]
 33
 34    def __init__(self, namespace: str, name: str, tables: list[dict[str, str]]):
 35        """Get an instance of an EventsStreamView."""
 36        super().__init__(namespace, name, EventsStreamView.type, tables)
 37
 38    @classmethod
 39    def from_db_views(
 40        klass,
 41        namespace: str,
 42        is_glean: bool,
 43        channels: list[dict[str, str]],
 44        db_views: dict,
 45    ) -> Iterator[EventsStreamView]:
 46        """Get EventsStreamViews from db views."""
 47        for view_id in db_views[namespace]:
 48            if view_id.endswith("events_stream"):
 49                yield EventsStreamView(
 50                    namespace,
 51                    view_id,
 52                    [{"table": f"mozdata.{namespace}.{view_id}"}],
 53                )
 54
 55    @classmethod
 56    def from_dict(
 57        klass, namespace: str, name: str, _dict: ViewDict
 58    ) -> EventsStreamView:
 59        """Get EventsStreamView from a name and dict definition."""
 60        return EventsStreamView(namespace, name, _dict["tables"])
 61
 62    def to_lookml(self, v1_name: Optional[str], dryrun) -> dict[str, Any]:
 63        """Generate LookML for this view."""
 64        dimensions = lookml_utils._generate_dimensions(
 65            self.tables[0]["table"], dryrun=dryrun
 66        )
 67        for dimension in dimensions:
 68            if dimension["name"] == "event_id":
 69                dimension["primary_key"] = "yes"
 70            elif dimension["name"] == "experiments":
 71                dimension["sql"] = "JSON_KEYS(${TABLE}.experiments, 1)"
 72
 73        measures = self.get_measures(dimensions)
 74
 75        return {
 76            "views": [
 77                {
 78                    "name": self.name,
 79                    "sql_table_name": f"`{self.tables[0]['table']}`",
 80                    "dimensions": [
 81                        d for d in dimensions if not lookml_utils._is_dimension_group(d)
 82                    ],
 83                    "dimension_groups": [
 84                        d for d in dimensions if lookml_utils._is_dimension_group(d)
 85                    ],
 86                    "measures": measures,
 87                },
 88                {
 89                    "name": f"{self.name}__experiments",
 90                    "dimensions": [
 91                        {
 92                            "name": "id",
 93                            "type": "string",
 94                            "sql": "${TABLE}",
 95                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
 96                        },
 97                        {
 98                            "name": "branch",
 99                            "type": "string",
100                            "sql": "JSON_VALUE(events_stream.experiments[${TABLE}].branch)",
101                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
102                        },
103                        {
104                            "name": "enrollment_id",
105                            "type": "string",
106                            "sql": "JSON_VALUE(events_stream.experiments[${TABLE}].extra.enrollment_id)",
107                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
108                        },
109                        {
110                            "name": "type",
111                            "type": "string",
112                            "sql": "JSON_VALUE(events_stream.experiments[${TABLE}].extra.type)",
113                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
114                        },
115                    ],
116                },
117            ],
118        }
119
120    def get_measures(self, dimensions: list[dict[str, Any]]) -> list[dict[str, str]]:
121        """Get measures for this view."""
122        measures = deepcopy(EventsStreamView.default_measures)
123        if client_id_dimension := self.get_client_id(
124            dimensions, self.tables[0]["table"]
125        ):
126            measures.append(
127                {
128                    "name": "client_count",
129                    "type": "count_distinct",
130                    "sql": f"${{{client_id_dimension}}}",
131                    "description": "The number of clients that completed the event(s).",
132                }
133            )
134            # GleanPingViews were previously generated for some `events_stream` views, and those had
135            # `clients` measures, so we generate the same measures here to avoid breaking anything.
136            # TODO: Remove this once dashboards have been migrated to use the proper `client_count` measures.
137            measures.append(
138                {
139                    "name": "clients",
140                    "type": "count_distinct",
141                    "sql": f"${{{client_id_dimension}}}",
142                    "hidden": "yes",
143                }
144            )
145        return measures
class EventsStreamView(generator.views.view.View):
 14class EventsStreamView(View):
 15    """A view for querying `events_stream` data, with one row per event."""
 16
 17    type: str = "events_stream_view"
 18
 19    default_measures: list[dict[str, str]] = [
 20        {
 21            "name": "event_count",
 22            "type": "count",
 23            "description": "The number of times the event(s) occurred.",
 24        },
 25        # GleanPingViews were previously generated for some `events_stream` views, and those had
 26        # `ping_count` measures, so we generate the same measures here to avoid breaking anything.
 27        # TODO: Remove this once dashboards have been migrated to use the proper `event_count` measures.
 28        {
 29            "name": "ping_count",
 30            "type": "count",
 31            "hidden": "yes",
 32        },
 33    ]
 34
 35    def __init__(self, namespace: str, name: str, tables: list[dict[str, str]]):
 36        """Get an instance of an EventsStreamView."""
 37        super().__init__(namespace, name, EventsStreamView.type, tables)
 38
 39    @classmethod
 40    def from_db_views(
 41        klass,
 42        namespace: str,
 43        is_glean: bool,
 44        channels: list[dict[str, str]],
 45        db_views: dict,
 46    ) -> Iterator[EventsStreamView]:
 47        """Get EventsStreamViews from db views."""
 48        for view_id in db_views[namespace]:
 49            if view_id.endswith("events_stream"):
 50                yield EventsStreamView(
 51                    namespace,
 52                    view_id,
 53                    [{"table": f"mozdata.{namespace}.{view_id}"}],
 54                )
 55
 56    @classmethod
 57    def from_dict(
 58        klass, namespace: str, name: str, _dict: ViewDict
 59    ) -> EventsStreamView:
 60        """Get EventsStreamView from a name and dict definition."""
 61        return EventsStreamView(namespace, name, _dict["tables"])
 62
 63    def to_lookml(self, v1_name: Optional[str], dryrun) -> dict[str, Any]:
 64        """Generate LookML for this view."""
 65        dimensions = lookml_utils._generate_dimensions(
 66            self.tables[0]["table"], dryrun=dryrun
 67        )
 68        for dimension in dimensions:
 69            if dimension["name"] == "event_id":
 70                dimension["primary_key"] = "yes"
 71            elif dimension["name"] == "experiments":
 72                dimension["sql"] = "JSON_KEYS(${TABLE}.experiments, 1)"
 73
 74        measures = self.get_measures(dimensions)
 75
 76        return {
 77            "views": [
 78                {
 79                    "name": self.name,
 80                    "sql_table_name": f"`{self.tables[0]['table']}`",
 81                    "dimensions": [
 82                        d for d in dimensions if not lookml_utils._is_dimension_group(d)
 83                    ],
 84                    "dimension_groups": [
 85                        d for d in dimensions if lookml_utils._is_dimension_group(d)
 86                    ],
 87                    "measures": measures,
 88                },
 89                {
 90                    "name": f"{self.name}__experiments",
 91                    "dimensions": [
 92                        {
 93                            "name": "id",
 94                            "type": "string",
 95                            "sql": "${TABLE}",
 96                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
 97                        },
 98                        {
 99                            "name": "branch",
100                            "type": "string",
101                            "sql": "JSON_VALUE(events_stream.experiments[${TABLE}].branch)",
102                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
103                        },
104                        {
105                            "name": "enrollment_id",
106                            "type": "string",
107                            "sql": "JSON_VALUE(events_stream.experiments[${TABLE}].extra.enrollment_id)",
108                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
109                        },
110                        {
111                            "name": "type",
112                            "type": "string",
113                            "sql": "JSON_VALUE(events_stream.experiments[${TABLE}].extra.type)",
114                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
115                        },
116                    ],
117                },
118            ],
119        }
120
121    def get_measures(self, dimensions: list[dict[str, Any]]) -> list[dict[str, str]]:
122        """Get measures for this view."""
123        measures = deepcopy(EventsStreamView.default_measures)
124        if client_id_dimension := self.get_client_id(
125            dimensions, self.tables[0]["table"]
126        ):
127            measures.append(
128                {
129                    "name": "client_count",
130                    "type": "count_distinct",
131                    "sql": f"${{{client_id_dimension}}}",
132                    "description": "The number of clients that completed the event(s).",
133                }
134            )
135            # GleanPingViews were previously generated for some `events_stream` views, and those had
136            # `clients` measures, so we generate the same measures here to avoid breaking anything.
137            # TODO: Remove this once dashboards have been migrated to use the proper `client_count` measures.
138            measures.append(
139                {
140                    "name": "clients",
141                    "type": "count_distinct",
142                    "sql": f"${{{client_id_dimension}}}",
143                    "hidden": "yes",
144                }
145            )
146        return measures

A view for querying events_stream data, with one row per event.

EventsStreamView(namespace: str, name: str, tables: list[dict[str, str]])
35    def __init__(self, namespace: str, name: str, tables: list[dict[str, str]]):
36        """Get an instance of an EventsStreamView."""
37        super().__init__(namespace, name, EventsStreamView.type, tables)

Get an instance of an EventsStreamView.

type: str = 'events_stream_view'
default_measures: list[dict[str, str]] = [{'name': 'event_count', 'type': 'count', 'description': 'The number of times the event(s) occurred.'}, {'name': 'ping_count', 'type': 'count', 'hidden': 'yes'}]
@classmethod
def from_db_views( klass, namespace: str, is_glean: bool, channels: list[dict[str, str]], db_views: dict) -> Iterator[EventsStreamView]:
39    @classmethod
40    def from_db_views(
41        klass,
42        namespace: str,
43        is_glean: bool,
44        channels: list[dict[str, str]],
45        db_views: dict,
46    ) -> Iterator[EventsStreamView]:
47        """Get EventsStreamViews from db views."""
48        for view_id in db_views[namespace]:
49            if view_id.endswith("events_stream"):
50                yield EventsStreamView(
51                    namespace,
52                    view_id,
53                    [{"table": f"mozdata.{namespace}.{view_id}"}],
54                )

Get EventsStreamViews from db views.

@classmethod
def from_dict( klass, namespace: str, name: str, _dict: generator.views.view.ViewDict) -> EventsStreamView:
56    @classmethod
57    def from_dict(
58        klass, namespace: str, name: str, _dict: ViewDict
59    ) -> EventsStreamView:
60        """Get EventsStreamView from a name and dict definition."""
61        return EventsStreamView(namespace, name, _dict["tables"])

Get EventsStreamView from a name and dict definition.

def to_lookml(self, v1_name: Optional[str], dryrun) -> dict[str, typing.Any]:
 63    def to_lookml(self, v1_name: Optional[str], dryrun) -> dict[str, Any]:
 64        """Generate LookML for this view."""
 65        dimensions = lookml_utils._generate_dimensions(
 66            self.tables[0]["table"], dryrun=dryrun
 67        )
 68        for dimension in dimensions:
 69            if dimension["name"] == "event_id":
 70                dimension["primary_key"] = "yes"
 71            elif dimension["name"] == "experiments":
 72                dimension["sql"] = "JSON_KEYS(${TABLE}.experiments, 1)"
 73
 74        measures = self.get_measures(dimensions)
 75
 76        return {
 77            "views": [
 78                {
 79                    "name": self.name,
 80                    "sql_table_name": f"`{self.tables[0]['table']}`",
 81                    "dimensions": [
 82                        d for d in dimensions if not lookml_utils._is_dimension_group(d)
 83                    ],
 84                    "dimension_groups": [
 85                        d for d in dimensions if lookml_utils._is_dimension_group(d)
 86                    ],
 87                    "measures": measures,
 88                },
 89                {
 90                    "name": f"{self.name}__experiments",
 91                    "dimensions": [
 92                        {
 93                            "name": "id",
 94                            "type": "string",
 95                            "sql": "${TABLE}",
 96                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
 97                        },
 98                        {
 99                            "name": "branch",
100                            "type": "string",
101                            "sql": "JSON_VALUE(events_stream.experiments[${TABLE}].branch)",
102                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
103                        },
104                        {
105                            "name": "enrollment_id",
106                            "type": "string",
107                            "sql": "JSON_VALUE(events_stream.experiments[${TABLE}].extra.enrollment_id)",
108                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
109                        },
110                        {
111                            "name": "type",
112                            "type": "string",
113                            "sql": "JSON_VALUE(events_stream.experiments[${TABLE}].extra.type)",
114                            "suggest_persist_for": DEFAULT_MAX_SUGGEST_PERSIST_FOR,
115                        },
116                    ],
117                },
118            ],
119        }

Generate LookML for this view.

def get_measures(self, dimensions: list[dict[str, typing.Any]]) -> list[dict[str, str]]:
121    def get_measures(self, dimensions: list[dict[str, Any]]) -> list[dict[str, str]]:
122        """Get measures for this view."""
123        measures = deepcopy(EventsStreamView.default_measures)
124        if client_id_dimension := self.get_client_id(
125            dimensions, self.tables[0]["table"]
126        ):
127            measures.append(
128                {
129                    "name": "client_count",
130                    "type": "count_distinct",
131                    "sql": f"${{{client_id_dimension}}}",
132                    "description": "The number of clients that completed the event(s).",
133                }
134            )
135            # GleanPingViews were previously generated for some `events_stream` views, and those had
136            # `clients` measures, so we generate the same measures here to avoid breaking anything.
137            # TODO: Remove this once dashboards have been migrated to use the proper `client_count` measures.
138            measures.append(
139                {
140                    "name": "clients",
141                    "type": "count_distinct",
142                    "sql": f"${{{client_id_dimension}}}",
143                    "hidden": "yes",
144                }
145            )
146        return measures

Get measures for this view.