generator/views/events_view.py (84 lines of code) (raw):

"""Class to describe an Events view.""" from __future__ import annotations from copy import deepcopy from typing import Any, Dict, Iterator, List, Optional from . import lookml_utils from .view import View, ViewDict class EventsView(View): """A view for querying events data, with one row per-event.""" type: str = "events_view" default_measures: List[Dict[str, str]] = [ { "name": "event_count", "type": "count", "description": ("The number of times the event(s) occurred."), }, ] def __init__(self, namespace: str, name: str, tables: List[Dict[str, str]]): """Get an instance of an EventsView.""" super().__init__(namespace, name, EventsView.type, tables) @classmethod def from_db_views( klass, namespace: str, is_glean: bool, channels: List[Dict[str, str]], db_views: dict, ) -> Iterator[EventsView]: """Get Events Views from db views and app variants.""" # We can guarantee there will always be at least one channel, # because this comes from the associated _get_glean_repos in # namespaces.py dataset = next( (channel for channel in channels if channel.get("channel") == "release"), channels[0], )["dataset"] for view_id, references in db_views[dataset].items(): if view_id == "events_unnested": yield EventsView( namespace, "events", [ { "events_table_view": "events_unnested_table", "base_table": f"mozdata.{dataset}.{view_id}", } ], ) @classmethod def from_dict(klass, namespace: str, name: str, _dict: ViewDict) -> EventsView: """Get a view from a name and dict definition.""" return EventsView(namespace, name, _dict["tables"]) def to_lookml(self, v1_name: Optional[str], dryrun) -> Dict[str, Any]: """Generate LookML for this view.""" view_defn: Dict[str, Any] = { "extends": [self.tables[0]["events_table_view"]], "name": self.name, } # add measures dimensions = lookml_utils._generate_dimensions( self.tables[0]["base_table"], dryrun=dryrun ) view_defn["measures"] = self.get_measures(dimensions) # set document_id as primary key if it exists in the underlying table # this will allow one_to_many joins event_id_dimension = self.generate_event_id_dimension(dimensions) if event_id_dimension is not None: view_defn["dimensions"] = [event_id_dimension] return { "includes": [f"{self.tables[0]['events_table_view']}.view.lkml"], "views": [view_defn], } def get_measures(self, dimensions) -> List[Dict[str, str]]: """Generate measures for Events Views.""" measures = deepcopy(EventsView.default_measures) client_id_field = self.get_client_id(dimensions, "events") if client_id_field is not None: measures.append( { "name": "client_count", "type": "count_distinct", "sql": f"${{{client_id_field}}}", "description": ( "The number of clients that completed the event(s)." ), } ) return measures def generate_event_id_dimension( self, dimensions: list[dict] ) -> Optional[Dict[str, str]]: """Generate the event_id dimension to be used as a primary key for a one to many join.""" event_id = self.select_dimension("event_id", dimensions, "events") if event_id: return { "name": "event_id", "primary_key": "yes", } return None