Skip to content

Commit 5b6217d

Browse files
[google-analytics] initial commit for garf-google-analytics
1 parent 1e5288b commit 5b6217d

File tree

6 files changed

+231
-0
lines changed

6 files changed

+231
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# `garf` for Google Analytics API
2+
3+
[![PyPI](https://img.shields.io/pypi/v/garf-google-analytics?logo=pypi&logoColor=white&style=flat-square)](https://pypi.org/project/garf-google-analytics)
4+
[![Downloads PyPI](https://img.shields.io/pypi/dw/garf-google-analytics?logo=pypi)](https://pypi.org/project/garf-google-analytics/)
5+
6+
`garf-google-analytics` simplifies fetching data from Google Analytics API using SQL-like queries.
7+
8+
## Prerequisites
9+
10+
* [Google Analytics API](https://console.cloud.google.com/apis/library/analytics.googleapis.com) enabled.
11+
12+
## Installation
13+
14+
`pip install garf-google-analytics`
15+
16+
## Usage
17+
18+
### Run as a library
19+
```
20+
import garf_google_analytics
21+
from garf_io import writer
22+
23+
# Fetch report
24+
fetched_report = (
25+
garf_google_analytics.GoogleAnalyticsApiReportFetcher()
26+
.fetch(query, query="<YOUR_QUERY_HERE">, property_id=PROPERTY_ID)
27+
)
28+
29+
# Write report to console
30+
console_writer = writer.create_writer('console')
31+
console_writer.write(fetched_report, 'output')
32+
```
33+
34+
### Run via CLI
35+
36+
> Install `garf-executors` package to run queries via CLI (`pip install garf-executors`).
37+
38+
```
39+
garf <PATH_TO_QUERIES> --source google-analytics \
40+
--output <OUTPUT_TYPE> \
41+
--source.<SOURCE_PARAMETER=VALUE>
42+
```
43+
44+
where:
45+
46+
* `<PATH_TO_QUERIES>` - local or remove files containing queries
47+
* `<OUTPUT_TYPE>` - output supported by [`garf-io` library](../garf_io/README.md).
48+
* `<SOURCE_PARAMETER=VALUE` - key-value pairs to refine fetching, check [available source parameters](#available-source-parameters).
49+
50+
## Available source parameters
51+
52+
| name | values| comments |
53+
|----- | ----- | -------- |
54+
| `property_id` | Property to get data to | Multiple properties are supported, should be comma-separated|
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from garf_google_analytics.api_clients import GoogleAnalyticsApiClient
16+
from garf_google_analytics.report_fetcher import GoogleAnalyticsApiReportFetcher
17+
18+
__all__ = [
19+
'GoogleAnalyticsApiClient',
20+
'GoogleAnalyticsApiReportFetcher',
21+
]
22+
23+
__version__ = '0.0.1'
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""Creates API client for Google Analytics API."""
15+
16+
from collections import defaultdict
17+
18+
from google.analytics.data_v1beta import BetaAnalyticsDataClient
19+
from google.analytics.data_v1beta.types import (
20+
DateRange,
21+
Dimension,
22+
Metric,
23+
RunReportRequest,
24+
)
25+
from typing_extensions import override
26+
27+
from garf_core import api_clients, query_editor
28+
29+
30+
class GoogleAnalyticsApiClient(api_clients.BaseClient):
31+
def __init__(self) -> None:
32+
"""Initializes GoogleAnalyticsApiClient."""
33+
self._client = None
34+
35+
@property
36+
def client(self):
37+
if self._client:
38+
return self._client_
39+
return BetaAnalyticsDataClient()
40+
41+
@override
42+
def get_response(
43+
self, request: query_editor.BaseQueryElements, **kwargs: str
44+
) -> api_clients.GarfApiResponse:
45+
property_id = kwargs.get('property_id')
46+
request = RunReportRequest(
47+
property=f'properties/{property_id}',
48+
dimensions=[Dimension(name='country')],
49+
metrics=[Metric(name='activeUsers')],
50+
date_ranges=[DateRange(start_date='2020-09-01', end_date='2025-09-15')],
51+
)
52+
response = self.client.run_report(request)
53+
results = []
54+
dimension_headers = [header.name for header in response.dimension_headers]
55+
metric_headers = [header.name for header in response.metric_headers]
56+
for row in response.rows:
57+
response_row: dict[str, dict[str, str]] = defaultdict(dict)
58+
for header, value in zip(row.dimension_values, dimension_headers):
59+
response_row[header] = value.value
60+
for header, value in zip(row.metric_values, metric_headers):
61+
response_row[header] = value.value
62+
results.append(response_row)
63+
return api_clients.GarfApiResponse(results=results)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""Defines Google Analytics API specific query parser."""
15+
16+
from garf_core import query_editor
17+
18+
19+
class GoogleAnalyticsApiQuery(query_editor.QuerySpecification):
20+
"""Query to Google Analytics API."""
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Defines report fetcher for Google Analytics API."""
16+
17+
from garf_core import parsers, report_fetcher
18+
from garf_google_analytics import GoogleAnalyticsApiClient, query_editor
19+
20+
21+
class GoogleAnalyticsApiReportFetcher(report_fetcher.ApiReportFetcher):
22+
"""Defines report fetcher."""
23+
24+
def __init__(
25+
self,
26+
api_client: GoogleAnalyticsApiClient = GoogleAnalyticsApiClient(),
27+
parser: parsers.DictParser = parsers.DictParser,
28+
query_spec: query_editor.GoogleAnalyticsApiQuery = (
29+
query_editor.GoogleAnalyticsApiQuery
30+
),
31+
**kwargs: str,
32+
) -> None:
33+
"""Initializes GoogleAnalyticsApiReportFetcher."""
34+
super().__init__(api_client, parser, query_spec, **kwargs)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[build-system]
2+
requires = ["setuptools >= 61.0"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "garf-google-analytics"
7+
dependencies = [
8+
"garf-core",
9+
"garf-io",
10+
"google-analytics-data",
11+
]
12+
authors = [
13+
{name = "Google Inc. (gTech gPS CSE team)", email = "no-reply@google.com"},
14+
]
15+
license = {text = "Apache 2.0"}
16+
requires-python = ">=3.8"
17+
description = "Garf implementation for Google Analytics API"
18+
readme = "README.md"
19+
classifiers = [
20+
"Development Status :: 2 - Pre-Alpha",
21+
"Programming Language :: Python"
22+
]
23+
24+
dynamic=["version"]
25+
26+
[tool.setuptools.dynamic]
27+
version = {attr = "garf_google_analytics.__version__"}
28+
29+
[project.entry-points.garf]
30+
google-analytics = "garf_google_analytics.report_fetcher"
31+
32+
[options.extras_require]
33+
test = [
34+
"pytest",
35+
"pytest-cov",
36+
"python-dotenv",
37+
]

0 commit comments

Comments
 (0)