generator.views.growth_accounting_view

Class to describe a Growth Accounting View.

  1"""Class to describe a Growth Accounting View."""
  2
  3from __future__ import annotations
  4
  5from copy import deepcopy
  6from itertools import filterfalse
  7from typing import Any, Dict, Iterator, List, Optional, Union
  8
  9from . import lookml_utils
 10from .view import View, ViewDict
 11
 12
 13class GrowthAccountingView(View):
 14    """A view for growth accounting measures."""
 15
 16    type: str = "growth_accounting_view"
 17    DEFAULT_IDENTIFIER_FIELD: str = "client_id"
 18
 19    other_dimensions: List[Dict[str, str]] = [
 20        {
 21            "name": "first",
 22            "sql": "{TABLE}.first",
 23            "type": "yesno",
 24            "hidden": "yes",
 25        }
 26    ]
 27
 28    default_measures: List[Dict[str, Union[str, List[Dict[str, str]]]]] = [
 29        {
 30            "name": "overall_active_previous",
 31            "type": "count",
 32            "filters": [{"active_last_week": "yes"}],
 33        },
 34        {
 35            "name": "overall_active_current",
 36            "type": "count",
 37            "filters": [{"active_this_week": "yes"}],
 38        },
 39        {
 40            "name": "overall_resurrected",
 41            "type": "count",
 42            "filters": [
 43                {"new_last_week": "no"},
 44                {"new_this_week": "no"},
 45                {"active_last_week": "no"},
 46                {"active_this_week": "yes"},
 47            ],
 48        },
 49        {
 50            "name": "new_users",
 51            "type": "count",
 52            "filters": [{"new_this_week": "yes"}, {"active_this_week": "yes"}],
 53        },
 54        {
 55            "name": "established_users_returning",
 56            "type": "count",
 57            "filters": [
 58                {"new_last_week": "no"},
 59                {"new_this_week": "no"},
 60                {"active_last_week": "yes"},
 61                {"active_this_week": "yes"},
 62            ],
 63        },
 64        {
 65            "name": "new_users_returning",
 66            "type": "count",
 67            "filters": [
 68                {"new_last_week": "yes"},
 69                {"active_last_week": "yes"},
 70                {"active_this_week": "yes"},
 71            ],
 72        },
 73        {
 74            "name": "new_users_churned_count",
 75            "type": "count",
 76            "filters": [
 77                {"new_last_week": "yes"},
 78                {"active_last_week": "yes"},
 79                {"active_this_week": "no"},
 80            ],
 81        },
 82        {
 83            "name": "established_users_churned_count",
 84            "type": "count",
 85            "filters": [
 86                {"new_last_week": "no"},
 87                {"new_this_week": "no"},
 88                {"active_last_week": "yes"},
 89                {"active_this_week": "no"},
 90            ],
 91        },
 92        {
 93            "name": "new_users_churned",
 94            "type": "number",
 95            "sql": "-1 * ${new_users_churned_count}",
 96        },
 97        {
 98            "name": "established_users_churned",
 99            "type": "number",
100            "sql": "-1 * ${established_users_churned_count}",
101        },
102        {
103            "name": "overall_churned",
104            "type": "number",
105            "sql": "${new_users_churned} + ${established_users_churned}",
106        },
107        {
108            "name": "overall_retention_rate",
109            "type": "number",
110            "sql": (
111                "SAFE_DIVIDE("
112                "(${established_users_returning} + ${new_users_returning}),"
113                "${overall_active_previous}"
114                ")"
115            ),
116        },
117        {
118            "name": "established_user_retention_rate",
119            "type": "number",
120            "sql": (
121                "SAFE_DIVIDE("
122                "${established_users_returning},"
123                "(${established_users_returning} + ${established_users_churned_count})"
124                ")"
125            ),
126        },
127        {
128            "name": "new_user_retention_rate",
129            "type": "number",
130            "sql": (
131                "SAFE_DIVIDE("
132                "${new_users_returning},"
133                "(${new_users_returning} + ${new_users_churned_count})"
134                ")"
135            ),
136        },
137        {
138            "name": "overall_churn_rate",
139            "type": "number",
140            "sql": (
141                "SAFE_DIVIDE("
142                "(${established_users_churned_count} + ${new_users_churned_count}),"
143                "${overall_active_previous}"
144                ")"
145            ),
146        },
147        {
148            "name": "fraction_of_active_resurrected",
149            "type": "number",
150            "sql": "SAFE_DIVIDE(${overall_resurrected}, ${overall_active_current})",
151        },
152        {
153            "name": "fraction_of_active_new",
154            "type": "number",
155            "sql": "SAFE_DIVIDE(${new_users}, ${overall_active_current})",
156        },
157        {
158            "name": "fraction_of_active_established_returning",
159            "type": "number",
160            "sql": (
161                "SAFE_DIVIDE("
162                "${established_users_returning},"
163                "${overall_active_current}"
164                ")"
165            ),
166        },
167        {
168            "name": "fraction_of_active_new_returning",
169            "type": "number",
170            "sql": "SAFE_DIVIDE(${new_users_returning}, ${overall_active_current})",
171        },
172        {
173            "name": "quick_ratio",
174            "type": "number",
175            "sql": (
176                "SAFE_DIVIDE("
177                "${new_users} + ${overall_resurrected},"
178                "${established_users_churned_count} + ${new_users_churned_count}"
179                ")"
180            ),
181        },
182    ]
183
184    def __init__(
185        self,
186        namespace: str,
187        tables: List[Dict[str, str]],
188        identifier_field: str = DEFAULT_IDENTIFIER_FIELD,
189    ):
190        """Get an instance of a GrowthAccountingView."""
191        self.identifier_field = identifier_field
192
193        super().__init__(
194            namespace, "growth_accounting", GrowthAccountingView.type, tables
195        )
196
197    @classmethod
198    def get_default_dimensions(
199        klass, identifier_field: str = DEFAULT_IDENTIFIER_FIELD
200    ) -> List[Dict[str, str]]:
201        """Get dimensions to be added to GrowthAccountingView by default."""
202        return [
203            {
204                "name": "active_this_week",
205                "sql": "mozfun.bits28.active_in_range(days_seen_bits, -6, 7)",
206                "type": "yesno",
207                "hidden": "yes",
208            },
209            {
210                "name": "active_last_week",
211                "sql": "mozfun.bits28.active_in_range(days_seen_bits, -13, 7)",
212                "type": "yesno",
213                "hidden": "yes",
214            },
215            {
216                "name": "new_this_week",
217                "sql": "DATE_DIFF(${submission_date}, first_run_date, DAY) BETWEEN 0 AND 6",
218                "type": "yesno",
219                "hidden": "yes",
220            },
221            {
222                "name": "new_last_week",
223                "sql": "DATE_DIFF(${submission_date}, first_run_date, DAY) BETWEEN 7 AND 13",
224                "type": "yesno",
225                "hidden": "yes",
226            },
227            {
228                "name": f"{identifier_field}_day",
229                "sql": f"CONCAT(CAST(${{TABLE}}.submission_date AS STRING), ${{{identifier_field}}})",
230                "type": "string",
231                "hidden": "yes",
232                "primary_key": "yes",
233            },
234        ]
235
236    @classmethod
237    def from_db_views(
238        klass,
239        namespace: str,
240        is_glean: bool,
241        channels: List[Dict[str, str]],
242        db_views: dict,
243        identifier_field: str = DEFAULT_IDENTIFIER_FIELD,
244    ) -> Iterator[GrowthAccountingView]:
245        """Get Growth Accounting Views from db views and app variants."""
246        dataset = next(
247            (channel for channel in channels if channel.get("channel") == "release"),
248            channels[0],
249        )["dataset"]
250
251        for view_id, references in db_views[dataset].items():
252            if view_id == "baseline_clients_last_seen":
253                yield GrowthAccountingView(
254                    namespace,
255                    [{"table": f"mozdata.{dataset}.{view_id}"}],
256                    identifier_field=identifier_field,
257                )
258
259    @classmethod
260    def from_dict(
261        klass, namespace: str, name: str, _dict: ViewDict
262    ) -> GrowthAccountingView:
263        """Get a view from a name and dict definition."""
264        return GrowthAccountingView(
265            namespace,
266            _dict["tables"],
267            identifier_field=str(
268                _dict.get(
269                    "identifier_field", GrowthAccountingView.DEFAULT_IDENTIFIER_FIELD
270                )
271            ),
272        )
273
274    def to_lookml(self, v1_name: Optional[str], dryrun) -> Dict[str, Any]:
275        """Generate LookML for this view."""
276        view_defn: Dict[str, Any] = {"name": self.name}
277        table = self.tables[0]["table"]
278
279        # add dimensions and dimension groups
280        dimensions = lookml_utils._generate_dimensions(table, dryrun=dryrun) + deepcopy(
281            GrowthAccountingView.get_default_dimensions(
282                identifier_field=self.identifier_field
283            )
284        )
285
286        view_defn["dimensions"] = list(
287            filterfalse(lookml_utils._is_dimension_group, dimensions)
288        )
289        view_defn["dimension_groups"] = list(
290            filter(lookml_utils._is_dimension_group, dimensions)
291        )
292
293        # add measures
294        view_defn["measures"] = self.get_measures()
295
296        # SQL Table Name
297        view_defn["sql_table_name"] = f"`{table}`"
298
299        return {"views": [view_defn]}
300
301    def get_measures(self) -> List[Dict[str, Union[str, List[Dict[str, str]]]]]:
302        """Generate measures for the Growth Accounting Framework."""
303        return deepcopy(GrowthAccountingView.default_measures)
class GrowthAccountingView(generator.views.view.View):
 14class GrowthAccountingView(View):
 15    """A view for growth accounting measures."""
 16
 17    type: str = "growth_accounting_view"
 18    DEFAULT_IDENTIFIER_FIELD: str = "client_id"
 19
 20    other_dimensions: List[Dict[str, str]] = [
 21        {
 22            "name": "first",
 23            "sql": "{TABLE}.first",
 24            "type": "yesno",
 25            "hidden": "yes",
 26        }
 27    ]
 28
 29    default_measures: List[Dict[str, Union[str, List[Dict[str, str]]]]] = [
 30        {
 31            "name": "overall_active_previous",
 32            "type": "count",
 33            "filters": [{"active_last_week": "yes"}],
 34        },
 35        {
 36            "name": "overall_active_current",
 37            "type": "count",
 38            "filters": [{"active_this_week": "yes"}],
 39        },
 40        {
 41            "name": "overall_resurrected",
 42            "type": "count",
 43            "filters": [
 44                {"new_last_week": "no"},
 45                {"new_this_week": "no"},
 46                {"active_last_week": "no"},
 47                {"active_this_week": "yes"},
 48            ],
 49        },
 50        {
 51            "name": "new_users",
 52            "type": "count",
 53            "filters": [{"new_this_week": "yes"}, {"active_this_week": "yes"}],
 54        },
 55        {
 56            "name": "established_users_returning",
 57            "type": "count",
 58            "filters": [
 59                {"new_last_week": "no"},
 60                {"new_this_week": "no"},
 61                {"active_last_week": "yes"},
 62                {"active_this_week": "yes"},
 63            ],
 64        },
 65        {
 66            "name": "new_users_returning",
 67            "type": "count",
 68            "filters": [
 69                {"new_last_week": "yes"},
 70                {"active_last_week": "yes"},
 71                {"active_this_week": "yes"},
 72            ],
 73        },
 74        {
 75            "name": "new_users_churned_count",
 76            "type": "count",
 77            "filters": [
 78                {"new_last_week": "yes"},
 79                {"active_last_week": "yes"},
 80                {"active_this_week": "no"},
 81            ],
 82        },
 83        {
 84            "name": "established_users_churned_count",
 85            "type": "count",
 86            "filters": [
 87                {"new_last_week": "no"},
 88                {"new_this_week": "no"},
 89                {"active_last_week": "yes"},
 90                {"active_this_week": "no"},
 91            ],
 92        },
 93        {
 94            "name": "new_users_churned",
 95            "type": "number",
 96            "sql": "-1 * ${new_users_churned_count}",
 97        },
 98        {
 99            "name": "established_users_churned",
100            "type": "number",
101            "sql": "-1 * ${established_users_churned_count}",
102        },
103        {
104            "name": "overall_churned",
105            "type": "number",
106            "sql": "${new_users_churned} + ${established_users_churned}",
107        },
108        {
109            "name": "overall_retention_rate",
110            "type": "number",
111            "sql": (
112                "SAFE_DIVIDE("
113                "(${established_users_returning} + ${new_users_returning}),"
114                "${overall_active_previous}"
115                ")"
116            ),
117        },
118        {
119            "name": "established_user_retention_rate",
120            "type": "number",
121            "sql": (
122                "SAFE_DIVIDE("
123                "${established_users_returning},"
124                "(${established_users_returning} + ${established_users_churned_count})"
125                ")"
126            ),
127        },
128        {
129            "name": "new_user_retention_rate",
130            "type": "number",
131            "sql": (
132                "SAFE_DIVIDE("
133                "${new_users_returning},"
134                "(${new_users_returning} + ${new_users_churned_count})"
135                ")"
136            ),
137        },
138        {
139            "name": "overall_churn_rate",
140            "type": "number",
141            "sql": (
142                "SAFE_DIVIDE("
143                "(${established_users_churned_count} + ${new_users_churned_count}),"
144                "${overall_active_previous}"
145                ")"
146            ),
147        },
148        {
149            "name": "fraction_of_active_resurrected",
150            "type": "number",
151            "sql": "SAFE_DIVIDE(${overall_resurrected}, ${overall_active_current})",
152        },
153        {
154            "name": "fraction_of_active_new",
155            "type": "number",
156            "sql": "SAFE_DIVIDE(${new_users}, ${overall_active_current})",
157        },
158        {
159            "name": "fraction_of_active_established_returning",
160            "type": "number",
161            "sql": (
162                "SAFE_DIVIDE("
163                "${established_users_returning},"
164                "${overall_active_current}"
165                ")"
166            ),
167        },
168        {
169            "name": "fraction_of_active_new_returning",
170            "type": "number",
171            "sql": "SAFE_DIVIDE(${new_users_returning}, ${overall_active_current})",
172        },
173        {
174            "name": "quick_ratio",
175            "type": "number",
176            "sql": (
177                "SAFE_DIVIDE("
178                "${new_users} + ${overall_resurrected},"
179                "${established_users_churned_count} + ${new_users_churned_count}"
180                ")"
181            ),
182        },
183    ]
184
185    def __init__(
186        self,
187        namespace: str,
188        tables: List[Dict[str, str]],
189        identifier_field: str = DEFAULT_IDENTIFIER_FIELD,
190    ):
191        """Get an instance of a GrowthAccountingView."""
192        self.identifier_field = identifier_field
193
194        super().__init__(
195            namespace, "growth_accounting", GrowthAccountingView.type, tables
196        )
197
198    @classmethod
199    def get_default_dimensions(
200        klass, identifier_field: str = DEFAULT_IDENTIFIER_FIELD
201    ) -> List[Dict[str, str]]:
202        """Get dimensions to be added to GrowthAccountingView by default."""
203        return [
204            {
205                "name": "active_this_week",
206                "sql": "mozfun.bits28.active_in_range(days_seen_bits, -6, 7)",
207                "type": "yesno",
208                "hidden": "yes",
209            },
210            {
211                "name": "active_last_week",
212                "sql": "mozfun.bits28.active_in_range(days_seen_bits, -13, 7)",
213                "type": "yesno",
214                "hidden": "yes",
215            },
216            {
217                "name": "new_this_week",
218                "sql": "DATE_DIFF(${submission_date}, first_run_date, DAY) BETWEEN 0 AND 6",
219                "type": "yesno",
220                "hidden": "yes",
221            },
222            {
223                "name": "new_last_week",
224                "sql": "DATE_DIFF(${submission_date}, first_run_date, DAY) BETWEEN 7 AND 13",
225                "type": "yesno",
226                "hidden": "yes",
227            },
228            {
229                "name": f"{identifier_field}_day",
230                "sql": f"CONCAT(CAST(${{TABLE}}.submission_date AS STRING), ${{{identifier_field}}})",
231                "type": "string",
232                "hidden": "yes",
233                "primary_key": "yes",
234            },
235        ]
236
237    @classmethod
238    def from_db_views(
239        klass,
240        namespace: str,
241        is_glean: bool,
242        channels: List[Dict[str, str]],
243        db_views: dict,
244        identifier_field: str = DEFAULT_IDENTIFIER_FIELD,
245    ) -> Iterator[GrowthAccountingView]:
246        """Get Growth Accounting Views from db views and app variants."""
247        dataset = next(
248            (channel for channel in channels if channel.get("channel") == "release"),
249            channels[0],
250        )["dataset"]
251
252        for view_id, references in db_views[dataset].items():
253            if view_id == "baseline_clients_last_seen":
254                yield GrowthAccountingView(
255                    namespace,
256                    [{"table": f"mozdata.{dataset}.{view_id}"}],
257                    identifier_field=identifier_field,
258                )
259
260    @classmethod
261    def from_dict(
262        klass, namespace: str, name: str, _dict: ViewDict
263    ) -> GrowthAccountingView:
264        """Get a view from a name and dict definition."""
265        return GrowthAccountingView(
266            namespace,
267            _dict["tables"],
268            identifier_field=str(
269                _dict.get(
270                    "identifier_field", GrowthAccountingView.DEFAULT_IDENTIFIER_FIELD
271                )
272            ),
273        )
274
275    def to_lookml(self, v1_name: Optional[str], dryrun) -> Dict[str, Any]:
276        """Generate LookML for this view."""
277        view_defn: Dict[str, Any] = {"name": self.name}
278        table = self.tables[0]["table"]
279
280        # add dimensions and dimension groups
281        dimensions = lookml_utils._generate_dimensions(table, dryrun=dryrun) + deepcopy(
282            GrowthAccountingView.get_default_dimensions(
283                identifier_field=self.identifier_field
284            )
285        )
286
287        view_defn["dimensions"] = list(
288            filterfalse(lookml_utils._is_dimension_group, dimensions)
289        )
290        view_defn["dimension_groups"] = list(
291            filter(lookml_utils._is_dimension_group, dimensions)
292        )
293
294        # add measures
295        view_defn["measures"] = self.get_measures()
296
297        # SQL Table Name
298        view_defn["sql_table_name"] = f"`{table}`"
299
300        return {"views": [view_defn]}
301
302    def get_measures(self) -> List[Dict[str, Union[str, List[Dict[str, str]]]]]:
303        """Generate measures for the Growth Accounting Framework."""
304        return deepcopy(GrowthAccountingView.default_measures)

A view for growth accounting measures.

GrowthAccountingView( namespace: str, tables: List[Dict[str, str]], identifier_field: str = 'client_id')
185    def __init__(
186        self,
187        namespace: str,
188        tables: List[Dict[str, str]],
189        identifier_field: str = DEFAULT_IDENTIFIER_FIELD,
190    ):
191        """Get an instance of a GrowthAccountingView."""
192        self.identifier_field = identifier_field
193
194        super().__init__(
195            namespace, "growth_accounting", GrowthAccountingView.type, tables
196        )

Get an instance of a GrowthAccountingView.

type: str = 'growth_accounting_view'
DEFAULT_IDENTIFIER_FIELD: str = 'client_id'
other_dimensions: List[Dict[str, str]] = [{'name': 'first', 'sql': '{TABLE}.first', 'type': 'yesno', 'hidden': 'yes'}]
default_measures: List[Dict[str, Union[str, List[Dict[str, str]]]]] = [{'name': 'overall_active_previous', 'type': 'count', 'filters': [{'active_last_week': 'yes'}]}, {'name': 'overall_active_current', 'type': 'count', 'filters': [{'active_this_week': 'yes'}]}, {'name': 'overall_resurrected', 'type': 'count', 'filters': [{'new_last_week': 'no'}, {'new_this_week': 'no'}, {'active_last_week': 'no'}, {'active_this_week': 'yes'}]}, {'name': 'new_users', 'type': 'count', 'filters': [{'new_this_week': 'yes'}, {'active_this_week': 'yes'}]}, {'name': 'established_users_returning', 'type': 'count', 'filters': [{'new_last_week': 'no'}, {'new_this_week': 'no'}, {'active_last_week': 'yes'}, {'active_this_week': 'yes'}]}, {'name': 'new_users_returning', 'type': 'count', 'filters': [{'new_last_week': 'yes'}, {'active_last_week': 'yes'}, {'active_this_week': 'yes'}]}, {'name': 'new_users_churned_count', 'type': 'count', 'filters': [{'new_last_week': 'yes'}, {'active_last_week': 'yes'}, {'active_this_week': 'no'}]}, {'name': 'established_users_churned_count', 'type': 'count', 'filters': [{'new_last_week': 'no'}, {'new_this_week': 'no'}, {'active_last_week': 'yes'}, {'active_this_week': 'no'}]}, {'name': 'new_users_churned', 'type': 'number', 'sql': '-1 * ${new_users_churned_count}'}, {'name': 'established_users_churned', 'type': 'number', 'sql': '-1 * ${established_users_churned_count}'}, {'name': 'overall_churned', 'type': 'number', 'sql': '${new_users_churned} + ${established_users_churned}'}, {'name': 'overall_retention_rate', 'type': 'number', 'sql': 'SAFE_DIVIDE((${established_users_returning} + ${new_users_returning}),${overall_active_previous})'}, {'name': 'established_user_retention_rate', 'type': 'number', 'sql': 'SAFE_DIVIDE(${established_users_returning},(${established_users_returning} + ${established_users_churned_count}))'}, {'name': 'new_user_retention_rate', 'type': 'number', 'sql': 'SAFE_DIVIDE(${new_users_returning},(${new_users_returning} + ${new_users_churned_count}))'}, {'name': 'overall_churn_rate', 'type': 'number', 'sql': 'SAFE_DIVIDE((${established_users_churned_count} + ${new_users_churned_count}),${overall_active_previous})'}, {'name': 'fraction_of_active_resurrected', 'type': 'number', 'sql': 'SAFE_DIVIDE(${overall_resurrected}, ${overall_active_current})'}, {'name': 'fraction_of_active_new', 'type': 'number', 'sql': 'SAFE_DIVIDE(${new_users}, ${overall_active_current})'}, {'name': 'fraction_of_active_established_returning', 'type': 'number', 'sql': 'SAFE_DIVIDE(${established_users_returning},${overall_active_current})'}, {'name': 'fraction_of_active_new_returning', 'type': 'number', 'sql': 'SAFE_DIVIDE(${new_users_returning}, ${overall_active_current})'}, {'name': 'quick_ratio', 'type': 'number', 'sql': 'SAFE_DIVIDE(${new_users} + ${overall_resurrected},${established_users_churned_count} + ${new_users_churned_count})'}]
identifier_field
@classmethod
def get_default_dimensions(klass, identifier_field: str = 'client_id') -> List[Dict[str, str]]:
198    @classmethod
199    def get_default_dimensions(
200        klass, identifier_field: str = DEFAULT_IDENTIFIER_FIELD
201    ) -> List[Dict[str, str]]:
202        """Get dimensions to be added to GrowthAccountingView by default."""
203        return [
204            {
205                "name": "active_this_week",
206                "sql": "mozfun.bits28.active_in_range(days_seen_bits, -6, 7)",
207                "type": "yesno",
208                "hidden": "yes",
209            },
210            {
211                "name": "active_last_week",
212                "sql": "mozfun.bits28.active_in_range(days_seen_bits, -13, 7)",
213                "type": "yesno",
214                "hidden": "yes",
215            },
216            {
217                "name": "new_this_week",
218                "sql": "DATE_DIFF(${submission_date}, first_run_date, DAY) BETWEEN 0 AND 6",
219                "type": "yesno",
220                "hidden": "yes",
221            },
222            {
223                "name": "new_last_week",
224                "sql": "DATE_DIFF(${submission_date}, first_run_date, DAY) BETWEEN 7 AND 13",
225                "type": "yesno",
226                "hidden": "yes",
227            },
228            {
229                "name": f"{identifier_field}_day",
230                "sql": f"CONCAT(CAST(${{TABLE}}.submission_date AS STRING), ${{{identifier_field}}})",
231                "type": "string",
232                "hidden": "yes",
233                "primary_key": "yes",
234            },
235        ]

Get dimensions to be added to GrowthAccountingView by default.

@classmethod
def from_db_views( klass, namespace: str, is_glean: bool, channels: List[Dict[str, str]], db_views: dict, identifier_field: str = 'client_id') -> Iterator[GrowthAccountingView]:
237    @classmethod
238    def from_db_views(
239        klass,
240        namespace: str,
241        is_glean: bool,
242        channels: List[Dict[str, str]],
243        db_views: dict,
244        identifier_field: str = DEFAULT_IDENTIFIER_FIELD,
245    ) -> Iterator[GrowthAccountingView]:
246        """Get Growth Accounting Views from db views and app variants."""
247        dataset = next(
248            (channel for channel in channels if channel.get("channel") == "release"),
249            channels[0],
250        )["dataset"]
251
252        for view_id, references in db_views[dataset].items():
253            if view_id == "baseline_clients_last_seen":
254                yield GrowthAccountingView(
255                    namespace,
256                    [{"table": f"mozdata.{dataset}.{view_id}"}],
257                    identifier_field=identifier_field,
258                )

Get Growth Accounting Views from db views and app variants.

@classmethod
def from_dict( klass, namespace: str, name: str, _dict: generator.views.view.ViewDict) -> GrowthAccountingView:
260    @classmethod
261    def from_dict(
262        klass, namespace: str, name: str, _dict: ViewDict
263    ) -> GrowthAccountingView:
264        """Get a view from a name and dict definition."""
265        return GrowthAccountingView(
266            namespace,
267            _dict["tables"],
268            identifier_field=str(
269                _dict.get(
270                    "identifier_field", GrowthAccountingView.DEFAULT_IDENTIFIER_FIELD
271                )
272            ),
273        )

Get a view from a name and dict definition.

def to_lookml(self, v1_name: Optional[str], dryrun) -> Dict[str, Any]:
275    def to_lookml(self, v1_name: Optional[str], dryrun) -> Dict[str, Any]:
276        """Generate LookML for this view."""
277        view_defn: Dict[str, Any] = {"name": self.name}
278        table = self.tables[0]["table"]
279
280        # add dimensions and dimension groups
281        dimensions = lookml_utils._generate_dimensions(table, dryrun=dryrun) + deepcopy(
282            GrowthAccountingView.get_default_dimensions(
283                identifier_field=self.identifier_field
284            )
285        )
286
287        view_defn["dimensions"] = list(
288            filterfalse(lookml_utils._is_dimension_group, dimensions)
289        )
290        view_defn["dimension_groups"] = list(
291            filter(lookml_utils._is_dimension_group, dimensions)
292        )
293
294        # add measures
295        view_defn["measures"] = self.get_measures()
296
297        # SQL Table Name
298        view_defn["sql_table_name"] = f"`{table}`"
299
300        return {"views": [view_defn]}

Generate LookML for this view.

def get_measures(self) -> List[Dict[str, Union[str, List[Dict[str, str]]]]]:
302    def get_measures(self) -> List[Dict[str, Union[str, List[Dict[str, str]]]]]:
303        """Generate measures for the Growth Accounting Framework."""
304        return deepcopy(GrowthAccountingView.default_measures)

Generate measures for the Growth Accounting Framework.