Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions libs/garf_community/google/dv360/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# `garf` for Display & Video 360 API

[![PyPI](https://img.shields.io/pypi/v/garf-dv360-api?logo=pypi&logoColor=white&style=flat-square)](https://pypi.org/project/garf-dv360-api)
[![Downloads PyPI](https://img.shields.io/pypi/dw/garf-dv360-api?logo=pypi)](https://pypi.org/project/garf-dv360-api/)

`garf-dv360-api` simplifies fetching data from Merchant API using SQL-like queries.

## Prerequisites

* [Merchant API](https://console.cloud.google.com/apis/library/dv360api.googleapis.com) enabled.

## Installation

`pip install garf-dv360-api`

## Usage

### Run as a library
```
from garf_dv360_api import report_fetcher
from garf_io import writer


# Specify query
query = """
SELECT
date,
clicks
FROM product_performance_view
WHERE date BETWEEN '2023-12-01' AND '2023-12-03'
ORDER BY clicks DESC
""

# Fetch report
fetched_report = (
report_fetcher.DV360ApiReportFetcher()
.fetch(query, query="<YOUR_SEARCH_QUERY_HERE">, account=ACCOUNT_ID)
)

# Write report to console
console_writer = writer.create_writer('console')
console_writer.write(fetched_report, 'output')
```

### Run via CLI

> Install `garf-executors` package to run queries via CLI (`pip install garf-executors`).

```
garf <PATH_TO_QUERIES> --source dv360-api \
--output <OUTPUT_TYPE> \
--source.<SOURCE_PARAMETER=VALUE>
```

where:

* `<PATH_TO_QUERIES>` - local or remove files containing queries
* `<OUTPUT_TYPE>` - output supported by [`garf-io` library](../garf_io/README.md).
* `<SOURCE_PARAMETER=VALUE` - key-value pairs to refine fetching, check [available source parameters](#available-source-parameters).

## Available source parameters

| name | values| comments |
|----- | ----- | -------- |
| `account` | Account(s) to get data to | Multiple accounts are supported, should be comma-separated|
29 changes: 29 additions & 0 deletions libs/garf_community/google/dv360/garf_dv360_api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Library for fetching reports from Google Merchant API."""

from __future__ import annotations

from garf_merchant_api.api_clients import MerchantApiClient
from garf_merchant_api.report_fetcher import (
MerchantApiReportFetcher,
)

__all__ = [
'MerchantApiClient',
'MerchantApiReportFetcher',
]

__version__ = '0.0.1'
54 changes: 54 additions & 0 deletions libs/garf_community/google/dv360/garf_dv360_api/api_clients.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Creates API client for DV360 API."""

from google.shopping import dv360_reports_v1beta
from typing_extensions import override

from garf_core import api_clients, query_editor
from garf_dv360_api import exceptions


class DV360ApiError(exceptions.GarfDV360ApiError):
"""API specific error."""


class DV360ApiClient(api_clients.BaseClient):
def __init__(
self,
api_version: str = 'v4',
**kwargs: str,
) -> None:
"""Initializes DV360Client."""
self.api_version = api_version
self.query_args = kwargs

@override
def get_response(
self, request: query_editor.BaseQueryElements, **kwargs: str
) -> api_clients.GarfApiResponse:
if not (account := kwargs.get('account')):
raise DV360ApiError('Missing account parameter')
client = dv360_reports_v1beta.ReportServiceClient()
dv360_request = dv360_reports_v1beta.SearchRequest(
parent=f'accounts/{account}',
query=request.text,
)
response = client.search(request=dv360_request)
results = []
for page in response:
for rows in page.get('results'):
for _, row in rows.items():
results.append(row)
return api_clients.GarfApiResponse(results=results)
19 changes: 19 additions & 0 deletions libs/garf_community/google/dv360/garf_dv360_api/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from garf_core import exceptions


class GarfDV360ApiError(exceptions.GarfError):
"""Base class for all library exceptions."""
30 changes: 30 additions & 0 deletions libs/garf_community/google/dv360/garf_dv360_api/query_editor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Defines DV360Query."""

from garf_core import query_editor


class DV360ApiQuery(query_editor.QuerySpecification):
"""Query to Display & Video 360 API."""

def __init__(
self,
text: str,
title: str | None = None,
args: dict[str, str] | None = None,
**kwargs,
) -> None:
"""Initializes DV360ApiQuery."""
super().__init__(text, title, args, **kwargs)
32 changes: 32 additions & 0 deletions libs/garf_community/google/dv360/garf_dv360_api/report_fetcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Defines report fetcher."""

from garf_core import parsers, report_fetcher
from garf_dv360_api import DV360ApiClient, query_editor


class DV360ApiReportFetcher(report_fetcher.ApiReportFetcher):
"""Defines report fetcher."""

def __init__(
self,
api_client: DV360ApiClient = DV360ApiClient(),
parser: parsers.BaseParser = parsers.NumericConverterDictParser,
query_spec: query_editor.DV360ApiQuery = query_editor.DV360ApiQuery,
**kwargs: str,
) -> None:
"""Initializes DV360ApiReportFetcher."""
super().__init__(api_client, parser, query_spec, **kwargs)
37 changes: 37 additions & 0 deletions libs/garf_community/google/dv360/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "garf-dv360-api"
dependencies = [
"garf-core",
"garf-io",
"google-shopping-dv360-reports",
]
authors = [
{name = "Google Inc. (gTech gPS CSE team)", email = "[email protected]"},
]
license = {text = "Apache 2.0"}
requires-python = ">=3.8"
description = "Garf implementation for Display & Video 360 API"
readme = "README.md"
classifiers = [
"Development Status :: 3 - Alpha",
"Programming Language :: Python"
]

dynamic=["version"]

[tool.setuptools.dynamic]
version = {attr = "garf_dv360_api.__version__"}

[project.entry-points.garf]
dv360-api = "garf_dv360_api.report_fetcher"

[options.extras_require]
test = [
"pytest",
"pytest-cov",
"python-dotenv",
]
Loading