generator/views/client_counts_view.py (86 lines of code) (raw):

"""Class to describe a Client Counts View.""" from __future__ import annotations from copy import deepcopy from typing import Any, Dict, Iterator, List, Optional, Union from .view import View, ViewDict class ClientCountsView(View): """A view for Client Counting measures.""" type: str = "client_counts_view" default_dimension_groups: List[Dict[str, Union[str, List[str]]]] = [ { "name": "since_first_seen", "type": "duration", "description": "Amount of time that has passed since the client was first seen.", "sql_start": "CAST(${TABLE}.first_seen_date AS TIMESTAMP)", "sql_end": "CAST(${TABLE}.submission_date AS TIMESTAMP)", "intervals": ["day", "week", "month", "year"], } ] default_dimensions: List[Dict[str, str]] = [ { "name": "have_completed_period", "type": "yesno", "description": "Only for use with cohort analysis. " "Filter on true to remove the tail of incomplete data from cohorts. " "Indicates whether the cohort for this row have all had a chance to complete this interval. " "For example, new clients from yesterday have not all had a chance to send a ping for today.", "sql": """ DATE_ADD( {% if client_counts.first_seen_date._is_selected %} DATE_ADD(DATE(${client_counts.first_seen_date}), INTERVAL 1 DAY) {% elsif client_counts.first_seen_week._is_selected %} DATE_ADD(DATE(${client_counts.first_seen_week}), INTERVAL 1 WEEK) {% elsif client_counts.first_seen_month._is_selected %} DATE_ADD(PARSE_DATE('%Y-%m', ${client_counts.first_seen_month}), INTERVAL 1 MONTH) {% elsif client_counts.first_seen_year._is_selected %} DATE_ADD(DATE(${client_counts.first_seen_year}, 1, 1), INTERVAL 1 YEAR) {% endif %} , {% if client_counts.days_since_first_seen._is_selected %} INTERVAL CAST(${client_counts.days_since_first_seen} AS INT64) DAY {% elsif client_counts.weeks_since_first_seen._is_selected %} INTERVAL CAST(${client_counts.weeks_since_first_seen} AS INT64) WEEK {% elsif client_counts.months_since_first_seen._is_selected %} INTERVAL CAST(${client_counts.months_since_first_seen} AS INT64) MONTH {% elsif client_counts.years_since_first_seen._is_selected %} INTERVAL CAST(${client_counts.months_since_first_seen} AS INT64) YEAR {% endif %} ) < current_date """, } ] default_measures: List[Dict[str, Union[str, List[Dict[str, str]]]]] = [ { "name": "client_count", "type": "number", "description": "The number of clients, " "determined by whether they sent a baseline ping on the day in question.", "sql": "COUNT(DISTINCT ${TABLE}.client_id)", } ] def __init__( self, namespace: str, tables: List[Dict[str, str]], name: str = "client_counts", ): """Get an instance of a ClientCountsView.""" super().__init__(namespace, name, ClientCountsView.type, tables) @classmethod def from_db_views( klass, namespace: str, is_glean: bool, channels: List[Dict[str, str]], db_views: dict, ) -> Iterator[ClientCountsView]: """Get Client Count 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 == "baseline_clients_daily" or view_id == "clients_daily": yield ClientCountsView( namespace, [{"table": f"mozdata.{dataset}.{view_id}"}] ) @classmethod def from_dict( klass, namespace: str, name: str, _dict: ViewDict ) -> ClientCountsView: """Get a view from a name and dict definition.""" return ClientCountsView(namespace, _dict["tables"], name) def to_lookml(self, v1_name: Optional[str], dryrun) -> Dict[str, Any]: """Generate LookML for this view.""" table = self.tables[0]["table"] base_view = "baseline_clients_daily_table" if table is not None: base_view = table.split(".")[-1] + "_table" view_defn: Dict[str, Any] = { "extends": [base_view], "name": self.name, } # add dimensions and dimension groups view_defn["dimensions"] = deepcopy(ClientCountsView.default_dimensions) view_defn["dimension_groups"] = deepcopy( ClientCountsView.default_dimension_groups ) # add measures view_defn["measures"] = self.get_measures() return { "includes": [base_view + ".view.lkml"], "views": [view_defn], } def get_measures(self) -> List[Dict[str, Union[str, List[Dict[str, str]]]]]: """Generate measures for the Growth Accounting Framework.""" return deepcopy(ClientCountsView.default_measures)