digital-analytics-toolkit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- digital_analytics_toolkit/__init__.py +81 -0
- digital_analytics_toolkit/bq_functions.py +97 -0
- digital_analytics_toolkit/free_form_report_GA4.py +155 -0
- digital_analytics_toolkit/funnel_report_GA4.py +269 -0
- digital_analytics_toolkit/ga4_admin_functions.py +176 -0
- digital_analytics_toolkit/ga4_audience.py +576 -0
- digital_analytics_toolkit/gtm_functions.py +468 -0
- digital_analytics_toolkit/utils.py +94 -0
- digital_analytics_toolkit-0.1.0.dist-info/METADATA +292 -0
- digital_analytics_toolkit-0.1.0.dist-info/RECORD +13 -0
- digital_analytics_toolkit-0.1.0.dist-info/WHEEL +5 -0
- digital_analytics_toolkit-0.1.0.dist-info/licenses/LICENSE +55 -0
- digital_analytics_toolkit-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from . import (
|
|
2
|
+
bq_functions,
|
|
3
|
+
free_form_report_GA4,
|
|
4
|
+
funnel_report_GA4,
|
|
5
|
+
ga4_admin_functions,
|
|
6
|
+
ga4_audience,
|
|
7
|
+
gtm_functions,
|
|
8
|
+
utils,
|
|
9
|
+
)
|
|
10
|
+
from .bq_functions import (
|
|
11
|
+
get_from_bigquery,
|
|
12
|
+
get_ga4_data_from_bq,
|
|
13
|
+
list_bq_datasets,
|
|
14
|
+
)
|
|
15
|
+
from .free_form_report_GA4 import dimension_filter_generation, free_form_report_GA4
|
|
16
|
+
from .funnel_report_GA4 import Funnel_Reports
|
|
17
|
+
from .ga4_admin_functions import (
|
|
18
|
+
get_ga4_accounts_list,
|
|
19
|
+
get_ga4_properties_list,
|
|
20
|
+
get_ga4_property_bq_link,
|
|
21
|
+
get_ga4_property_keyEvents,
|
|
22
|
+
)
|
|
23
|
+
from .ga4_audience import (
|
|
24
|
+
GA4AudienceClient,
|
|
25
|
+
GA4AudienceError,
|
|
26
|
+
build_admin_client,
|
|
27
|
+
create_audience,
|
|
28
|
+
)
|
|
29
|
+
from .gtm_functions import (
|
|
30
|
+
get_accounts,
|
|
31
|
+
get_built_in_variables,
|
|
32
|
+
get_containers,
|
|
33
|
+
get_custom_templates,
|
|
34
|
+
get_tags,
|
|
35
|
+
get_triggers,
|
|
36
|
+
get_variables,
|
|
37
|
+
get_workspaces,
|
|
38
|
+
getLatestVersionHeader,
|
|
39
|
+
)
|
|
40
|
+
from .utils import get_dimension_metric
|
|
41
|
+
|
|
42
|
+
__version__ = "0.1.0"
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
# Submodules
|
|
46
|
+
"bq_functions",
|
|
47
|
+
"ga4_admin_functions",
|
|
48
|
+
"ga4_audience",
|
|
49
|
+
"gtm_functions",
|
|
50
|
+
"utils",
|
|
51
|
+
# Reporting
|
|
52
|
+
"Funnel_Reports",
|
|
53
|
+
"free_form_report_GA4",
|
|
54
|
+
"dimension_filter_generation",
|
|
55
|
+
# GA4 Admin
|
|
56
|
+
"get_ga4_accounts_list",
|
|
57
|
+
"get_ga4_properties_list",
|
|
58
|
+
"get_ga4_property_keyEvents",
|
|
59
|
+
"get_ga4_property_bq_link",
|
|
60
|
+
# Audiences
|
|
61
|
+
"GA4AudienceClient",
|
|
62
|
+
"create_audience",
|
|
63
|
+
"build_admin_client",
|
|
64
|
+
"GA4AudienceError",
|
|
65
|
+
# GTM
|
|
66
|
+
"get_accounts",
|
|
67
|
+
"get_containers",
|
|
68
|
+
"get_workspaces",
|
|
69
|
+
"getLatestVersionHeader",
|
|
70
|
+
"get_tags",
|
|
71
|
+
"get_variables",
|
|
72
|
+
"get_triggers",
|
|
73
|
+
"get_built_in_variables",
|
|
74
|
+
"get_custom_templates",
|
|
75
|
+
# BigQuery
|
|
76
|
+
"list_bq_datasets",
|
|
77
|
+
"get_from_bigquery",
|
|
78
|
+
"get_ga4_data_from_bq",
|
|
79
|
+
# Utilities
|
|
80
|
+
"get_dimension_metric",
|
|
81
|
+
]
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from google.cloud import bigquery
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from google.oauth2 import service_account
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def list_bq_datasets(credentials, gcp_project_id):
|
|
8
|
+
"""
|
|
9
|
+
List all BigQuery datasets in a given Google Cloud project.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
credentials: Authenticated Google credentials object used to initialize
|
|
13
|
+
the BigQuery client.
|
|
14
|
+
gcp_project_id (str): The Google Cloud project ID.
|
|
15
|
+
"""
|
|
16
|
+
client = bigquery.Client(project=gcp_project_id, credentials=credentials)
|
|
17
|
+
dataset_objects = client.list_datasets(project=str(gcp_project_id))
|
|
18
|
+
|
|
19
|
+
# Extract just the string IDs from the objects
|
|
20
|
+
datasets = [dataset.dataset_id for dataset in dataset_objects]
|
|
21
|
+
|
|
22
|
+
return datasets
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_from_bigquery(project_id:str,dataset_id:str,table_id:str,service_account_file:Optional[str] = None, OAuth_creds:Optional[str] = None) -> pd.DataFrame:
|
|
26
|
+
'''
|
|
27
|
+
Args:
|
|
28
|
+
--------
|
|
29
|
+
project_id: str
|
|
30
|
+
Google Cloud Project ID where the BigQuery table is located.
|
|
31
|
+
|
|
32
|
+
dataset_id: str
|
|
33
|
+
BigQuery dataset ID where the table is located.
|
|
34
|
+
|
|
35
|
+
table_id: str
|
|
36
|
+
BigQuery table ID from where the data needs to be extracted.
|
|
37
|
+
service_account_file: str
|
|
38
|
+
API service account JSON key file.
|
|
39
|
+
|
|
40
|
+
--------
|
|
41
|
+
Returns:
|
|
42
|
+
pd.DataFrame
|
|
43
|
+
'''
|
|
44
|
+
if service_account_file != None:
|
|
45
|
+
credentials = service_account.Credentials.from_service_account_file(
|
|
46
|
+
service_account_file,
|
|
47
|
+
scopes=["https://www.googleapis.com/auth/cloud-platform"]
|
|
48
|
+
)
|
|
49
|
+
elif OAuth_creds != None:
|
|
50
|
+
credentials = OAuth_creds
|
|
51
|
+
client = bigquery.Client(credentials=credentials, project=project_id)
|
|
52
|
+
query = f"SELECT * FROM `{project_id}.{dataset_id}.{table_id}`"
|
|
53
|
+
query_job = client.query(query)
|
|
54
|
+
results = query_job.result().to_dataframe()
|
|
55
|
+
return results
|
|
56
|
+
|
|
57
|
+
def get_ga4_data_from_bq(project_id: str,query: str, service_account_file:Optional[str] = None, OAuth_creds:Optional[str] = None) -> pd.DataFrame:
|
|
58
|
+
'''
|
|
59
|
+
Args:
|
|
60
|
+
--------
|
|
61
|
+
project_id: str
|
|
62
|
+
Google Cloud Project ID where the BigQuery table is located.
|
|
63
|
+
query: str
|
|
64
|
+
SQL query to execute.
|
|
65
|
+
service_account: str
|
|
66
|
+
Path to service account json file
|
|
67
|
+
OAuth_creds:
|
|
68
|
+
OAuth creds
|
|
69
|
+
--------
|
|
70
|
+
Returns:
|
|
71
|
+
pd.DataFrame
|
|
72
|
+
'''
|
|
73
|
+
if service_account_file != None:
|
|
74
|
+
credentials = service_account.Credentials.from_service_account_file(
|
|
75
|
+
service_account_file,
|
|
76
|
+
scopes=["https://www.googleapis.com/auth/cloud-platform"]
|
|
77
|
+
)
|
|
78
|
+
elif OAuth_creds != None:
|
|
79
|
+
credentials = OAuth_creds
|
|
80
|
+
client = bigquery.Client(credentials=credentials, project=project_id)
|
|
81
|
+
query_job = client.query(query)
|
|
82
|
+
results = query_job.result().to_dataframe()
|
|
83
|
+
return results
|
|
84
|
+
|
|
85
|
+
# def create_bq_table(project_id:str,dataset_id:str,table_id:str,schema_dict:dict,service_account_file:Optional[str] = None, OAuth_creds:Optional[str] = None) -> pd.DataFrame:
|
|
86
|
+
# table_path = f"{project_id}.{dataset_id}.{table_id}"
|
|
87
|
+
# schema = []
|
|
88
|
+
# for key, value in schema_dict.items():
|
|
89
|
+
# if type(value) == dict:
|
|
90
|
+
# temp_list = []
|
|
91
|
+
# for nested_key, nested_value in value.items():
|
|
92
|
+
# temp_list.append(bigquery.SchemaField(nested_key,nested_value))
|
|
93
|
+
# schema.append(bigquery.SchemaField(key, "RECORD",fields=temp_list))
|
|
94
|
+
# else:
|
|
95
|
+
# schema.append(bigquery.SchemaField(key, value))
|
|
96
|
+
# table = bigquery.Table(table_path, schema=schema)
|
|
97
|
+
# def write_to_bq(project_id:str,dataset_id:str,table_id:str,service_account_file:Optional[str] = None, OAuth_creds:Optional[str] = None) -> pd.DataFrame:
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from google.oauth2 import service_account
|
|
2
|
+
from google.analytics.data_v1beta import BetaAnalyticsDataClient
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
|
|
7
|
+
class free_form_report_GA4():
|
|
8
|
+
'''
|
|
9
|
+
Generate Free Form GA4 reports.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
service_account_file (str): API service account JSON key file.
|
|
13
|
+
property_id (str): Property ID of the Google Analytics Property from which report needs to be generated.
|
|
14
|
+
'''
|
|
15
|
+
def __init__(self, service_account_file: str, property_id: str):
|
|
16
|
+
self.service_account_file = service_account_file
|
|
17
|
+
self.property_id = property_id
|
|
18
|
+
self.credentials = service_account.Credentials.from_service_account_file(self.service_account_file)
|
|
19
|
+
self.client = BetaAnalyticsDataClient(credentials=self.credentials)
|
|
20
|
+
self.property_id = f'properties/{property_id}'
|
|
21
|
+
|
|
22
|
+
def free_form_report(self, start_date, end_date, metrics: list, dimensions:Optional[list] = None, dimension_filter:Optional[str] = None, metric_filter:Optional[str] = None):
|
|
23
|
+
'''
|
|
24
|
+
Collects date ranges, metrics and dimensions to generate report.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
start_date (str):
|
|
28
|
+
Report date range starting date in "YYYY-MM-DD" format or "YYYY-MM-DD HH:MM:SS" format or GA4 string format like "7daysAgo", "today" etc.
|
|
29
|
+
end_date (str):
|
|
30
|
+
Report date range ending date in "YYYY-MM-DD" format or "YYYY-MM-DD HH:MM:SS" format or GA4 string format like "7daysAgo", "today" etc.
|
|
31
|
+
metrics (list):
|
|
32
|
+
List of metrics required in the report pass only the api names.
|
|
33
|
+
example: ['totalUsers', 'eventCount']
|
|
34
|
+
dimensions (list, Optional):
|
|
35
|
+
List of dimensional breakdown required in the report pass only the api names.
|
|
36
|
+
example: ['browser','sessionDefaultChannelGroup']
|
|
37
|
+
dimension_filter (str, Optional):
|
|
38
|
+
Dimension Filter condition required in the report in JSON format
|
|
39
|
+
metric_filter (str, Optional):
|
|
40
|
+
Metrics Filter condition required in the report in JSON format
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
result_df (DataFrame): Report generated in pandas DataFrame format.
|
|
44
|
+
'''
|
|
45
|
+
self.dimensions = []
|
|
46
|
+
self.metrics = []
|
|
47
|
+
if "-" in str(start_date):
|
|
48
|
+
self.start_date = str(start_date).split(" ")[0]
|
|
49
|
+
else:
|
|
50
|
+
self.start_date = str(start_date)
|
|
51
|
+
if "-" in str(end_date):
|
|
52
|
+
self.end_date = str(end_date).split(" ")[0]
|
|
53
|
+
else:
|
|
54
|
+
self.end_date = str(end_date)
|
|
55
|
+
try:
|
|
56
|
+
for dim in dimensions:
|
|
57
|
+
self.dimensions.append({'name' : dim})
|
|
58
|
+
except TypeError as e:
|
|
59
|
+
print("dimensions received is not in list format")
|
|
60
|
+
try:
|
|
61
|
+
for met in metrics:
|
|
62
|
+
self.metrics.append({'name' : met})
|
|
63
|
+
except TypeError as e:
|
|
64
|
+
print("metrics received is not in list format")
|
|
65
|
+
self.request = {
|
|
66
|
+
"property": self.property_id,
|
|
67
|
+
"date_ranges" : [
|
|
68
|
+
{
|
|
69
|
+
"start_date" : self.start_date,
|
|
70
|
+
"end_date" : self.end_date,
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"dimensions" : self.dimensions,
|
|
74
|
+
"metrics" : self.metrics,
|
|
75
|
+
"dimension_filter" : dimension_filter,
|
|
76
|
+
"metric_filter" : metric_filter,
|
|
77
|
+
"limit" : "250000"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
self.response = self.client.run_report(request=self.request)
|
|
82
|
+
except Exception as e:
|
|
83
|
+
print(e)
|
|
84
|
+
return None
|
|
85
|
+
self.data = []
|
|
86
|
+
for row in self.response.rows:
|
|
87
|
+
dimension_values = [value.value for value in row.dimension_values]
|
|
88
|
+
metric_values = [float(value.value) for value in row.metric_values]
|
|
89
|
+
self.data.append(dimension_values + metric_values)
|
|
90
|
+
self.columns = [dimension.name for dimension in self.response.dimension_headers] + [metric.name for metric in self.response.metric_headers]
|
|
91
|
+
self.result_df = pd.DataFrame(data=self.data, columns=self.columns)
|
|
92
|
+
|
|
93
|
+
return self.result_df
|
|
94
|
+
|
|
95
|
+
def dimension_filter_generation(operator: str, value: Union[str, list], dimension_name: Optional[str] = None):
|
|
96
|
+
'''
|
|
97
|
+
Generates dimension filter in JSON format to be used in report generation.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
operator (str): Operator for the filter condition. available options: "EXACT", "BEGINS_WITH", "ENDS_WITH", "CONTAINS", "FULL_REGEXP", "AND", "OR", "NOT", "IN_LIST".
|
|
101
|
+
value (Union[str, list]): Value for the filter condition. For operators "AND", "OR" and "NOT", pass the value in list format with each condition as a separate item in the list.
|
|
102
|
+
dimension_name (str): Name of the dimension for which filter needs to be generated. Required only when operator is not "AND", "OR" or "NOT".
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
dimension_filter (str): Generated dimension filter in JSON format.
|
|
106
|
+
'''
|
|
107
|
+
secondary_opeartor = {
|
|
108
|
+
"AND" : "and_group",
|
|
109
|
+
"OR" : "or_group",
|
|
110
|
+
"NOT" : "not_expression"
|
|
111
|
+
}
|
|
112
|
+
if operator in ["AND", "OR"]:
|
|
113
|
+
# filter_expression = value.split(",")
|
|
114
|
+
# filter_expression = [item.strip() for item in filter_expression]
|
|
115
|
+
filter_expression = value
|
|
116
|
+
dimension_filter = {
|
|
117
|
+
secondary_opeartor[operator] : {
|
|
118
|
+
"expressions" : filter_expression
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
elif operator in ["NOT"]:
|
|
122
|
+
# filter_expression = value.split(",")
|
|
123
|
+
# filter_expression = [item.strip() for item in filter_expression]
|
|
124
|
+
filter_expression = value
|
|
125
|
+
dimension_filter = {
|
|
126
|
+
secondary_opeartor[operator] : filter_expression
|
|
127
|
+
}
|
|
128
|
+
elif operator == "IN_LIST":
|
|
129
|
+
dimension_filter = {
|
|
130
|
+
"filter": {
|
|
131
|
+
"field_name": dimension_name,
|
|
132
|
+
"in_list_filter": {
|
|
133
|
+
"values": value
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else:
|
|
138
|
+
dimension_filter = {
|
|
139
|
+
"filter": {
|
|
140
|
+
"field_name": dimension_name,
|
|
141
|
+
"string_filter": {
|
|
142
|
+
"match_type": operator,
|
|
143
|
+
"value": value
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return dimension_filter
|
|
148
|
+
# def anomaly_detection(self, report, comparison_report, report_date_range, comparison_date_range, dimensions_list):
|
|
149
|
+
# self.data = report
|
|
150
|
+
# self.comp_data = comparison_report
|
|
151
|
+
# self.dimensions_list = dimensions_list
|
|
152
|
+
# self.data_start_date, self.data_end_date = [str(date).split(" ")[0] for date in report_date_range]
|
|
153
|
+
# self.comp_start_date, self.comp_end_date = [str(date).split(" ")[0] for date in comparison_date_range]
|
|
154
|
+
|
|
155
|
+
# merged_df = pd.merge(self.data, self.comp_data, on=self.dimensions_list, how="outer")
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import gspread
|
|
2
|
+
from google.oauth2 import service_account
|
|
3
|
+
from google.analytics.data_v1alpha import AlphaAnalyticsDataClient
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from google.analytics.data_v1alpha.types import (
|
|
7
|
+
DateRange,
|
|
8
|
+
Dimension,
|
|
9
|
+
Funnel,
|
|
10
|
+
FunnelBreakdown,
|
|
11
|
+
FunnelEventFilter,
|
|
12
|
+
FunnelFieldFilter,
|
|
13
|
+
FunnelFilterExpression,
|
|
14
|
+
FunnelFilterExpressionList,
|
|
15
|
+
FunnelStep,
|
|
16
|
+
RunFunnelReportRequest,
|
|
17
|
+
StringFilter,
|
|
18
|
+
Filter,
|
|
19
|
+
FilterExpressionList,
|
|
20
|
+
FilterExpression,
|
|
21
|
+
QuotaStatus,
|
|
22
|
+
InListFilter,
|
|
23
|
+
)
|
|
24
|
+
import pandas as pd
|
|
25
|
+
|
|
26
|
+
class Funnel_Reports():
|
|
27
|
+
|
|
28
|
+
'''
|
|
29
|
+
|
|
30
|
+
Initializing object to run funnel reports.
|
|
31
|
+
|
|
32
|
+
Parameters:
|
|
33
|
+
-----------
|
|
34
|
+
reporting_client : str
|
|
35
|
+
Service Account JSON key for Google Analytics Data API
|
|
36
|
+
prop_id : str
|
|
37
|
+
Property ID of the Google Analytics Property from which report needs to be generated.
|
|
38
|
+
|
|
39
|
+
Funnel functions covered:
|
|
40
|
+
-----------
|
|
41
|
+
1. OR Group
|
|
42
|
+
2. AND Group
|
|
43
|
+
3. Contains
|
|
44
|
+
4. Event Name
|
|
45
|
+
5. Equals To
|
|
46
|
+
6. Is One of
|
|
47
|
+
7. Begins with
|
|
48
|
+
8. Ends with
|
|
49
|
+
9. Matched Regex
|
|
50
|
+
10. Does not in combition with all the other mentioned functions
|
|
51
|
+
'''
|
|
52
|
+
|
|
53
|
+
def __init__(self,reporting_client: str,prop_id: str):
|
|
54
|
+
self.property_id = prop_id
|
|
55
|
+
self.SCOPES = [
|
|
56
|
+
'https://www.googleapis.com/auth/analytics.readonly',
|
|
57
|
+
'https://www.googleapis.com/auth/spreadsheets',
|
|
58
|
+
'https://www.googleapis.com/auth/drive'
|
|
59
|
+
]
|
|
60
|
+
# self.file_n = file_n
|
|
61
|
+
self.credentials = service_account.Credentials.from_service_account_file(reporting_client, scopes=self.SCOPES)
|
|
62
|
+
self.client = AlphaAnalyticsDataClient(credentials = self.credentials)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def condition_creator(self,condition_string):
|
|
66
|
+
if ' or ' in condition_string.lower():
|
|
67
|
+
conditions_li = condition_string.lower().split(' or ')
|
|
68
|
+
expressions = []
|
|
69
|
+
funnel_filter_expression = FunnelFilterExpression(
|
|
70
|
+
or_group = FunnelFilterExpressionList(
|
|
71
|
+
expressions = [self.condition_creator(condition) for condition in conditions_li]
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
elif ' and ' in condition_string.lower():
|
|
75
|
+
conditions_li = condition_string.split(' and ')
|
|
76
|
+
expressions = []
|
|
77
|
+
funnel_filter_expression = FunnelFilterExpression(
|
|
78
|
+
and_group = FunnelFilterExpressionList(
|
|
79
|
+
expressions = [self.condition_creator(condition) for condition in conditions_li]
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
elif ' does not ' in condition_string.lower():
|
|
83
|
+
new_condition = condition_string.split(' does not ')[0]+ ' '+condition_string.split(' does not ')[1]
|
|
84
|
+
funnel_filter_expression = FunnelFilterExpression(
|
|
85
|
+
not_expression = FunnelFilterExpression(
|
|
86
|
+
self.condition_creator(new_condition)
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
else:
|
|
90
|
+
if '= ' in condition_string and 'eventName' in condition_string:
|
|
91
|
+
event_name = condition_string.split('"')[1]
|
|
92
|
+
funnel_event_filter = FunnelEventFilter(event_name=event_name)
|
|
93
|
+
funnel_filter_expression = FunnelFilterExpression(funnel_event_filter=funnel_event_filter)
|
|
94
|
+
elif 'contains' in condition_string:
|
|
95
|
+
field_name = condition_string.split(' contains ')[0]
|
|
96
|
+
event_name = condition_string.split('"')[1]
|
|
97
|
+
string_filter=StringFilter(match_type=StringFilter.MatchType.CONTAINS,case_sensitive=False,value=event_name)
|
|
98
|
+
funnel_field_filter = FunnelFieldFilter(field_name= field_name, string_filter=string_filter)
|
|
99
|
+
funnel_filter_expression = FunnelFilterExpression(funnel_field_filter=funnel_field_filter)
|
|
100
|
+
|
|
101
|
+
elif 'is one of' in condition_string:
|
|
102
|
+
field_name, event_name = condition_string.split(' is one of ')[0], condition_string.split(' is one of ')[1]
|
|
103
|
+
test_str = event_name.replace('"','').replace('[','').replace(']','').replace('\n','')
|
|
104
|
+
new_list = test_str.split(', ')
|
|
105
|
+
# print(event_name)
|
|
106
|
+
in_list_filter=InListFilter(values=new_list)
|
|
107
|
+
|
|
108
|
+
funnel_field_filter = FunnelFieldFilter(field_name= field_name, in_list_filter=in_list_filter)
|
|
109
|
+
funnel_filter_expression = FunnelFilterExpression(funnel_field_filter=funnel_field_filter)
|
|
110
|
+
|
|
111
|
+
elif '=' in condition_string:
|
|
112
|
+
field_name = condition_string.split(' = ')[0]
|
|
113
|
+
event_name = condition_string.split('"')[1]
|
|
114
|
+
string_filter=StringFilter(match_type=StringFilter.MatchType.EXACT,case_sensitive=False,value=event_name)
|
|
115
|
+
funnel_field_filter = FunnelFieldFilter(field_name= field_name, string_filter=string_filter)
|
|
116
|
+
funnel_filter_expression = FunnelFilterExpression(funnel_field_filter=funnel_field_filter)
|
|
117
|
+
elif 'begins with' in condition_string:
|
|
118
|
+
field_name = condition_string.split(' begins with ')[0]
|
|
119
|
+
event_name = condition_string.split('"')[1]
|
|
120
|
+
string_filter=StringFilter(match_type=StringFilter.MatchType.BEGINS_WITH,case_sensitive=False,value=event_name)
|
|
121
|
+
funnel_field_filter = FunnelFieldFilter(field_name= field_name, string_filter=string_filter)
|
|
122
|
+
funnel_filter_expression = FunnelFilterExpression(funnel_field_filter=funnel_field_filter)
|
|
123
|
+
elif 'ends with' in condition_string:
|
|
124
|
+
field_name = condition_string.split(' ends with ')[0]
|
|
125
|
+
event_name = condition_string.split('"')[1]
|
|
126
|
+
string_filter=StringFilter(match_type=StringFilter.MatchType.ENDS_WITH,case_sensitive=False,value=event_name)
|
|
127
|
+
funnel_field_filter = FunnelFieldFilter(field_name= field_name, string_filter=string_filter)
|
|
128
|
+
funnel_filter_expression = FunnelFilterExpression(funnel_field_filter=funnel_field_filter)
|
|
129
|
+
elif 'matches regex' in condition_string:
|
|
130
|
+
field_name = condition_string.split(' matches regex ')[0]
|
|
131
|
+
field_name = field_name.strip()
|
|
132
|
+
event_name = condition_string.split('"')[1]
|
|
133
|
+
string_filter=StringFilter(match_type=StringFilter.MatchType.FULL_REGEXP,case_sensitive=False,value=event_name)
|
|
134
|
+
funnel_field_filter = FunnelFieldFilter(field_name= field_name, string_filter=string_filter)
|
|
135
|
+
funnel_filter_expression = FunnelFilterExpression(funnel_field_filter=funnel_field_filter)
|
|
136
|
+
return funnel_filter_expression
|
|
137
|
+
|
|
138
|
+
def generate_dimension_filter(self, dim_filter):
|
|
139
|
+
'''
|
|
140
|
+
Pass filter in string format
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
---
|
|
144
|
+
1. sessionDefaultChannelGroup contains "Organic"
|
|
145
|
+
2. sessionDefaultChannelGroup = "Direct"
|
|
146
|
+
'''
|
|
147
|
+
if 'contains' in dim_filter:
|
|
148
|
+
filter_field_name = dim_filter.split(' contains ')[0]
|
|
149
|
+
filter_event_name = dim_filter.split('"')[1]
|
|
150
|
+
str_filter = StringFilter(match_type=StringFilter.MatchType.CONTAINS, case_sensitive=False, value=filter_event_name)
|
|
151
|
+
filter_condition = Filter(field_name=filter_field_name, string_filter=str_filter)
|
|
152
|
+
dimension_filter = FilterExpression(filter=filter_condition)
|
|
153
|
+
elif ' = ' in dim_filter:
|
|
154
|
+
filter_field_name = dim_filter.split(' contains ')[0]
|
|
155
|
+
filter_event_name = dim_filter.split('"')[1]
|
|
156
|
+
str_filter = StringFilter(match_type=StringFilter.MatchType.EXACT, value=filter_event_name)
|
|
157
|
+
filter_condition = Filter(field_name=filter_field_name, string_filter=str_filter)
|
|
158
|
+
dimension_filter = FilterExpression(filter=filter_condition)
|
|
159
|
+
return dimension_filter
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def get_funnel_steps(self):
|
|
163
|
+
self.steps = len(self.funnel_data.keys())
|
|
164
|
+
funnel_steps = []
|
|
165
|
+
for i in range(1,self.steps):
|
|
166
|
+
if self.funnel_data.iloc[0,i] == 'Single Condition':
|
|
167
|
+
fs = FunnelStep(
|
|
168
|
+
name = self.funnel_data.keys()[i],
|
|
169
|
+
filter_expression = FunnelFilterExpression(self.condition_creator(self.funnel_data.iloc[1,i]))
|
|
170
|
+
)
|
|
171
|
+
funnel_steps.append(fs)
|
|
172
|
+
elif self.funnel_data.iloc[0,i] == 'AND Group':
|
|
173
|
+
fs = FunnelStep(
|
|
174
|
+
name = self.funnel_data.keys()[i],
|
|
175
|
+
filter_expression = FunnelFilterExpression(
|
|
176
|
+
and_group = FunnelFilterExpressionList(
|
|
177
|
+
expressions = [self.condition_creator(self.funnel_data.iloc[j,i]) for j in range(1,len(self.funnel_data))]
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
funnel_steps.append(fs)
|
|
182
|
+
elif self.funnel_data.iloc[0,i] == 'OR Group':
|
|
183
|
+
fs = FunnelStep(
|
|
184
|
+
name = self.funnel_data.keys()[i],
|
|
185
|
+
filter_expression = FunnelFilterExpression(
|
|
186
|
+
or_group = FunnelFilterExpressionList(
|
|
187
|
+
expressions = [self.condition_creator(self.funnel_data.iloc[j,i]) for j in range(1,len(self.funnel_data))]
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
funnel_steps.append(fs)
|
|
192
|
+
return funnel_steps
|
|
193
|
+
|
|
194
|
+
def funnel_report(self,startDate: datetime,endDate: datetime,file_n: str,dimension_filter:Optional[str] = None, breakdown:Optional[str] = None,breakdown_limit:Optional[str] = None):
|
|
195
|
+
'''
|
|
196
|
+
Generate funnel report between two dates.
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
startDate : datetime
|
|
201
|
+
Start date of the report.
|
|
202
|
+
endDate : datetime
|
|
203
|
+
End date of the report.
|
|
204
|
+
file_n : str
|
|
205
|
+
Google Sheets File containing Steps of the funnel.
|
|
206
|
+
dimension_filter : str, optional
|
|
207
|
+
Filter condition for the report (must be a string if provided).
|
|
208
|
+
breakdown : str, optional
|
|
209
|
+
Breakdown dimension to view funnels across dimension.
|
|
210
|
+
breakdown_limit : str, optional
|
|
211
|
+
Available limit 1 - 15.
|
|
212
|
+
Need to specify the number of breakdown rows to be displayed per step.
|
|
213
|
+
|
|
214
|
+
Default limit = 15
|
|
215
|
+
'''
|
|
216
|
+
self.SCOPES = [
|
|
217
|
+
'https://www.googleapis.com/auth/analytics.readonly',
|
|
218
|
+
'https://www.googleapis.com/auth/spreadsheets',
|
|
219
|
+
'https://www.googleapis.com/auth/drive'
|
|
220
|
+
]
|
|
221
|
+
self.breakdown = breakdown
|
|
222
|
+
if breakdown_limit == None:
|
|
223
|
+
self.breakdown_limit = '15'
|
|
224
|
+
else:
|
|
225
|
+
self.breakdown_limit = breakdown_limit
|
|
226
|
+
self.dimension_filter = dimension_filter
|
|
227
|
+
self.file_n = file_n
|
|
228
|
+
self.gc = gspread.authorize(self.credentials)
|
|
229
|
+
self.worksheet = self.gc.open(f'{self.file_n}').sheet1
|
|
230
|
+
self.rows = self.worksheet.get_all_values()
|
|
231
|
+
self.funnel_data = pd.DataFrame(self.rows[1:], columns=self.rows[0])
|
|
232
|
+
if dimension_filter == None:
|
|
233
|
+
if self.breakdown == None:
|
|
234
|
+
request = RunFunnelReportRequest(
|
|
235
|
+
property=f"properties/{self.property_id}",
|
|
236
|
+
date_ranges=[DateRange(start_date=str(startDate).split(" ")[0], end_date=str(endDate).split(" ")[0])],
|
|
237
|
+
funnel=Funnel(steps = self.get_funnel_steps()),
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
request = RunFunnelReportRequest(
|
|
241
|
+
property=f"properties/{self.property_id}",
|
|
242
|
+
date_ranges=[DateRange(start_date=str(startDate).split(" ")[0], end_date=str(endDate).split(" ")[0])],
|
|
243
|
+
funnel_breakdown = FunnelBreakdown(
|
|
244
|
+
breakdown_dimension = Dimension(name=self.breakdown),
|
|
245
|
+
limit = self.breakdown_limit
|
|
246
|
+
),
|
|
247
|
+
funnel=Funnel(steps = self.get_funnel_steps()),
|
|
248
|
+
)
|
|
249
|
+
else:
|
|
250
|
+
if self.breakdown == None:
|
|
251
|
+
request = RunFunnelReportRequest(
|
|
252
|
+
property=f"properties/{self.property_id}",
|
|
253
|
+
date_ranges=[DateRange(start_date=str(startDate).split(" ")[0], end_date=str(endDate).split(" ")[0])],
|
|
254
|
+
funnel=Funnel(steps = self.get_funnel_steps()),
|
|
255
|
+
dimension_filter = self.generate_dimension_filter(self.dimension_filter)
|
|
256
|
+
)
|
|
257
|
+
else:
|
|
258
|
+
request = RunFunnelReportRequest(
|
|
259
|
+
property=f"properties/{self.property_id}",
|
|
260
|
+
date_ranges=[DateRange(start_date=str(startDate).split(" ")[0], end_date=str(endDate).split(" ")[0])],
|
|
261
|
+
funnel_breakdown = FunnelBreakdown(
|
|
262
|
+
breakdown_dimension = Dimension(name=self.breakdown),
|
|
263
|
+
limit = self.breakdown_limit
|
|
264
|
+
),
|
|
265
|
+
funnel=Funnel(steps = self.get_funnel_steps()),
|
|
266
|
+
dimension_filter = self.generate_dimension_filter(self.dimension_filter)
|
|
267
|
+
)
|
|
268
|
+
response = self.client.run_funnel_report(request)
|
|
269
|
+
return response
|