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
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.
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.