generator.views.client_counts_view
Class to describe a Client Counts View.
1"""Class to describe a Client Counts View.""" 2 3from __future__ import annotations 4 5from copy import deepcopy 6from typing import Any, Dict, Iterator, List, Optional, Union 7 8from .view import View, ViewDict 9 10 11class ClientCountsView(View): 12 """A view for Client Counting measures.""" 13 14 type: str = "client_counts_view" 15 16 default_dimension_groups: List[Dict[str, Union[str, List[str]]]] = [ 17 { 18 "name": "since_first_seen", 19 "type": "duration", 20 "description": "Amount of time that has passed since the client was first seen.", 21 "sql_start": "CAST(${TABLE}.first_seen_date AS TIMESTAMP)", 22 "sql_end": "CAST(${TABLE}.submission_date AS TIMESTAMP)", 23 "intervals": ["day", "week", "month", "year"], 24 } 25 ] 26 27 default_dimensions: List[Dict[str, str]] = [ 28 { 29 "name": "have_completed_period", 30 "type": "yesno", 31 "description": "Only for use with cohort analysis. " 32 "Filter on true to remove the tail of incomplete data from cohorts. " 33 "Indicates whether the cohort for this row have all had a chance to complete this interval. " 34 "For example, new clients from yesterday have not all had a chance to send a ping for today.", 35 "sql": """ 36 DATE_ADD( 37 {% if client_counts.first_seen_date._is_selected %} 38 DATE_ADD(DATE(${client_counts.first_seen_date}), INTERVAL 1 DAY) 39 {% elsif client_counts.first_seen_week._is_selected %} 40 DATE_ADD(DATE(${client_counts.first_seen_week}), INTERVAL 1 WEEK) 41 {% elsif client_counts.first_seen_month._is_selected %} 42 DATE_ADD(PARSE_DATE('%Y-%m', ${client_counts.first_seen_month}), INTERVAL 1 MONTH) 43 {% elsif client_counts.first_seen_year._is_selected %} 44 DATE_ADD(DATE(${client_counts.first_seen_year}, 1, 1), INTERVAL 1 YEAR) 45 {% endif %} 46 , 47 {% if client_counts.days_since_first_seen._is_selected %} 48 INTERVAL CAST(${client_counts.days_since_first_seen} AS INT64) DAY 49 {% elsif client_counts.weeks_since_first_seen._is_selected %} 50 INTERVAL CAST(${client_counts.weeks_since_first_seen} AS INT64) WEEK 51 {% elsif client_counts.months_since_first_seen._is_selected %} 52 INTERVAL CAST(${client_counts.months_since_first_seen} AS INT64) MONTH 53 {% elsif client_counts.years_since_first_seen._is_selected %} 54 INTERVAL CAST(${client_counts.months_since_first_seen} AS INT64) YEAR 55 {% endif %} 56 ) < current_date 57 """, 58 } 59 ] 60 61 default_measures: List[Dict[str, Union[str, List[Dict[str, str]]]]] = [ 62 { 63 "name": "client_count", 64 "type": "number", 65 "description": "The number of clients, " 66 "determined by whether they sent a baseline ping on the day in question.", 67 "sql": "COUNT(DISTINCT ${TABLE}.client_id)", 68 } 69 ] 70 71 def __init__( 72 self, 73 namespace: str, 74 tables: List[Dict[str, str]], 75 name: str = "client_counts", 76 ): 77 """Get an instance of a ClientCountsView.""" 78 super().__init__(namespace, name, ClientCountsView.type, tables) 79 80 @classmethod 81 def from_db_views( 82 klass, 83 namespace: str, 84 is_glean: bool, 85 channels: List[Dict[str, str]], 86 db_views: dict, 87 ) -> Iterator[ClientCountsView]: 88 """Get Client Count Views from db views and app variants.""" 89 # We can guarantee there will always be at least one channel, 90 # because this comes from the associated _get_glean_repos in 91 # namespaces.py 92 dataset = next( 93 (channel for channel in channels if channel.get("channel") == "release"), 94 channels[0], 95 )["dataset"] 96 97 for view_id, references in db_views[dataset].items(): 98 if view_id == "baseline_clients_daily" or view_id == "clients_daily": 99 yield ClientCountsView( 100 namespace, [{"table": f"mozdata.{dataset}.{view_id}"}] 101 ) 102 103 @classmethod 104 def from_dict( 105 klass, namespace: str, name: str, _dict: ViewDict 106 ) -> ClientCountsView: 107 """Get a view from a name and dict definition.""" 108 return ClientCountsView(namespace, _dict["tables"], name) 109 110 def to_lookml(self, v1_name: Optional[str], dryrun) -> Dict[str, Any]: 111 """Generate LookML for this view.""" 112 table = self.tables[0]["table"] 113 114 base_view = "baseline_clients_daily_table" 115 if table is not None: 116 base_view = table.split(".")[-1] + "_table" 117 118 view_defn: Dict[str, Any] = { 119 "extends": [base_view], 120 "name": self.name, 121 } 122 123 # add dimensions and dimension groups 124 view_defn["dimensions"] = deepcopy(ClientCountsView.default_dimensions) 125 view_defn["dimension_groups"] = deepcopy( 126 ClientCountsView.default_dimension_groups 127 ) 128 129 # add measures 130 view_defn["measures"] = self.get_measures() 131 132 return { 133 "includes": [base_view + ".view.lkml"], 134 "views": [view_defn], 135 } 136 137 def get_measures(self) -> List[Dict[str, Union[str, List[Dict[str, str]]]]]: 138 """Generate measures for the Growth Accounting Framework.""" 139 return deepcopy(ClientCountsView.default_measures)
12class ClientCountsView(View): 13 """A view for Client Counting measures.""" 14 15 type: str = "client_counts_view" 16 17 default_dimension_groups: List[Dict[str, Union[str, List[str]]]] = [ 18 { 19 "name": "since_first_seen", 20 "type": "duration", 21 "description": "Amount of time that has passed since the client was first seen.", 22 "sql_start": "CAST(${TABLE}.first_seen_date AS TIMESTAMP)", 23 "sql_end": "CAST(${TABLE}.submission_date AS TIMESTAMP)", 24 "intervals": ["day", "week", "month", "year"], 25 } 26 ] 27 28 default_dimensions: List[Dict[str, str]] = [ 29 { 30 "name": "have_completed_period", 31 "type": "yesno", 32 "description": "Only for use with cohort analysis. " 33 "Filter on true to remove the tail of incomplete data from cohorts. " 34 "Indicates whether the cohort for this row have all had a chance to complete this interval. " 35 "For example, new clients from yesterday have not all had a chance to send a ping for today.", 36 "sql": """ 37 DATE_ADD( 38 {% if client_counts.first_seen_date._is_selected %} 39 DATE_ADD(DATE(${client_counts.first_seen_date}), INTERVAL 1 DAY) 40 {% elsif client_counts.first_seen_week._is_selected %} 41 DATE_ADD(DATE(${client_counts.first_seen_week}), INTERVAL 1 WEEK) 42 {% elsif client_counts.first_seen_month._is_selected %} 43 DATE_ADD(PARSE_DATE('%Y-%m', ${client_counts.first_seen_month}), INTERVAL 1 MONTH) 44 {% elsif client_counts.first_seen_year._is_selected %} 45 DATE_ADD(DATE(${client_counts.first_seen_year}, 1, 1), INTERVAL 1 YEAR) 46 {% endif %} 47 , 48 {% if client_counts.days_since_first_seen._is_selected %} 49 INTERVAL CAST(${client_counts.days_since_first_seen} AS INT64) DAY 50 {% elsif client_counts.weeks_since_first_seen._is_selected %} 51 INTERVAL CAST(${client_counts.weeks_since_first_seen} AS INT64) WEEK 52 {% elsif client_counts.months_since_first_seen._is_selected %} 53 INTERVAL CAST(${client_counts.months_since_first_seen} AS INT64) MONTH 54 {% elsif client_counts.years_since_first_seen._is_selected %} 55 INTERVAL CAST(${client_counts.months_since_first_seen} AS INT64) YEAR 56 {% endif %} 57 ) < current_date 58 """, 59 } 60 ] 61 62 default_measures: List[Dict[str, Union[str, List[Dict[str, str]]]]] = [ 63 { 64 "name": "client_count", 65 "type": "number", 66 "description": "The number of clients, " 67 "determined by whether they sent a baseline ping on the day in question.", 68 "sql": "COUNT(DISTINCT ${TABLE}.client_id)", 69 } 70 ] 71 72 def __init__( 73 self, 74 namespace: str, 75 tables: List[Dict[str, str]], 76 name: str = "client_counts", 77 ): 78 """Get an instance of a ClientCountsView.""" 79 super().__init__(namespace, name, ClientCountsView.type, tables) 80 81 @classmethod 82 def from_db_views( 83 klass, 84 namespace: str, 85 is_glean: bool, 86 channels: List[Dict[str, str]], 87 db_views: dict, 88 ) -> Iterator[ClientCountsView]: 89 """Get Client Count Views from db views and app variants.""" 90 # We can guarantee there will always be at least one channel, 91 # because this comes from the associated _get_glean_repos in 92 # namespaces.py 93 dataset = next( 94 (channel for channel in channels if channel.get("channel") == "release"), 95 channels[0], 96 )["dataset"] 97 98 for view_id, references in db_views[dataset].items(): 99 if view_id == "baseline_clients_daily" or view_id == "clients_daily": 100 yield ClientCountsView( 101 namespace, [{"table": f"mozdata.{dataset}.{view_id}"}] 102 ) 103 104 @classmethod 105 def from_dict( 106 klass, namespace: str, name: str, _dict: ViewDict 107 ) -> ClientCountsView: 108 """Get a view from a name and dict definition.""" 109 return ClientCountsView(namespace, _dict["tables"], name) 110 111 def to_lookml(self, v1_name: Optional[str], dryrun) -> Dict[str, Any]: 112 """Generate LookML for this view.""" 113 table = self.tables[0]["table"] 114 115 base_view = "baseline_clients_daily_table" 116 if table is not None: 117 base_view = table.split(".")[-1] + "_table" 118 119 view_defn: Dict[str, Any] = { 120 "extends": [base_view], 121 "name": self.name, 122 } 123 124 # add dimensions and dimension groups 125 view_defn["dimensions"] = deepcopy(ClientCountsView.default_dimensions) 126 view_defn["dimension_groups"] = deepcopy( 127 ClientCountsView.default_dimension_groups 128 ) 129 130 # add measures 131 view_defn["measures"] = self.get_measures() 132 133 return { 134 "includes": [base_view + ".view.lkml"], 135 "views": [view_defn], 136 } 137 138 def get_measures(self) -> List[Dict[str, Union[str, List[Dict[str, str]]]]]: 139 """Generate measures for the Growth Accounting Framework.""" 140 return deepcopy(ClientCountsView.default_measures)
A view for Client Counting measures.
ClientCountsView( namespace: str, tables: List[Dict[str, str]], name: str = 'client_counts')
72 def __init__( 73 self, 74 namespace: str, 75 tables: List[Dict[str, str]], 76 name: str = "client_counts", 77 ): 78 """Get an instance of a ClientCountsView.""" 79 super().__init__(namespace, name, ClientCountsView.type, tables)
Get an instance of a ClientCountsView.
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': "\n DATE_ADD(\n {% if client_counts.first_seen_date._is_selected %}\n DATE_ADD(DATE(${client_counts.first_seen_date}), INTERVAL 1 DAY)\n {% elsif client_counts.first_seen_week._is_selected %}\n DATE_ADD(DATE(${client_counts.first_seen_week}), INTERVAL 1 WEEK)\n {% elsif client_counts.first_seen_month._is_selected %}\n DATE_ADD(PARSE_DATE('%Y-%m', ${client_counts.first_seen_month}), INTERVAL 1 MONTH)\n {% elsif client_counts.first_seen_year._is_selected %}\n DATE_ADD(DATE(${client_counts.first_seen_year}, 1, 1), INTERVAL 1 YEAR)\n {% endif %}\n ,\n {% if client_counts.days_since_first_seen._is_selected %}\n INTERVAL CAST(${client_counts.days_since_first_seen} AS INT64) DAY\n {% elsif client_counts.weeks_since_first_seen._is_selected %}\n INTERVAL CAST(${client_counts.weeks_since_first_seen} AS INT64) WEEK\n {% elsif client_counts.months_since_first_seen._is_selected %}\n INTERVAL CAST(${client_counts.months_since_first_seen} AS INT64) MONTH\n {% elsif client_counts.years_since_first_seen._is_selected %}\n INTERVAL CAST(${client_counts.months_since_first_seen} AS INT64) YEAR\n {% endif %}\n ) < current_date\n "}]
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)'}]
@classmethod
def
from_db_views( klass, namespace: str, is_glean: bool, channels: List[Dict[str, str]], db_views: dict) -> Iterator[ClientCountsView]:
81 @classmethod 82 def from_db_views( 83 klass, 84 namespace: str, 85 is_glean: bool, 86 channels: List[Dict[str, str]], 87 db_views: dict, 88 ) -> Iterator[ClientCountsView]: 89 """Get Client Count Views from db views and app variants.""" 90 # We can guarantee there will always be at least one channel, 91 # because this comes from the associated _get_glean_repos in 92 # namespaces.py 93 dataset = next( 94 (channel for channel in channels if channel.get("channel") == "release"), 95 channels[0], 96 )["dataset"] 97 98 for view_id, references in db_views[dataset].items(): 99 if view_id == "baseline_clients_daily" or view_id == "clients_daily": 100 yield ClientCountsView( 101 namespace, [{"table": f"mozdata.{dataset}.{view_id}"}] 102 )
Get Client Count Views from db views and app variants.
@classmethod
def
from_dict( klass, namespace: str, name: str, _dict: generator.views.view.ViewDict) -> ClientCountsView:
104 @classmethod 105 def from_dict( 106 klass, namespace: str, name: str, _dict: ViewDict 107 ) -> ClientCountsView: 108 """Get a view from a name and dict definition.""" 109 return ClientCountsView(namespace, _dict["tables"], name)
Get a view from a name and dict definition.
def
to_lookml(self, v1_name: Optional[str], dryrun) -> Dict[str, Any]:
111 def to_lookml(self, v1_name: Optional[str], dryrun) -> Dict[str, Any]: 112 """Generate LookML for this view.""" 113 table = self.tables[0]["table"] 114 115 base_view = "baseline_clients_daily_table" 116 if table is not None: 117 base_view = table.split(".")[-1] + "_table" 118 119 view_defn: Dict[str, Any] = { 120 "extends": [base_view], 121 "name": self.name, 122 } 123 124 # add dimensions and dimension groups 125 view_defn["dimensions"] = deepcopy(ClientCountsView.default_dimensions) 126 view_defn["dimension_groups"] = deepcopy( 127 ClientCountsView.default_dimension_groups 128 ) 129 130 # add measures 131 view_defn["measures"] = self.get_measures() 132 133 return { 134 "includes": [base_view + ".view.lkml"], 135 "views": [view_defn], 136 }
Generate LookML for this view.
def
get_measures(self) -> List[Dict[str, Union[str, List[Dict[str, str]]]]]:
138 def get_measures(self) -> List[Dict[str, Union[str, List[Dict[str, str]]]]]: 139 """Generate measures for the Growth Accounting Framework.""" 140 return deepcopy(ClientCountsView.default_measures)
Generate measures for the Growth Accounting Framework.