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)
class ClientCountsView(generator.views.view.View):
 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.

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': "\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.