├── .gitignore ├── package.json ├── LICENSE.txt ├── get-accounts.sql ├── bq_queries ├── 20-placements_view.sql ├── 14-poor_assets_summary.sql ├── 06-assetssnapshots.sql ├── 13-campaign_scores_union.sql ├── 09-bpscore.sql ├── 05-video_assets.sql ├── 19-assetgroupperformance.sql ├── 08-assetsummary.sql ├── 03-primary_conversion_action_search.sql ├── 02-primary_conversion_action_pmax.sql ├── 04-text_assets.sql ├── 12-campaign_settings.sql ├── 01-image_assets.sql ├── 11-assetgroupbestpracticessnapshots.sql ├── 10-assetgroupbestpractices.sql └── 07-campaign_data.sql ├── google_ads_queries ├── customer.sql ├── conversions.sql ├── ad_group_asset.sql ├── ocid_mapping.sql ├── custom_goal_names.sql ├── assetgroupsignal.sql ├── recommendations.sql ├── conversion_category.sql ├── customerasset.sql ├── campaignasset.sql ├── conversion_custom.sql ├── conversion_split.sql ├── pmax_placement_view.sql ├── tcpa_search.sql ├── assetgroupsummary.sql ├── assetgroupasset.sql ├── campaignconversionaction.sql └── campaign_settings.sql ├── non_retail_to_retail_upgrade.sh ├── plugins └── retail │ ├── bq_queries │ ├── 17-shopping_productgroupsummary.sql │ ├── 15-campaign_conversion_action_name.sql │ ├── 16-shopping_campaignproducttype.sql │ └── 18-shopping_lowclicks_highroas_producttype.sql │ └── google_ads_queries │ └── shoppingperformance_view.sql ├── setup-wfs.sh ├── CONTRIBUTING.md ├── answers.json ├── upgrade_pmaximizer.sh ├── no_gmc_update.sh ├── walkthrough.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | work_retail/ 2 | node_modules -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pmaximizer", 3 | "main": "index.js", 4 | "author": "", 5 | "license": "ISC", 6 | "dependencies": { 7 | "lsd-cloner": "^1.0.3" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2022 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 | -------------------------------------------------------------------------------- /get-accounts.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2022 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 | SELECT customer.id 16 | FROM campaign 17 | WHERE campaign.advertising_channel_type = "PERFORMANCE_MAX" -------------------------------------------------------------------------------- /bq_queries/20-placements_view.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.placements_view` AS 16 | SELECT 17 | * 18 | FROM `{bq_dataset}.pmax_placement_view` -------------------------------------------------------------------------------- /google_ads_queries/customer.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | customer.id as account_id, 17 | customer.descriptive_name as account_name, 18 | customer.currency_code as currency 19 | FROM customer -------------------------------------------------------------------------------- /google_ads_queries/conversions.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | conversion_action.name AS conversion_action, 17 | conversion_action.type AS conversion_type, 18 | customer.id AS account_id 19 | FROM conversion_action -------------------------------------------------------------------------------- /google_ads_queries/ad_group_asset.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | asset.id AS asset_id, 17 | ad_group_asset.ad_group~0 AS ad_group_id, 18 | customer.id AS account_id, 19 | ad_group_asset.field_type AS asset_type 20 | FROM 21 | ad_group_asset -------------------------------------------------------------------------------- /google_ads_queries/ocid_mapping.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | 16 | -- This is a GAARF capability that makes it possible 17 | -- to add links to the dashboard later. 18 | -- GAARF docs: https://github.com/google/ads-api-report-fetcher 19 | SELECT * FROM builtin.ocid_mapping -------------------------------------------------------------------------------- /google_ads_queries/custom_goal_names.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | customer.id AS account_id, 17 | custom_conversion_goal.id AS custom_conversion_goal_id, 18 | custom_conversion_goal.name AS custom_conversion_goal_name 19 | FROM custom_conversion_goal 20 | -------------------------------------------------------------------------------- /non_retail_to_retail_upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2022 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | set -e 17 | 18 | git pull 19 | source ./build-retail.sh 20 | 21 | echo "Now upgrading from non-retail version to retail version by re-installing." 22 | npm init gaarf-wf@latest -- --answers=answers.json -------------------------------------------------------------------------------- /google_ads_queries/assetgroupsignal.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | asset_group_signal.audience.audience AS audience_signals, 17 | campaign.id AS campaign_id, 18 | asset_group.id AS asset_group_id 19 | FROM 20 | asset_group_signal 21 | WHERE 22 | asset_group_signal.audience.audience IS NOT NULL 23 | -------------------------------------------------------------------------------- /google_ads_queries/recommendations.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | customer.id as account_id, 17 | campaign.id as campaign_id, 18 | recommendation.type as rec_type 19 | FROM 20 | recommendation 21 | WHERE 22 | campaign.advertising_channel_type = 'PERFORMANCE_MAX' 23 | AND recommendation.type in ('CAMPAIGN_BUDGET', 'FORECASTING_CAMPAIGN_BUDGET') -------------------------------------------------------------------------------- /bq_queries/14-poor_assets_summary.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.poor_assets_summary` AS 16 | SELECT 17 | COUNT(distinct ABP.campaign_id) AS num_campaigns, 18 | COUNT(distinct ABP.asset_group_id) AS num_asset_groups 19 | FROM `{bq_dataset}_bq.assetgroupbestpractices` ABP 20 | WHERE ad_strength = 'POOR' 21 | -------------------------------------------------------------------------------- /google_ads_queries/conversion_category.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | campaign.id AS campaign_id, 17 | customer.id AS account_id, 18 | campaign_conversion_goal.category AS conversion_name, 19 | campaign.advertising_channel_type AS campaign_type 20 | FROM campaign_conversion_goal 21 | WHERE campaign.advertising_channel_type IN ("PERFORMANCE_MAX", "SEARCH") 22 | AND campaign_conversion_goal.biddable = true 23 | -------------------------------------------------------------------------------- /google_ads_queries/customerasset.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | 16 | SELECT 17 | customer.descriptive_name AS account_name, 18 | campaign.id AS campaign_id, 19 | customer.id AS account_id, 20 | customer_asset.asset AS asset, 21 | customer_asset.field_type AS asset_type, 22 | customer_asset.primary_status AS asset_primary_status, 23 | customer_asset.status AS asset_status, 24 | FROM customer_asset 25 | WHERE segments.date >= "{start_date}" 26 | AND segments.date <= "{end_date}" -------------------------------------------------------------------------------- /google_ads_queries/campaignasset.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | customer.descriptive_name as account_name, 17 | campaign.id as campaign_id, 18 | customer.id as account_id, 19 | campaign.name as campaign_name, 20 | campaign.advertising_channel_type as campaign_type, 21 | campaign.status as campaign_status, 22 | campaign_asset.field_type as asset_type 23 | FROM campaign_asset 24 | WHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX' 25 | AND campaign.status = 'ENABLED' -------------------------------------------------------------------------------- /google_ads_queries/conversion_custom.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | campaign.id AS campaign_id, 17 | conversion_goal_campaign_config.custom_conversion_goal~0 AS custom_conversion_goal_id, 18 | customer.id AS account_id, 19 | campaign.advertising_channel_type AS campaign_type 20 | FROM conversion_goal_campaign_config 21 | WHERE campaign.advertising_channel_type IN ("PERFORMANCE_MAX", "SEARCH") 22 | AND conversion_goal_campaign_config.custom_conversion_goal IS NOT NULL 23 | -------------------------------------------------------------------------------- /google_ads_queries/conversion_split.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | segments.date AS date, 17 | campaign.id AS campaign_id, 18 | segments.conversion_action~0 AS conversion_action_id, 19 | segments.conversion_action_name AS conversion_name, 20 | metrics.conversions AS conversions, 21 | customer.id as account_id, 22 | campaign.advertising_channel_type AS campaign_type 23 | FROM ad_group_ad 24 | WHERE 25 | segments.date >= "{start_date}" 26 | AND segments.date <= "{end_date}" 27 | AND campaign.advertising_channel_type IN ('PERFORMANCE_MAX', 'SEARCH') -------------------------------------------------------------------------------- /plugins/retail/bq_queries/17-shopping_productgroupsummary.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.shopping_productgroupsummary` 16 | AS ( 17 | SELECT 18 | date, 19 | product_type, 20 | SUM(clicks) AS clicks, 21 | SUM(impressions) AS impressions, 22 | # ctr = clicks/impressions*100 23 | SUM(sum_cost) AS sum_cost, 24 | SUM(sum_conversions_value) AS sum_conversions_value 25 | # roas = conversions_value/cost 26 | FROM 27 | `{bq_dataset}_bq.shopping_campaignproducttype` 28 | GROUP BY 1, 2 29 | ) 30 | -------------------------------------------------------------------------------- /setup-wfs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2022 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | set -e 17 | 18 | 19 | until [[ "$yn" == [YyNn] ]]; do 20 | msg='Do you operate a retail business and wish to gain product insights with our retail-focused pMaximizer?' 21 | msg+=' Please respond with 'y' for yes or 'n' for no: ' 22 | read -p "$msg" yn 23 | done 24 | 25 | if [[ "$yn" == "y" || "$yn" == "Y" ]]; then 26 | source ./build-retail.sh 27 | else 28 | echo "skipping Retail setup..." 29 | fi 30 | 31 | echo "initializing Google Ads data ETL Workflow..." 32 | npm init gaarf-wf@latest -- --answers=answers.json 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our Community Guidelines 22 | 23 | This project follows 24 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code Reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use GitHub pull requests for this purpose. Consult 32 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 33 | information on using pull requests. 34 | -------------------------------------------------------------------------------- /plugins/retail/bq_queries/15-campaign_conversion_action_name.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | 16 | 17 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.campaign_conversion_action_name` AS ( 18 | SELECT 19 | date, 20 | account_id, 21 | account_name, 22 | campaign_id, 23 | campaign_name, 24 | conversion_action_name, 25 | conversion_action_category, 26 | sum(all_conversions) AS all_conversions, 27 | sum(all_conversions_value) AS all_conversions_value, 28 | sum(conversions) AS conversions, 29 | sum(view_through_conversions) AS view_thrupgh_conversions, 30 | sum(value_per_conversion) AS value_per_conversion 31 | FROM `{bq_dataset}.campaignconversionaction` 32 | GROUP BY 1, 2, 3, 4, 5, 6, 7 33 | ) -------------------------------------------------------------------------------- /answers.json: -------------------------------------------------------------------------------- 1 | { 2 | "use_current_project": true, 3 | "name": "pmax", 4 | "gcs_bucket": "", 5 | "service_account": "", 6 | "path_to_ads_queries": "google_ads_queries", 7 | "path_to_bq_queries": "bq_queries", 8 | "custom_ids_query_path": "get-accounts.sql", 9 | "path_to_googleads_config": "google-ads.yaml", 10 | "cf_memory": "2048MB", 11 | "deploy_scripts": true, 12 | "deploy_wf": true, 13 | "output_dataset": "pmax_ads", 14 | "ads_macro": { 15 | "start_date": ":YYYYMMDD-90", 16 | "end_date": ":YYYYMMDD-1" 17 | }, 18 | "bq_macro": { 19 | "bq_dataset": "pmax_ads" 20 | }, 21 | "schedule_wf": true, 22 | "schedule_time": "04:00", 23 | "run_job": false, 24 | "run_wf": true, 25 | "clone_dashboard": true, 26 | "dashboard_id": "6e773f65-7612-45af-af37-e3cab6a9828b", 27 | "dashboard_name": "pMaximizer", 28 | "dashboard_dataset": "pmax_ads_bq", 29 | "dashboard_datasources": { 30 | "poor_assets": "poor_assets_summary", 31 | "campaign_settings": "campaign_settings", 32 | "campaign_setup": "campaign_data", 33 | "asset_group_bp": "assetgroupbestpractices", 34 | "asset_performance_snapshots": "assetssnapshots_*", 35 | "assets_performance": "summaryassets", 36 | "scores": "campaign_scores_union", 37 | "asset_group_performance": "assetgroup_performance", 38 | "placements_view": "placements_view" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /bq_queries/06-assetssnapshots.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.assetssnapshots_${format(yesterday(),'yyyyMMdd')}` AS ( 16 | SELECT 17 | CURRENT_DATE()-1 as day, 18 | AGA.account_id, 19 | AGA.account_name, 20 | AGA.asset_group_id, 21 | AGA.asset_group_name, 22 | AGA.campaign_name, 23 | AGA.campaign_id, 24 | AGA.asset_id, 25 | AGA.asset_sub_type, 26 | AGA.asset_performance, 27 | AGA.text_asset_text, 28 | COALESCE(AGA.image_url,CONCAT('https://www.youtube.com/watch?v=',AGA.video_id)) AS image_video, 29 | COALESCE(AGA.image_url,CONCAT('https://i.ytimg.com/vi/', CONCAT(AGA.video_id, '/hqdefault.jpg'))) AS image_video_url 30 | FROM {bq_dataset}.assetgroupasset AGA 31 | WHERE asset_performance NOT IN ('PENDING','UNKNOWN') 32 | GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13) 33 | -------------------------------------------------------------------------------- /google_ads_queries/pmax_placement_view.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | segments.date AS date, 17 | campaign.advertising_channel_type AS campaign_type, 18 | campaign.status AS campaign_status, 19 | customer.id AS account_id, 20 | customer.descriptive_name AS account_name, 21 | campaign.id AS campaign_id, 22 | campaign.name AS campaign_name, 23 | performance_max_placement_view.display_name AS placement_name, 24 | performance_max_placement_view.placement AS placement, 25 | performance_max_placement_view.placement_type AS placement_type, 26 | performance_max_placement_view.target_url AS placement_target_url, 27 | metrics.impressions AS impressions 28 | FROM performance_max_placement_view 29 | WHERE campaign.advertising_channel_type = "PERFORMANCE_MAX" 30 | AND segments.date >= "{start_date}" 31 | AND segments.date <= "{end_date}" 32 | AND campaign.status = 'ENABLED' -------------------------------------------------------------------------------- /google_ads_queries/tcpa_search.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | customer.id as account_id, 17 | campaign.id as campaign_id, 18 | segments.conversion_action~0 AS conversion_action_id, 19 | segments.conversion_action_name AS conversion_name, 20 | bidding_strategy.maximize_conversion_value.target_roas AS bidding_strategy_mcv_troas, 21 | bidding_strategy.target_roas.target_roas AS bidding_strategy_troas, 22 | bidding_strategy.maximize_conversions.target_cpa_micros AS bidding_strategy_mc_tcpa, 23 | bidding_strategy.target_cpa.target_cpa_micros AS bidding_strategy_tcpa, 24 | campaign.maximize_conversions.target_cpa_micros AS campaign_mc_tcpa, 25 | campaign.target_cpa.target_cpa_micros AS campaign_tcpa, 26 | campaign.maximize_conversion_value.target_roas AS campaign_mcv_troas, 27 | campaign.target_roas.target_roas AS campaign_troas 28 | FROM 29 | campaign 30 | WHERE 31 | campaign.advertising_channel_type = 'SEARCH' -------------------------------------------------------------------------------- /upgrade_pmaximizer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2022 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | #This is a script to update code from git and re-run all queries for retail-pmax users. 18 | set -e 19 | 20 | WORK_RETAIL="work_retail" 21 | 22 | git pull 23 | 24 | #Check if the user deployed the retail version of pMaximizer. If so, work from there. 25 | if [ -d "$WORK_RETAIL" ]; then 26 | ./build-retail.sh 27 | cd "$WORK_RETAIL" 28 | fi 29 | 30 | # This will trigger an update of gaarf, our underlying ETL library 31 | ./deploy-wf.sh 32 | 33 | if [ -f "./deploy-queries.sh" ]; then 34 | # If deploy-queries.sh exists, run it 35 | ./deploy-queries.sh 36 | else 37 | # If deploy-queries.sh does not exist, run deploy-scripts.sh 38 | ./deploy-scripts.sh 39 | fi 40 | ./run-wf.sh 41 | 42 | echo "generating a new up-to-date copy of our main template..." 43 | # This will use our answers.json to generate a new template 44 | npm install 45 | npx lsd-cloner --answers=answers.json -------------------------------------------------------------------------------- /bq_queries/13-campaign_scores_union.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.campaign_scores_union` AS 16 | WITH 17 | score_types AS 18 | ( 19 | SELECT 'Video Score' AS score_type UNION ALL 20 | SELECT 'Image Score' UNION ALL 21 | SELECT 'Text Score' UNION ALL 22 | SELECT 'Campaign Best Practice Score' 23 | ), 24 | 25 | scores_union AS 26 | ( 27 | SELECT 28 | `date`, 29 | campaign_name, 30 | video_score, 31 | image_score, 32 | text_score, 33 | campaign_bp_score, 34 | S.score_type AS score_type 35 | FROM `{bq_dataset}_bq.campaign_settings` 36 | CROSS JOIN score_types S 37 | ) 38 | 39 | SELECT 40 | `date`, 41 | campaign_name, 42 | score_type, 43 | CASE score_type 44 | WHEN "Video Score" THEN video_score 45 | WHEN "Image Score" THEN image_score 46 | WHEN "Text Score" THEN text_score 47 | WHEN "Campaign Best Practice Score" THEN campaign_bp_score 48 | ELSE NULL 49 | END AS score 50 | FROM scores_union 51 | -------------------------------------------------------------------------------- /bq_queries/09-bpscore.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.campaignbpscore_${format(today(),'yyyyMMdd')}` AS 16 | ( 17 | SELECT 18 | date, 19 | CD.account_id, 20 | CD.account_name, 21 | CD.campaign_id, 22 | CD.campaign_name, 23 | round( 24 | ( 25 | IF(CD.url_expansion_opt_out=true,1,0) + 26 | CD.audience_signals_score + 27 | IF(CD.missing_sitelinks=0,1,missing_sitelinks/4) + 28 | CASE 29 | WHEN positive_geo_target_type_configured_good='X' AND negative_geo_target_type_configured_good='X' THEN 0 30 | WHEN positive_geo_target_type_configured_good='Yes' AND negative_geo_target_type_configured_good='X' THEN 0.5 31 | WHEN positive_geo_target_type_configured_good='X' AND negative_geo_target_type_configured_good='Yes' THEN 0.5 32 | WHEN positive_geo_target_type_configured_good='Yes' AND negative_geo_target_type_configured_good='Yes' THEN 1 33 | END 34 | )/4,2 35 | ) AS campaign_bp_score 36 | FROM `{bq_dataset}_bq.campaign_data`AS CD 37 | GROUP BY 1,2,3,4,5,6 38 | ) -------------------------------------------------------------------------------- /plugins/retail/google_ads_queries/shoppingperformance_view.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | customer.descriptive_name as account_name, 17 | customer.id as account_id, 18 | campaign.advertising_channel_type, 19 | campaign.id AS campaign_id, 20 | campaign.name AS campaign_name, 21 | campaign.status AS campaign_status, 22 | segments.date AS date, 23 | segments.product_type_l1 AS product_type_l1, 24 | segments.product_type_l2 AS product_type_l2, 25 | segments.product_type_l3 AS product_type_l3, 26 | segments.product_type_l4 AS product_type_l4, 27 | segments.product_type_l5 AS product_type_l5, 28 | metrics.ctr AS ctr, 29 | metrics.clicks AS clicks, 30 | metrics.conversions AS conversions, 31 | metrics.impressions AS impressions, 32 | metrics.conversions_value AS conversions_value, 33 | metrics.cost_micros AS cost 34 | FROM shopping_performance_view 35 | WHERE campaign.advertising_channel_type = "PERFORMANCE_MAX" 36 | AND segments.date >= "{start_date}" 37 | AND segments.date <= "{end_date}" 38 | AND campaign.status = 'ENABLED' -------------------------------------------------------------------------------- /bq_queries/05-video_assets.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.video_assets` 16 | AS ( 17 | WITH asset_group_with_videos AS( 18 | SELECT DISTINCT AGA.asset_group_id 19 | FROM `{bq_dataset}.assetgroupasset` AS AGA 20 | WHERE AGA.asset_type = 'YOUTUBE_VIDEO' 21 | AND AGA.video_id IS NOT NULL 22 | AND AGA.video_id != '' 23 | ), 24 | count_videos AS ( 25 | SELECT 26 | campaign_id, 27 | asset_group_id, 28 | COUNT(*) AS count_videos 29 | FROM `{bq_dataset}.assetgroupasset` 30 | WHERE asset_type = 'YOUTUBE_VIDEO' 31 | GROUP BY 1, 2 32 | ) 33 | SELECT DISTINCT 34 | AGS.account_id, 35 | AGS.account_name, 36 | AGS.campaign_id, 37 | AGS.campaign_name, 38 | AGS.asset_group_id, 39 | AGS.asset_group_name, 40 | AGS.ad_strength, 41 | IF(AGV.asset_group_id IS NULL, "X", "Yes") AS is_video_uploaded, 42 | COALESCE(CV.count_videos,0) AS count_videos 43 | FROM `{bq_dataset}.assetgroupsummary` AS AGS 44 | LEFT JOIN asset_group_with_videos AS AGV USING (asset_group_id) 45 | LEFT JOIN count_videos CV USING (campaign_id,asset_group_id) 46 | ) 47 | -------------------------------------------------------------------------------- /google_ads_queries/assetgroupsummary.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | segments.date AS date, 17 | asset_group.id AS asset_group_id, 18 | asset_group.name AS asset_group_name, 19 | asset_group.status AS asset_group_status, 20 | campaign.id AS campaign_id, 21 | campaign.name AS campaign_name, 22 | customer.id AS account_id, 23 | customer.descriptive_name AS account_name, 24 | asset_group.ad_strength AS ad_strength, 25 | metrics.clicks AS clicks, 26 | metrics.conversions AS conversions, 27 | metrics.all_conversions AS all_conversions, 28 | metrics.cost_micros As cost, 29 | metrics.conversions_value AS conversions_value, 30 | metrics.impressions AS impressions, 31 | metrics.ctr AS ctr, 32 | metrics.value_per_all_conversions AS value_per_all_conversions, 33 | metrics.value_per_conversion AS value_per_conversion, 34 | metrics.all_conversions_value AS all_conversions_value, 35 | FROM 36 | asset_group 37 | WHERE 38 | campaign.advertising_channel_type = 'PERFORMANCE_MAX' 39 | AND asset_group.status = 'ENABLED' 40 | AND campaign.status = 'ENABLED' 41 | AND segments.date >= '{start_date}' 42 | AND segments.date <= '{end_date}' -------------------------------------------------------------------------------- /google_ads_queries/assetgroupasset.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | asset_group.id AS asset_group_id, 17 | asset_group.name AS asset_group_name, 18 | asset_group.status AS asset_group_status, 19 | campaign.id AS campaign_id, 20 | campaign.name AS campaign_name, 21 | asset.id AS asset_id, 22 | customer.id AS account_id, 23 | customer.descriptive_name AS account_name, 24 | asset.type AS asset_type, 25 | asset_group_asset.field_type AS asset_sub_type, 26 | asset_group_asset.performance_label as asset_performance, 27 | asset.image_asset.file_size AS image_file_size, 28 | asset.image_asset.full_size.url AS image_url, 29 | asset.text_asset.text AS text_asset_text, 30 | asset.image_asset.full_size.height_pixels AS image_height, 31 | asset.image_asset.full_size.width_pixels AS image_width, 32 | asset.youtube_video_asset.youtube_video_id AS video_id, 33 | asset.youtube_video_asset.youtube_video_title AS video_title 34 | FROM asset_group_asset 35 | WHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX' 36 | AND campaign.status = 'ENABLED' 37 | AND asset_group.status = 'ENABLED' 38 | AND asset_group_asset.status = 'ENABLED' 39 | AND asset_group_asset.source = 'ADVERTISER' -------------------------------------------------------------------------------- /bq_queries/19-assetgroupperformance.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | 16 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.assetgroup_performance` AS ( 17 | WITH 18 | LOW_ASSETS AS ( 19 | SELECT 20 | asset_group_id, 21 | COUNT(*) AS sum_of_low_perf_assets 22 | FROM `{bq_dataset}_bq.summaryassets` 23 | WHERE asset_performance = 'LOW' 24 | GROUP BY asset_group_id 25 | ) 26 | SELECT 27 | AGS.date, 28 | AGS.account_id, 29 | AGS.account_name, 30 | AGS.campaign_id, 31 | AGS.campaign_name, 32 | AGS.asset_group_id, 33 | AGS.asset_group_name, 34 | AGS.asset_group_status, 35 | AGS.ad_strength, 36 | AGS.clicks, 37 | AGS.impressions, 38 | AGS.ctr, 39 | AGS.cost / 1e6 AS cost, 40 | AGS.conversions, 41 | AGS.all_conversions, 42 | AGS.conversions_value, 43 | AGS.all_conversions_value, 44 | AGS.value_per_all_conversions, 45 | AGS.value_per_conversion, 46 | IFNULL(LA.sum_of_low_perf_assets, 0) AS sum_of_low_performing_assets 47 | FROM `{bq_dataset}.assetgroupsummary` AS AGS 48 | LEFT JOIN LOW_ASSETS AS LA 49 | ON AGS.asset_group_id = LA.asset_group_id 50 | ) -------------------------------------------------------------------------------- /plugins/retail/bq_queries/16-shopping_campaignproducttype.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.shopping_campaignproducttype` AS ( 16 | SELECT 17 | date, 18 | account_id, 19 | account_name, 20 | campaign_name, 21 | campaign_id, 22 | CASE 23 | WHEN product_type_l1 IS NOT NULL AND product_type_l1 <> '' THEN 24 | COALESCE(product_type_l1, '') || CASE WHEN product_type_l2 IS NOT NULL AND product_type_l2 <> '' THEN ' > ' ELSE '' END || 25 | COALESCE(product_type_l2, '') || CASE WHEN product_type_l3 IS NOT NULL AND product_type_l3 <> '' THEN ' > ' ELSE '' END || 26 | COALESCE(product_type_l3, '') || CASE WHEN product_type_l4 IS NOT NULL AND product_type_l4 <> '' THEN ' > ' ELSE '' END || 27 | COALESCE(product_type_l4, '') || CASE WHEN product_type_l5 IS NOT NULL AND product_type_l5 <> '' THEN ' > ' ELSE '' END || 28 | COALESCE(product_type_l5, '') 29 | ELSE '' 30 | END AS product_type, 31 | SUM(clicks) AS clicks, 32 | SUM(conversions) AS conversions, 33 | SUM(impressions) AS impressions, 34 | SUM(conversions_value) AS sum_conversions_value, 35 | SUM(cost/1e6) AS sum_cost, 36 | SUM(ctr) AS ctr 37 | FROM 38 | `{bq_dataset}.shoppingperformance_view` 39 | GROUP BY 1,2,3,4,5,6 40 | ) 41 | -------------------------------------------------------------------------------- /bq_queries/08-assetsummary.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.summaryassets` AS ( 16 | SELECT 17 | AGA.account_id, 18 | AGA.account_name, 19 | AGA.asset_group_id, 20 | AGA.asset_group_name, 21 | AGA.campaign_name, 22 | AGA.campaign_id, 23 | AGA.asset_id, 24 | AGA.asset_sub_type, 25 | AGA.asset_performance, 26 | AGA.text_asset_text, 27 | AGS.asset_group_status, 28 | COALESCE(AGA.image_url,CONCAT('https://www.youtube.com/watch?v=',AGA.video_id)) AS image_video, 29 | COALESCE(AGA.image_url,CONCAT('https://i.ytimg.com/vi/', CONCAT(AGA.video_id, '/hqdefault.jpg'))) AS image_video_url, 30 | OCID.ocid, 31 | CS.last_30_day_campaign_cost 32 | FROM `{bq_dataset}.assetgroupasset` AS AGA 33 | JOIN `{bq_dataset}.assetgroupsummary` AS AGS 34 | USING(asset_group_id) 35 | LEFT JOIN `{bq_dataset}.ocid_mapping` AS OCID 36 | ON OCID.customer_id = AGA.account_id 37 | LEFT JOIN ( 38 | SELECT 39 | campaign_id, 40 | SUM(cost) AS last_30_day_campaign_cost 41 | FROM `{bq_dataset}.campaign_settings` 42 | WHERE PARSE_DATE('%Y-%m-%d', date) >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY) 43 | GROUP BY campaign_id 44 | ) AS CS ON AGA.campaign_id = CS.campaign_id 45 | WHERE AGA.asset_performance NOT IN ('PENDING','UNKNOWN') 46 | AND AGA.campaign_id 47 | IN (SELECT campaign_id FROM `{bq_dataset}.campaign_settings`) 48 | GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); 49 | -------------------------------------------------------------------------------- /google_ads_queries/campaignconversionaction.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | 16 | SELECT 17 | campaign.id as campaign_id, 18 | campaign.name as campaign_name, 19 | campaign.status as campaign_status, 20 | campaign.advertising_channel_type as campaign_type, 21 | customer.id as account_id, 22 | customer.descriptive_name as account_name, 23 | segments.conversion_action_category as conversion_action_category, 24 | segments.conversion_action as conversion_action_id, 25 | segments.conversion_action_name as conversion_action_name, 26 | segments.date as date, 27 | metrics.all_conversions as all_conversions, 28 | metrics.all_conversions_by_conversion_date as all_conversions_by_conversion_date, 29 | metrics.all_conversions_value as all_conversions_value, 30 | metrics.all_conversions_value_by_conversion_date as all_conversions_value_by_conversion_date, 31 | metrics.conversions as conversions, 32 | metrics.conversions_by_conversion_date as conversions_by_conversion_date, 33 | metrics.conversions_value as conversions_value, 34 | metrics.conversions_value_by_conversion_date as conversions_value_by_conversion_date, 35 | metrics.value_per_conversion as value_per_conversion, 36 | metrics.value_per_all_conversions as value_per_all_conversions, 37 | metrics.view_through_conversions as view_through_conversions 38 | FROM campaign 39 | WHERE campaign.advertising_channel_type = "PERFORMANCE_MAX" 40 | AND segments.date >= "{start_date}" 41 | AND segments.date <= "{end_date}" 42 | AND campaign.status = 'ENABLED' -------------------------------------------------------------------------------- /google_ads_queries/campaign_settings.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | SELECT 16 | segments.date AS date, 17 | customer.id AS account_id, 18 | customer.descriptive_name AS account_name, 19 | campaign.id AS campaign_id, 20 | campaign.name AS campaign_name, 21 | campaign.status AS campaign_status, 22 | campaign.url_expansion_opt_out AS url_expansion_opt_out, 23 | campaign.bidding_strategy_type AS bidding_strategy, 24 | campaign_budget.amount_micros AS budget_amount, 25 | campaign_budget.total_amount_micros AS total_budget, 26 | campaign_budget.type AS budget_type, 27 | campaign_budget.explicitly_shared AS is_shared_budget, 28 | campaign_budget.period AS budget_period, 29 | bidding_strategy.maximize_conversion_value.target_roas AS bidding_strategy_mcv_troas, 30 | bidding_strategy.target_roas.target_roas AS bidding_strategy_troas, 31 | bidding_strategy.maximize_conversions.target_cpa_micros AS bidding_strategy_mc_tcpa, 32 | bidding_strategy.target_cpa.target_cpa_micros AS bidding_strategy_tcpa, 33 | campaign.maximize_conversions.target_cpa_micros AS campaign_mc_tcpa, 34 | campaign.target_cpa.target_cpa_micros AS campaign_tcpa, 35 | campaign.maximize_conversion_value.target_roas AS campaign_mcv_troas, 36 | campaign.target_roas.target_roas AS campaign_troas, 37 | campaign.selective_optimization.conversion_actions AS selective_optimization_conversion_actions, 38 | campaign.shopping_setting.merchant_id as gmc_id, 39 | campaign.optimization_score as optiscore, 40 | campaign.geo_target_type_setting.negative_geo_target_type as negative_geo_target_type, 41 | campaign.geo_target_type_setting.positive_geo_target_type as positive_geo_target_type, 42 | metrics.cost_micros AS cost, 43 | metrics.conversions AS conversions, 44 | metrics.conversions_value AS conversions_value, 45 | metrics.all_conversions_value AS all_conversions_value 46 | FROM campaign 47 | WHERE campaign.advertising_channel_type = "PERFORMANCE_MAX" 48 | AND segments.date >= "{start_date}" 49 | AND segments.date <= "{end_date}" 50 | AND campaign.status = 'ENABLED' -------------------------------------------------------------------------------- /bq_queries/03-primary_conversion_action_search.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.primary_conversion_action_search` 16 | AS 17 | WITH CONVERSION_ACCOUNT_LEVEL AS ( 18 | SELECT DISTINCT 19 | account_id, 20 | campaign_id, 21 | campaign_type, 22 | conversion_name 23 | FROM `{bq_dataset}.conversion_category` 24 | ), 25 | CONVERSION_SPLIT AS ( 26 | SELECT 27 | account_id, 28 | campaign_id, 29 | campaign_type, 30 | ARRAY_TO_STRING(ARRAY_AGG(conversion_name),",") AS conversion_name 31 | FROM CONVERSION_ACCOUNT_LEVEL 32 | GROUP BY 1,2,3 33 | UNION ALL 34 | SELECT 35 | account_id, 36 | campaign_id, 37 | campaign_type, 38 | custom_conversion_goal_name AS conversion_name 39 | FROM `{bq_dataset}.conversion_custom` 40 | JOIN `{bq_dataset}.custom_goal_names` 41 | USING (account_id,custom_conversion_goal_id) 42 | ), 43 | CONVERSION_SPLIT_GROUPED_W_CUSTOM AS ( 44 | SELECT 45 | account_id, 46 | campaign_id, 47 | campaign_type, 48 | ARRAY_TO_STRING(ARRAY_AGG(conversion_name),",") AS conversion_name 49 | FROM CONVERSION_SPLIT 50 | GROUP BY 1,2,3 51 | ), 52 | CONVERSION_ACTION_FREQUENCY AS ( 53 | SELECT 54 | account_id, 55 | campaign_type, 56 | conversion_name, 57 | RANK() OVER (PARTITION BY account_id, campaign_type 58 | ORDER BY COUNT(DISTINCT campaign_id) DESC) AS row_num 59 | FROM CONVERSION_SPLIT_GROUPED_W_CUSTOM 60 | WHERE campaign_type = "SEARCH" 61 | GROUP BY 62 | account_id, 63 | conversion_name, 64 | campaign_type 65 | ), 66 | CONVERSION_ACTION_FREQUENCY_GROUPED AS ( 67 | SELECT DISTINCT 68 | account_id, 69 | campaign_type, 70 | row_num, 71 | conversion_name 72 | FROM CONVERSION_ACTION_FREQUENCY 73 | ), 74 | MOST_USED_CONVERSIONS AS ( 75 | SELECT DISTINCT 76 | CS.account_id, 77 | CS.campaign_id, 78 | CF.conversion_name 79 | FROM CONVERSION_SPLIT_GROUPED_W_CUSTOM CS 80 | JOIN CONVERSION_ACTION_FREQUENCY_GROUPED CF 81 | USING (account_id) 82 | WHERE row_num = 1 83 | AND CS.campaign_type = "SEARCH" 84 | ) 85 | SELECT DISTINCT 86 | account_id, 87 | conversion_name 88 | FROM MOST_USED_CONVERSIONS -------------------------------------------------------------------------------- /plugins/retail/bq_queries/18-shopping_lowclicks_highroas_producttype.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.shopping_lowclicks_highroas_producttype` AS ( 16 | WITH no_date_shopping_gads AS ( 17 | SELECT 18 | campaign_name, 19 | campaign_id, 20 | CASE 21 | WHEN product_type_l1 IS NOT NULL AND product_type_l1 <> '' THEN 22 | COALESCE(product_type_l1, '') || CASE WHEN product_type_l2 IS NOT NULL AND product_type_l2 <> '' THEN ' > ' ELSE '' END || 23 | COALESCE(product_type_l2, '') || CASE WHEN product_type_l3 IS NOT NULL AND product_type_l3 <> '' THEN ' > ' ELSE '' END || 24 | COALESCE(product_type_l3, '') || CASE WHEN product_type_l4 IS NOT NULL AND product_type_l4 <> '' THEN ' > ' ELSE '' END || 25 | COALESCE(product_type_l4, '') || CASE WHEN product_type_l5 IS NOT NULL AND product_type_l5 <> '' THEN ' > ' ELSE '' END || 26 | COALESCE(product_type_l5, '') 27 | ELSE '' 28 | END AS product_type, 29 | SUM(clicks) AS clicks, 30 | SUM(conversions) AS conversions, 31 | SUM(impressions) AS impressions, 32 | SUM(conversions_value) AS conversions_value, 33 | SUM(cost/1e6) AS cost, 34 | SUM(ctr) AS ctr 35 | FROM 36 | `{bq_dataset}.shoppingperformance_view` 37 | GROUP BY 1,2,3 38 | ), 39 | AverageClicks AS ( 40 | SELECT 41 | SUM(clicks) / COUNT(*) AS average_clicks 42 | FROM 43 | no_date_shopping_gads 44 | ) 45 | SELECT 46 | NDSG.campaign_name, 47 | NDSG.product_type, 48 | CONCAT(NDSG.product_type, ' ||| ', NDSG.campaign_name) AS product_type_plus_campaign, 49 | AC.average_clicks, 50 | NDSG.clicks, 51 | NDSG.impressions, 52 | NDSG.cost, 53 | NDSG.conversions_value, 54 | CASE 55 | WHEN NDSG.cost = 0 THEN 0 56 | ELSE NDSG.conversions_value/NDSG.cost 57 | END AS roas 58 | FROM 59 | no_date_shopping_gads NDSG, 60 | AverageClicks AC 61 | WHERE 62 | NDSG.clicks BETWEEN 0.10 * AC.average_clicks AND 0.50 * AC.average_clicks 63 | ORDER BY roas DESC 64 | LIMIT 15 65 | ) 66 | -------------------------------------------------------------------------------- /no_gmc_update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2022 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | #This is a script to update the version of the retail pmax that uses the Merchant Center data to the version hat doesn;t use GMC data, only google Ads. 18 | set -e 19 | 20 | git pull 21 | 22 | ANSWERS="answers.json" 23 | WORK_RETAIL="work_retail" 24 | 25 | # Using Node.js to read values from answers.json 26 | READ_SCRIPT=" 27 | const fs = require('fs'); 28 | const data = JSON.parse(fs.readFileSync('${ANSWERS}', 'utf8')); 29 | console.log(data.name); 30 | console.log(data.gcs_bucket); 31 | console.log(data.path_to_bq_queries); 32 | " 33 | 34 | # Read values from answers.json to determine which bucket to delete. 35 | read -r name gcs_bucket path_to_bq_queries <<< $(node -e "$READ_SCRIPT") 36 | 37 | # Determine the GCS bucket name 38 | if [ -z "$gcs_bucket" ]; then 39 | PROJECT_ID=$(gcloud config get-value project) 40 | BUCKET_NAME=$PROJECT_ID 41 | else 42 | BUCKET_NAME=$gcs_bucket 43 | fi 44 | 45 | # Construct the full path of the folder to delete 46 | FOLDER_PATH="gs://${BUCKET_NAME}/${name}/${path_to_bq_queries}" 47 | 48 | # Check if the folder exists 49 | if gsutil ls "$FOLDER_PATH" >/dev/null 2>&1; then 50 | # Deleting the folder in the GCS bucket 51 | gsutil -m rm -r "${FOLDER_PATH}" 52 | echo "Folder ${path_to_bq_queries} in bucket ${BUCKET_NAME} under ${name} has been deleted." 53 | else 54 | echo "Folder ${path_to_bq_queries} does not exist in bucket ${BUCKET_NAME} under ${name}." 55 | fi 56 | 57 | 58 | if [ -d "$WORK_RETAIL" ]; then 59 | # Check and remove bq_queries if it exists 60 | if [ -d "$WORK_RETAIL/bq_queries" ]; then 61 | rm -rf "$WORK_RETAIL/bq_queries" 62 | echo "Directory '$WORK_RETAIL/bq_queries' has been removed." 63 | else 64 | echo "Directory '$WORK_RETAIL/bq_queries' does not exist." 65 | fi 66 | 67 | # Check and remove google_ads_queries if it exists 68 | if [ -d "$WORK_RETAIL/google_ads_queries" ]; then 69 | rm -rf "$WORK_RETAIL/google_ads_queries" 70 | echo "Directory '$WORK_RETAIL/google_ads_queries' has been removed." 71 | else 72 | echo "Directory '$WORK_RETAIL/google_ads_queries' does not exist." 73 | fi 74 | else 75 | echo "Directory '$WORK_RETAIL' does not exist." 76 | #if the directory doesn't exist at all, we will need to deploy from scratch 77 | ./build-retail.sh 78 | cd "$WORK_RETAIL" 79 | npm init gaarf-wf@latest -- --answers=answers.json 80 | exit 81 | fi 82 | ./build-retail.sh 83 | cd "$WORK_RETAIL" 84 | ./deploy-queries.sh 85 | ./run-wf.sh -------------------------------------------------------------------------------- /bq_queries/02-primary_conversion_action_pmax.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.primary_conversion_action_pmax` AS 16 | WITH 17 | CONVERSION_ACCOUNT_LEVEL AS ( 18 | SELECT DISTINCT 19 | account_id, 20 | campaign_id, 21 | campaign_type, 22 | conversion_name 23 | FROM `{bq_dataset}.conversion_category` 24 | ), 25 | CONVERSION_SPLIT AS ( 26 | SELECT 27 | account_id, 28 | campaign_id, 29 | campaign_type, 30 | ARRAY_TO_STRING(ARRAY_AGG(conversion_name),",") AS conversion_name 31 | FROM CONVERSION_ACCOUNT_LEVEL 32 | GROUP BY 1,2,3 33 | UNION ALL 34 | SELECT 35 | account_id, 36 | campaign_id, 37 | campaign_type, 38 | custom_conversion_goal_name AS conversion_name 39 | FROM `{bq_dataset}.conversion_custom` 40 | JOIN `{bq_dataset}.custom_goal_names` USING (account_id,custom_conversion_goal_id) 41 | ), 42 | CONVERSION_SPLIT_GROUPED_W_CUSTOM AS ( 43 | SELECT 44 | account_id, 45 | campaign_id, 46 | campaign_type, 47 | ARRAY_TO_STRING(ARRAY_AGG(conversion_name),",") AS conversion_name 48 | FROM CONVERSION_SPLIT 49 | GROUP BY 1,2,3 50 | ), 51 | CONVERSION_ACTION_FREQUENCY AS ( 52 | SELECT 53 | account_id, 54 | campaign_id, 55 | campaign_type, 56 | conversion_name, 57 | RANK() OVER (PARTITION BY account_id, campaign_type 58 | ORDER BY COUNT(DISTINCT campaign_id) DESC) AS row_num 59 | FROM CONVERSION_SPLIT_GROUPED_W_CUSTOM 60 | WHERE campaign_type = "PERFORMANCE_MAX" 61 | GROUP BY 62 | account_id, 63 | conversion_name, 64 | campaign_type, 65 | campaign_id 66 | ), 67 | CONVERSION_ACTION_FREQUENCY_GROUPED AS ( 68 | SELECT 69 | account_id, 70 | campaign_id, 71 | campaign_type, 72 | row_num, 73 | ARRAY_TO_STRING(ARRAY_AGG(conversion_name),",") AS conversion_name 74 | FROM CONVERSION_ACTION_FREQUENCY 75 | GROUP BY 76 | account_id, 77 | campaign_id, 78 | campaign_type, 79 | row_num 80 | ), 81 | MOST_USED_CONVERSIONS AS ( 82 | SELECT 83 | CS.account_id, 84 | CS.campaign_id, 85 | ARRAY_TO_STRING(ARRAY_AGG(CF.conversion_name),",") AS conversion_name 86 | FROM CONVERSION_SPLIT_GROUPED_W_CUSTOM AS CS 87 | JOIN CONVERSION_ACTION_FREQUENCY_GROUPED AS CF 88 | USING (account_id, campaign_id) 89 | WHERE row_num = 1 90 | AND CS.campaign_type = "PERFORMANCE_MAX" 91 | GROUP BY 1,2 92 | ) 93 | SELECT DISTINCT 94 | account_id, 95 | campaign_id, 96 | conversion_name 97 | FROM MOST_USED_CONVERSIONS -------------------------------------------------------------------------------- /bq_queries/04-text_assets.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.text_assets` 16 | AS 17 | WITH count_headlines AS ( 18 | SELECT 19 | campaign_id, 20 | asset_group_id, 21 | COUNT(*) AS count_headlines 22 | FROM `{bq_dataset}.assetgroupasset` 23 | WHERE asset_sub_type IN ('HEADLINE', 'LONG_HEADLINE') 24 | GROUP BY 1, 2 25 | ), 26 | count_short_headlines AS ( 27 | SELECT 28 | AGA.campaign_id, 29 | AGA.asset_group_id, 30 | COUNT(*) AS count_short_headlines 31 | FROM `{bq_dataset}.assetgroupasset` AS AGA 32 | WHERE AGA.asset_sub_type = 'HEADLINE' 33 | GROUP BY 1, 2 34 | ), 35 | count_long_headlines AS ( 36 | SELECT 37 | AGA.campaign_id, 38 | AGA.asset_group_id, 39 | COUNT(*) AS count_long_headlines 40 | FROM `{bq_dataset}.assetgroupasset` AS AGA 41 | WHERE AGA.asset_sub_type = 'LONG_HEADLINE' 42 | GROUP BY 1, 2 43 | ), 44 | count_descriptions AS ( 45 | SELECT 46 | AGA.campaign_id, 47 | AGA.asset_group_id, 48 | COUNT(*) AS count_descriptions 49 | FROM `{bq_dataset}.assetgroupasset` AS AGA 50 | WHERE AGA.asset_sub_type = 'DESCRIPTION' 51 | GROUP BY 1, 2 52 | ), 53 | count_short_descriptions AS ( 54 | SELECT 55 | AGA.campaign_id, 56 | AGA.asset_group_id, 57 | COUNT(*) AS count_short_descriptions 58 | FROM `{bq_dataset}.assetgroupasset` AS AGA 59 | WHERE AGA.asset_sub_type = 'DESCRIPTION' 60 | AND LENGTH(AGA.text_asset_text) <= 60 61 | GROUP BY 1, 2 62 | ) 63 | SELECT DISTINCT 64 | AGS.account_id, 65 | AGS.account_name, 66 | AGS.campaign_id, 67 | AGS.campaign_name, 68 | AGS.asset_group_id, 69 | AGS.asset_group_name, 70 | COALESCE(CH.count_headlines,0) AS count_headlines, 71 | COALESCE(CSH.count_short_headlines,0) AS count_short_headlines, 72 | COALESCE(CD.count_descriptions,0) AS count_descriptions, 73 | COALESCE(CSD.count_short_descriptions,0) AS count_short_descriptions, 74 | COALESCE(CLH.count_long_headlines,0) AS count_long_headlines 75 | FROM `{bq_dataset}.assetgroupsummary` AS AGS 76 | LEFT JOIN count_headlines AS CH 77 | ON CH.campaign_id = AGS.campaign_id 78 | AND CH.asset_group_id = AGS.asset_group_id 79 | LEFT JOIN count_short_headlines AS CSH 80 | ON CSH.campaign_id = AGS.campaign_id 81 | AND CSH.asset_group_id = AGS.asset_group_id 82 | LEFT JOIN count_descriptions AS CD 83 | ON CD.campaign_id = AGS.campaign_id 84 | AND CD.asset_group_id = AGS.asset_group_id 85 | LEFT JOIN count_short_descriptions AS CSD 86 | ON CSD.campaign_id = AGS.campaign_id 87 | AND CSD.asset_group_id = AGS.asset_group_id 88 | LEFT JOIN count_long_headlines AS CLH 89 | ON CLH.campaign_id = AGS.campaign_id 90 | AND CLH.asset_group_id = AGS.asset_group_id 91 | GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 92 | -------------------------------------------------------------------------------- /bq_queries/12-campaign_settings.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.campaign_settings` AS 16 | WITH 17 | TARGETS AS ( 18 | SELECT 19 | C.date, 20 | C.account_id, 21 | C.campaign_id, 22 | CASE 23 | WHEN C.campaign_mcv_troas IS NOT NULL THEN C.campaign_mcv_troas 24 | WHEN C.campaign_troas IS NOT NULL THEN C.campaign_troas 25 | WHEN C.bidding_strategy_mcv_troas IS NOT NULL THEN C.bidding_strategy_mcv_troas 26 | WHEN C.bidding_strategy_troas IS NOT NULL THEN C.bidding_strategy_troas 27 | END AS troas, 28 | CASE 29 | WHEN C.campaign_mc_tcpa IS NOT NULL THEN C.campaign_mc_tcpa 30 | WHEN C.campaign_tcpa IS NOT NULL THEN C.campaign_tcpa 31 | WHEN C.bidding_strategy_mc_tcpa IS NOT NULL THEN C.bidding_strategy_mc_tcpa 32 | WHEN C.bidding_strategy_tcpa IS NOT NULL THEN C.bidding_strategy_tcpa 33 | END AS tcpa 34 | FROM `{bq_dataset}.campaign_settings` C 35 | ), 36 | ASSET_GROUP_BEST_PRACTICES AS ( 37 | SELECT 38 | CAST(DATE(TIMESTAMP(CONCAT(SUBSTR(_TABLE_SUFFIX,0,4),'-',SUBSTR(_TABLE_SUFFIX,5,2),'-',SUBSTR(_TABLE_SUFFIX,7))))-1 AS STRING) AS date_, 39 | account_id, 40 | campaign_id, 41 | ROUND(AVG(video_score), 2) AS video_score, 42 | ROUND(AVG(text_score), 2) AS text_score, 43 | ROUND(AVG(image_score), 2) AS image_score 44 | FROM `{bq_dataset}_bq.assetgroupbpscore_*` 45 | GROUP BY 1,2,3 46 | ), 47 | CAMPAIGN_BEST_PRACTICES AS ( 48 | SELECT 49 | CAST(DATE(TIMESTAMP(CONCAT(SUBSTR(_TABLE_SUFFIX,0,4),'-',SUBSTR(_TABLE_SUFFIX,5,2),'-',SUBSTR(_TABLE_SUFFIX,7))))-1 AS STRING) AS date_, 50 | account_id, 51 | campaign_id, 52 | campaign_bp_score 53 | FROM `{bq_dataset}_bq.campaignbpscore_*` 54 | GROUP BY 1,2,3,4 55 | ) 56 | SELECT 57 | C.date, 58 | C.account_id, 59 | C.account_name, 60 | C.campaign_id, 61 | C.campaign_name, 62 | C.bidding_strategy, 63 | C.budget_amount, 64 | C.total_budget, 65 | C.budget_type, 66 | C.is_shared_budget, 67 | C.budget_period, 68 | T.troas, 69 | T.tcpa, 70 | C.gmc_id, 71 | C.optiscore, 72 | C.cost/1e6 AS cost, 73 | C.conversions, 74 | C.conversions_value, 75 | C.all_conversions_value, 76 | ABP.video_score, 77 | ABP.text_score, 78 | ABP.image_score, 79 | CBP.campaign_bp_score, 80 | OCID.ocid 81 | FROM `{bq_dataset}.campaign_settings` AS C 82 | LEFT JOIN TARGETS AS T 83 | USING(account_id, campaign_id, date) 84 | LEFT JOIN ASSET_GROUP_BEST_PRACTICES AS ABP 85 | ON C.date = ABP.date_ 86 | AND C.account_id = ABP.account_id 87 | AND C.campaign_id = ABP.campaign_id 88 | LEFT JOIN CAMPAIGN_BEST_PRACTICES AS CBP 89 | ON C.date = CBP.date_ 90 | AND C.account_id = CBP.account_id 91 | AND C.campaign_id = CBP.campaign_id 92 | LEFT JOIN `{bq_dataset}.ocid_mapping` AS OCID 93 | ON OCID.customer_id = C.account_id; 94 | -------------------------------------------------------------------------------- /bq_queries/01-image_assets.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE SCHEMA IF NOT EXISTS `{bq_dataset}_bq`; 16 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.image_assets` 17 | AS 18 | WITH count_image_assets AS ( 19 | SELECT 20 | campaign_id, 21 | asset_group_id, 22 | COUNT(*) AS count_images 23 | FROM 24 | `{bq_dataset}.assetgroupasset` 25 | WHERE asset_type = 'IMAGE' 26 | GROUP BY 1, 2 27 | ), 28 | count_logos AS ( 29 | SELECT 30 | campaign_id, 31 | asset_group_id, 32 | COUNT(*) AS count_logos 33 | FROM 34 | `{bq_dataset}.assetgroupasset` 35 | WHERE asset_sub_type = 'LOGO' 36 | GROUP BY 1, 2 37 | ), 38 | count_landscape_assets AS ( 39 | SELECT 40 | campaign_id, 41 | asset_group_id, 42 | COUNT(*) AS count_landscape 43 | FROM 44 | `{bq_dataset}.assetgroupasset` 45 | WHERE asset_sub_type = 'MARKETING_IMAGE' 46 | GROUP BY 1, 2 47 | ), 48 | count_portrait_assets AS ( 49 | SELECT 50 | campaign_id, 51 | asset_group_id, 52 | COUNT(*) AS count_portrait 53 | FROM 54 | `{bq_dataset}.assetgroupasset` 55 | WHERE asset_sub_type = 'PORTRAIT_MARKETING_IMAGE' 56 | GROUP BY 1, 2 57 | ), 58 | count_square_images AS ( 59 | SELECT 60 | campaign_id, 61 | asset_group_id, 62 | COUNT(*) AS count_square 63 | FROM `{bq_dataset}.assetgroupasset` 64 | WHERE asset_sub_type = 'SQUARE_MARKETING_IMAGE' 65 | GROUP BY 1, 2 66 | ), 67 | count_square_logos AS ( 68 | SELECT 69 | campaign_id, 70 | asset_group_id, 71 | COUNT(*) AS count_square_logos 72 | FROM `{bq_dataset}.assetgroupasset` 73 | WHERE image_width = image_height 74 | AND asset_sub_type = 'LOGO' 75 | GROUP BY 1, 2 76 | ), 77 | count_landscape_logos AS ( 78 | SELECT 79 | campaign_id, 80 | asset_group_id, 81 | COUNT(*) AS count_landscape_logos 82 | FROM `{bq_dataset}.assetgroupasset` 83 | WHERE ROUND(SAFE_DIVIDE(image_width, image_height),2) = 4 84 | AND asset_sub_type = 'LOGO' 85 | GROUP BY 1, 2 86 | ) 87 | SELECT DISTINCT 88 | AGS.account_id, 89 | AGS.account_name, 90 | AGS.campaign_id, 91 | AGS.campaign_name, 92 | AGS.asset_group_id, 93 | AGS.asset_group_name, 94 | COALESCE(CIA.count_images,0) AS count_images, 95 | COALESCE(CL.count_logos,0) AS count_logos, 96 | COALESCE(CLA.count_landscape,0) AS count_landscape, 97 | COALESCE(CSN.count_square,0) AS count_square, 98 | COALESCE(CPA.count_portrait,0) AS count_portrait, 99 | COALESCE(CSL.count_square_logos,0) AS count_square_logos, 100 | COALESCE(CRL.count_landscape_logos,0) AS count_landscape_logos, 101 | FROM `{bq_dataset}.assetgroupsummary` AS AGS 102 | LEFT JOIN count_image_assets AS CIA 103 | ON CIA.campaign_id = AGS.campaign_id 104 | AND CIA.asset_group_id = AGS.asset_group_id 105 | LEFT JOIN count_logos AS CL 106 | ON CL.campaign_id = AGS.campaign_id 107 | AND CL.asset_group_id = AGS.asset_group_id 108 | LEFT JOIN count_landscape_assets AS CLA 109 | ON CLA.campaign_id = AGS.campaign_id 110 | AND CLA.asset_group_id = AGS.asset_group_id 111 | LEFT JOIN count_square_images AS CSN 112 | ON CSN.campaign_id = AGS.campaign_id 113 | AND CSN.asset_group_id = AGS.asset_group_id 114 | LEFT JOIN count_portrait_assets AS CPA 115 | ON CPA.campaign_id = AGS.campaign_id 116 | AND CPA.asset_group_id = AGS.asset_group_id 117 | LEFT JOIN count_square_logos AS CSL 118 | ON CSL.campaign_id = AGS.campaign_id 119 | AND CSL.asset_group_id = AGS.asset_group_id 120 | LEFT JOIN count_landscape_logos AS CRL 121 | ON CRL.campaign_id = AGS.campaign_id 122 | AND CRL.asset_group_id = AGS.asset_group_id 123 | GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 124 | -------------------------------------------------------------------------------- /bq_queries/11-assetgroupbestpracticessnapshots.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.assetgroupbpscore_${format(today(),'yyyyMMdd')}` AS 16 | WITH video_data AS ( 17 | SELECT 18 | account_id, 19 | account_name, 20 | campaign_id, 21 | campaign_name, 22 | asset_group_id, 23 | asset_group_name, 24 | ad_strength, 25 | is_video_uploaded, 26 | count_videos, 27 | IF(count_videos < 3, CAST(0 AS FLOAT64), CAST(1 as FLOAT64)) AS video_score 28 | FROM `{bq_dataset}_bq.video_assets` 29 | ), 30 | text_data AS ( 31 | SELECT 32 | account_id, 33 | account_name, 34 | campaign_id, 35 | campaign_name, 36 | asset_group_id, 37 | count_descriptions, 38 | count_headlines, 39 | count_long_headlines, 40 | count_short_descriptions, 41 | count_short_headlines, 42 | (IF(count_descriptions >= 4, 1, 0) + 43 | IF(count_headlines >= 11, 1, 0) + 44 | IF(count_long_headlines >= 2, 1, 0)) / 3 AS text_score 45 | FROM `{bq_dataset}_bq.text_assets` 46 | ), 47 | image_data AS ( 48 | SELECT 49 | account_id, 50 | account_name, 51 | campaign_id, 52 | campaign_name, 53 | asset_group_id, 54 | count_images, 55 | count_logos, 56 | count_landscape, 57 | count_portrait, 58 | count_landscape_logos, 59 | count_square, 60 | count_square_logos, 61 | (IF(count_landscape >= 4, 1, 0) + 62 | IF(count_square >= 4, 1, 0) + 63 | IF(count_portrait >= 4, 1, 0) + 64 | IF(count_landscape_logos >= 1, 1, 0) + 65 | IF(count_square_logos >= 1, 1, 0)) / 5 AS image_score 66 | FROM `{bq_dataset}_bq.image_assets` 67 | ), 68 | audience_signals_count AS ( 69 | SELECT 70 | AGS.campaign_id AS campaign_id, 71 | AGS.asset_group_id, 72 | COUNT(DISTINCT AGS.asset_group_id) AS number_of_audience_signals, 73 | FROM `{bq_dataset}.assetgroupsignal` AGS 74 | GROUP BY 75 | campaign_id, 76 | asset_group_id 77 | ) 78 | SELECT 79 | CURRENT_DATE() AS date, 80 | AGS.account_id, 81 | AGS.account_name, 82 | AGS.campaign_id, 83 | AGS.campaign_name, 84 | AGS.asset_group_id, 85 | AGS.asset_group_name, 86 | AGS.ad_strength, 87 | V.is_video_uploaded, 88 | V.video_score, 89 | T.text_score, 90 | I.image_score, 91 | IF(T.count_descriptions < 4, 4 - T.count_descriptions, 0) AS missing_descriptions, 92 | IF(T.count_descriptions < 5, 5 - T.count_descriptions, 0) AS missing_descriptions_max, 93 | IF(T.count_headlines < 11, 11 - T.count_headlines, 0) AS missing_headlines, 94 | IF(T.count_headlines < 15, 15 - T.count_headlines, 0) AS missing_headlines_max, 95 | IF(T.count_long_headlines < 2, 2 - T.count_long_headlines, 0) AS missing_long_headlines, 96 | IF(T.count_long_headlines < 5, 5 - T.count_long_headlines, 0) AS missing_long_headlines_max, 97 | IF((I.count_landscape + I.count_square + I.count_portrait + I.count_logos) < 12, 12 - (I.count_landscape + I.count_square + I.count_portrait + I.count_logos), 0) AS missing_images, 98 | IF((I.count_landscape + I.count_square + I.count_portrait + I.count_logos) < 20, 20 - (I.count_landscape + I.count_square + I.count_portrait + I.count_logos), 0) AS missing_images_max, 99 | IF(I.count_landscape < 4, 4 - I.count_landscape, 0) AS missing_landscape, 100 | IF(I.count_square < 4, 4 - I.count_square, 0) AS missing_square, 101 | IF(I.count_portrait < 2, 2 - I.count_portrait, 0) AS missing_portrait, 102 | IF(I.count_logos < 2, 2 - I.count_logos, 0) AS missing_logos, 103 | IF(I.count_square_logos = 0, 1, 0) AS missing_square_logos, 104 | IF(I.count_landscape_logos = 0, 1, 0) AS missing_landscape_logos, 105 | IF(V.count_videos < 3, 3 - V.count_videos, 0) AS missing_video, 106 | IF(V.count_videos < 5, 5 - V.count_videos, 0) AS missing_video_max, 107 | IF(ASA.number_of_audience_signals IS NOT NULL,"Yes","X") AS audience_signals 108 | FROM `{bq_dataset}.assetgroupsummary` AGS 109 | INNER JOIN video_data AS V 110 | ON AGS.account_id = V.account_id 111 | AND AGS.campaign_id = V.campaign_id 112 | AND AGS.asset_group_id = V.asset_group_id 113 | INNER JOIN text_data AS T 114 | ON AGS.account_id = T.account_id 115 | AND AGS.campaign_id = T.campaign_id 116 | AND AGS.asset_group_id = T.asset_group_id 117 | INNER JOIN image_data AS I 118 | ON AGS.account_id = I.account_id 119 | AND AGS.campaign_id = I.campaign_id 120 | AND AGS.asset_group_id = I.asset_group_id 121 | LEFT JOIN audience_signals_count AS ASA 122 | ON AGS.campaign_id = ASA.campaign_id 123 | AND AGS.asset_group_id = ASA.asset_group_id 124 | WHERE AGS.campaign_id IN (SELECT campaign_id FROM `{bq_dataset}.campaign_settings`) -------------------------------------------------------------------------------- /bq_queries/10-assetgroupbestpractices.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.assetgroupbestpractices` AS 16 | WITH 17 | VIDEO_DATA AS ( 18 | SELECT 19 | account_id, 20 | account_name, 21 | campaign_id, 22 | campaign_name, 23 | asset_group_id, 24 | asset_group_name, 25 | ad_strength, 26 | is_video_uploaded, 27 | count_videos, 28 | IF(count_videos < 3, CAST(0 AS FLOAT64), CAST(1 as FLOAT64)) AS video_score 29 | FROM `{bq_dataset}_bq.video_assets` 30 | ), 31 | TEXT_DATA AS ( 32 | SELECT 33 | account_id, 34 | account_name, 35 | campaign_id, 36 | campaign_name, 37 | asset_group_id, 38 | count_descriptions, 39 | count_headlines, 40 | count_long_headlines, 41 | count_short_descriptions, 42 | count_short_headlines, 43 | (IF(count_descriptions >= 4, 1, 0) + 44 | IF(count_headlines >= 11, 1, 0) + 45 | IF(count_long_headlines >= 2, 1, 0)) / 3 AS text_score 46 | FROM `{bq_dataset}_bq.text_assets` 47 | ), 48 | IMAGE_DATA AS ( 49 | SELECT 50 | account_id, 51 | account_name, 52 | campaign_id, 53 | campaign_name, 54 | asset_group_id, 55 | count_images, 56 | count_logos, 57 | count_landscape, 58 | count_portrait, 59 | count_landscape_logos, 60 | count_square, 61 | count_square_logos, 62 | (IF(count_landscape >= 4, 1, 0) + 63 | IF(count_square >= 4, 1, 0) + 64 | IF(count_portrait >= 4, 1, 0) + 65 | IF(count_landscape_logos >= 1, 1, 0) + 66 | IF(count_square_logos >= 1, 1, 0)) / 5 AS image_score 67 | FROM `{bq_dataset}_bq.image_assets` 68 | ), 69 | AUDIENCE_SIGNALS_COUNT AS ( 70 | SELECT 71 | COUNT(DISTINCT AGS.asset_group_id) AS number_of_audience_signals, 72 | AGS.campaign_id AS campaign_id, 73 | AGS.asset_group_id 74 | FROM `{bq_dataset}.assetgroupsignal` AS AGS 75 | GROUP BY 76 | campaign_id, 77 | asset_group_id 78 | ) 79 | SELECT 80 | AGS.date, 81 | AGS.account_id, 82 | AGS.account_name, 83 | AGS.campaign_id, 84 | AGS.campaign_name, 85 | AGS.asset_group_id, 86 | AGS.asset_group_name, 87 | AGS.ad_strength, 88 | V.is_video_uploaded, 89 | V.video_score, 90 | T.text_score, 91 | I.image_score, 92 | "Recommended" AS recommendation_type, 93 | IF(T.count_descriptions < 4, 4 - T.count_descriptions, 0) AS missing_descriptions, 94 | IF(T.count_headlines < 11, 11 - T.count_headlines, 0) AS missing_headlines, 95 | IF(T.count_long_headlines < 2, 2 - T.count_long_headlines, 0) AS missing_long_headlines, 96 | IF((I.count_landscape + I.count_square + I.count_portrait + I.count_logos) < 12, 97 | 12 - (I.count_landscape + I.count_square + I.count_portrait + I.count_logos), 0) AS missing_images, 98 | IF(I.count_landscape < 4, 4 - I.count_landscape, 0) AS missing_landscape, 99 | IF(I.count_square < 4, 4 - I.count_square, 0) AS missing_square, 100 | IF(I.count_portrait < 2, 2 - I.count_portrait, 0) AS missing_portrait, 101 | IF(I.count_logos < 2, 2 - I.count_logos, 0) AS missing_logos, 102 | IF(I.count_square_logos = 0, 1, 0) AS missing_square_logos, 103 | IF(I.count_landscape_logos = 0, 1, 0) AS missing_landscape_logos, 104 | IF(V.count_videos < 3, 3 - V.count_videos, 0) AS missing_video, 105 | IF(ASA.number_of_audience_signals IS NOT NULL,"Yes","X") AS audience_signals, 106 | OCID.ocid 107 | FROM `{bq_dataset}.assetgroupsummary` AS AGS 108 | INNER JOIN VIDEO_DATA AS V 109 | ON AGS.account_id = V.account_id 110 | AND AGS.campaign_id = V.campaign_id 111 | AND AGS.asset_group_id = V.asset_group_id 112 | INNER JOIN TEXT_DATA AS T 113 | ON AGS.account_id = T.account_id 114 | AND AGS.campaign_id = T.campaign_id 115 | AND AGS.asset_group_id = T.asset_group_id 116 | INNER JOIN IMAGE_DATA AS I 117 | ON AGS.account_id = I.account_id 118 | AND AGS.campaign_id = I.campaign_id 119 | AND AGS.asset_group_id = I.asset_group_id 120 | LEFT JOIN AUDIENCE_SIGNALS_COUNT AS ASA 121 | ON AGS.campaign_id = ASA.campaign_id 122 | AND AGS.asset_group_id = ASA.asset_group_id 123 | LEFT JOIN `{bq_dataset}.ocid_mapping` AS OCID 124 | ON OCID.customer_id = AGS.account_id 125 | WHERE AGS.campaign_id IN (SELECT campaign_id FROM `{bq_dataset}.campaign_settings`) 126 | UNION ALL 127 | SELECT 128 | AGS.date, 129 | AGS.account_id, 130 | AGS.account_name, 131 | AGS.campaign_id, 132 | AGS.campaign_name, 133 | AGS.asset_group_id, 134 | AGS.asset_group_name, 135 | AGS.ad_strength, 136 | V.is_video_uploaded, 137 | V.video_score, 138 | T.text_score, 139 | I.image_score, 140 | "Maximum" AS recommendation_type, 141 | IF(T.count_descriptions < 5, 5 - T.count_descriptions, 0) AS missing_descriptions, 142 | IF(T.count_headlines < 15, 15 - T.count_headlines, 0) AS missing_headlines, 143 | IF(T.count_long_headlines < 5, 5 - T.count_long_headlines, 0) AS missing_long_headlines, 144 | IF((I.count_landscape + I.count_square + I.count_portrait + I.count_logos) < 20, 145 | 20 - (I.count_landscape + I.count_square + I.count_portrait + I.count_logos), 0) AS missing_images, 146 | IF(I.count_landscape < 4, 4 - I.count_landscape, 0) AS missing_landscape, 147 | IF(I.count_square < 4, 4 - I.count_square, 0) AS missing_square, 148 | IF(I.count_portrait < 2, 2 - I.count_portrait, 0) AS missing_portrait, 149 | IF(I.count_logos < 2, 2 - I.count_logos, 0) AS missing_logos, 150 | IF(I.count_square_logos = 0, 1, 0) AS missing_square_logos, 151 | IF(I.count_landscape_logos = 0, 1, 0) AS missing_landscape_logos, 152 | IF(V.count_videos < 5, 5 - V.count_videos, 0) AS missing_video, 153 | IF(ASA.number_of_audience_signals IS NOT NULL,"Yes","X") AS audience_signals, 154 | OCID.ocid 155 | FROM `{bq_dataset}.assetgroupsummary` AS AGS 156 | INNER JOIN VIDEO_DATA AS V 157 | ON AGS.account_id = V.account_id 158 | AND AGS.campaign_id = V.campaign_id 159 | AND AGS.asset_group_id = V.asset_group_id 160 | INNER JOIN TEXT_DATA AS T 161 | ON AGS.account_id = T.account_id 162 | AND AGS.campaign_id = T.campaign_id 163 | AND AGS.asset_group_id = T.asset_group_id 164 | INNER JOIN IMAGE_DATA AS I 165 | ON AGS.account_id = I.account_id 166 | AND AGS.campaign_id = I.campaign_id 167 | AND AGS.asset_group_id = I.asset_group_id 168 | LEFT JOIN AUDIENCE_SIGNALS_COUNT AS ASA 169 | ON AGS.campaign_id = ASA.campaign_id 170 | AND AGS.asset_group_id = ASA.asset_group_id 171 | LEFT JOIN `{bq_dataset}.ocid_mapping` AS OCID 172 | ON OCID.customer_id = AGS.account_id 173 | WHERE AGS.campaign_id IN (SELECT campaign_id FROM `{bq_dataset}.campaign_settings`); -------------------------------------------------------------------------------- /bq_queries/07-campaign_data.sql: -------------------------------------------------------------------------------- 1 | # Copyright 2022 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 | CREATE OR REPLACE TABLE `{bq_dataset}_bq.campaign_data` AS 16 | WITH 17 | TARGETS_RAW AS ( 18 | SELECT 19 | account_id, 20 | campaign_id, 21 | CASE 22 | WHEN campaign_mcv_troas IS NOT NULL THEN campaign_mcv_troas 23 | WHEN campaign_troas IS NOT NULL THEN campaign_troas 24 | WHEN bidding_strategy_mcv_troas IS NOT NULL THEN bidding_strategy_mcv_troas 25 | WHEN bidding_strategy_troas IS NOT NULL THEN bidding_strategy_troas 26 | END AS troas, 27 | CASE 28 | WHEN campaign_mc_tcpa IS NOT NULL THEN campaign_mc_tcpa 29 | WHEN campaign_tcpa IS NOT NULL THEN campaign_tcpa 30 | WHEN bidding_strategy_mc_tcpa IS NOT NULL THEN bidding_strategy_mc_tcpa 31 | WHEN bidding_strategy_tcpa IS NOT NULL THEN bidding_strategy_tcpa 32 | END AS tcpa 33 | FROM `{bq_dataset}.campaign_settings` 34 | ), 35 | TARGETS AS ( 36 | SELECT 37 | account_id, 38 | campaign_id, 39 | ANY_VALUE(troas) AS troas, 40 | ANY_VALUE(tcpa) AS tcpa 41 | FROM TARGETS_RAW 42 | GROUP BY 1, 2 43 | ), 44 | ASSET_GROUPS_COUNT AS ( 45 | SELECT 46 | COUNT(DISTINCT ASA.asset_group_id) AS number_of_asset_groups, 47 | ASA.campaign_id AS campaign_id 48 | FROM `{bq_dataset}.assetgroupasset` AS ASA 49 | GROUP BY campaign_id 50 | ), 51 | AUDIENCE_SIGNALS_COUNT AS ( 52 | SELECT 53 | COUNT(DISTINCT AGS.audience_signals) AS number_of_audience_signals, 54 | AGS.campaign_id AS campaign_id 55 | FROM `{bq_dataset}.assetgroupsignal` AS AGS 56 | GROUP BY 57 | campaign_id 58 | ), 59 | CAMPIAGN_SITELINKS_DATA AS ( 60 | SELECT 61 | account_id, 62 | account_name, 63 | campaign_id, 64 | campaign_name, 65 | COUNT(*) AS campaign_sitelinks, 66 | FROM `{bq_dataset}.campaignasset` 67 | WHERE asset_type = 'SITELINK' 68 | GROUP BY 1, 2, 3, 4 69 | ), 70 | ACCOUNT_SITELINKS_DATA AS ( 71 | SELECT 72 | account_id, 73 | account_name, 74 | COUNT(*) AS account_sitelinks, 75 | FROM `{bq_dataset}.customerasset` 76 | WHERE asset_type = 'SITELINK' 77 | GROUP BY 1, 2 78 | ), 79 | CAMPAIGN_CALLOUTS_DATA AS ( 80 | SELECT 81 | campaign_id, 82 | COUNT(*) AS campaign_callouts 83 | FROM `{bq_dataset}.campaignasset` 84 | WHERE asset_type = 'CALLOUT' 85 | GROUP BY 1 86 | ), 87 | ACCOUNT_CALLOUTS_DATA AS ( 88 | SELECT 89 | account_id, 90 | COUNT(*) AS account_callouts 91 | FROM `{bq_dataset}.customerasset` 92 | WHERE asset_type='CALLOUT' 93 | GROUP BY 1 94 | ), 95 | CAMPAIGN_STRUCTURED_SNIPPETS_DATA AS ( 96 | SELECT 97 | campaign_id, 98 | COUNT(*) AS campaign_structsnippets 99 | FROM `{bq_dataset}.campaignasset` 100 | WHERE asset_type = 'STRUCTURED_SNIPPET' 101 | GROUP BY 1 102 | ), 103 | ACCOUNT_STRUCTURED_SNIPPETS_DATA AS ( 104 | SELECT 105 | account_id, 106 | COUNT(*) AS account_structsnippets 107 | FROM `{bq_dataset}.customerasset` 108 | WHERE asset_type = 'STRUCTURED_SNIPPET' 109 | GROUP BY 1 110 | ) 111 | SELECT 112 | C.date, 113 | C.account_id, 114 | C.account_name, 115 | C.campaign_id, 116 | C.campaign_name, 117 | C.campaign_status, 118 | C.url_expansion_opt_out, 119 | C.bidding_strategy, 120 | C.budget_amount, 121 | C.total_budget, 122 | C.budget_type, 123 | C.is_shared_budget, 124 | C.budget_period, 125 | T.troas, 126 | T.tcpa, 127 | C.gmc_id, 128 | C.optiscore, 129 | C.cost/1e6 AS cost, 130 | C.conversions, 131 | C.conversions_value, 132 | PCA.conversion_name AS pmax_conversion, 133 | PCAS.conversion_name AS primary_conversion_search, 134 | C.negative_geo_target_type as negative_geo_target_type, 135 | C.positive_geo_target_type as positive_geo_target_type, 136 | IF (ASCC.number_of_audience_signals IS NOT NULL, ASCC.number_of_audience_signals, 0) AS number_of_audience_signals, 137 | AGC.number_of_asset_groups AS number_of_asset_groups, 138 | IF (C.negative_geo_target_type != 'PRESENCE_OR_INTEREST', "X", "Yes") as negative_geo_target_type_configured_good, 139 | IF (C.positive_geo_target_type != 'PRESENCE_OR_INTEREST', "X", "Yes") as positive_geo_target_type_configured_good, 140 | CASE 141 | WHEN (IF(SD.campaign_sitelinks IS NULL, 0, SD.campaign_sitelinks) 142 | + IF(ASD.account_sitelinks IS NULL, 0, ASD.account_sitelinks)) >= 4 143 | THEN 0 144 | ELSE 4 - (IF(SD.campaign_sitelinks IS NULL, 0, SD.campaign_sitelinks) 145 | + IF(ASD.account_sitelinks IS NULL, 0, ASD.account_sitelinks)) 146 | END AS missing_sitelinks, 147 | CASE 148 | WHEN (IF(CD.campaign_callouts IS NULL, 0, CD.campaign_callouts) 149 | + IF(ACD.account_callouts IS NULL, 0, ACD.account_callouts)) >= 1 150 | THEN 0 151 | ELSE 1 152 | END AS missing_callouts, 153 | CASE 154 | WHEN (IF(SSD.campaign_structsnippets IS NULL, 0, SSD.campaign_structsnippets) 155 | + IF(ASSD.account_structsnippets IS NULL, 0, ASSD.account_structsnippets)) >= 1 156 | THEN 0 157 | ELSE 1 158 | END AS missing_structured_snippets, 159 | CASE 160 | WHEN T.tcpa IS NOT NULL AND T.tcpa > 0 161 | THEN 'tCPA' 162 | WHEN T.troas IS NOT NULL AND T.troas > 0 163 | THEN 'tROAS' 164 | ELSE 'null' 165 | END AS target_type, 166 | CASE 167 | WHEN T.tcpa IS NOT NULL AND T.tcpa > 0 168 | THEN T.tcpa 169 | WHEN T.troas IS NOT NULL AND T.troas > 0 170 | THEN T.troas 171 | ELSE NULL 172 | END AS target_value, 173 | CASE 174 | WHEN ASCC.number_of_audience_signals IS NULL 175 | THEN 0 176 | ELSE (ASCC.number_of_audience_signals) / AGC.number_of_asset_groups 177 | END AS audience_signals_score, 178 | OCID.ocid 179 | FROM `{bq_dataset}.campaign_settings` C 180 | LEFT JOIN TARGETS AS T 181 | ON C.account_id = T.account_id 182 | AND C.campaign_id = T.campaign_id 183 | LEFT JOIN `{bq_dataset}_bq.primary_conversion_action_search` AS PCAS 184 | ON C.account_id = PCAS.account_id 185 | LEFT JOIN `{bq_dataset}_bq.primary_conversion_action_pmax` AS PCA 186 | ON PCA.account_id = C.account_id 187 | AND PCA.campaign_id = C.campaign_id 188 | LEFT JOIN ASSET_GROUPS_COUNT AS AGC 189 | ON C.campaign_id = AGC.campaign_id 190 | LEFT JOIN AUDIENCE_SIGNALS_COUNT AS ASCC 191 | ON C.campaign_id = ASCC.campaign_id 192 | LEFT JOIN CAMPIAGN_SITELINKS_DATA AS SD 193 | ON C.campaign_id = SD.campaign_id 194 | LEFT JOIN ACCOUNT_SITELINKS_DATA AS ASD 195 | ON C.account_id = ASD.account_id 196 | LEFT JOIN CAMPAIGN_CALLOUTS_DATA AS CD 197 | ON C.campaign_id = CD.campaign_id 198 | LEFT JOIN ACCOUNT_CALLOUTS_DATA AS ACD 199 | ON C.account_id = ACD.account_id 200 | LEFT JOIN CAMPAIGN_STRUCTURED_SNIPPETS_DATA AS SSD 201 | ON C.campaign_id = SSD.campaign_id 202 | LEFT JOIN ACCOUNT_STRUCTURED_SNIPPETS_DATA AS ASSD 203 | ON C.account_id = ASSD.account_id 204 | LEFT JOIN `{bq_dataset}.ocid_mapping` AS OCID 205 | ON OCID.customer_id = C.account_id; -------------------------------------------------------------------------------- /walkthrough.md: -------------------------------------------------------------------------------- 1 | # Deploying the pMaximizer 2 | 3 | 4 | 5 | 6 | 7 | 8 | ## Introduction 9 | 10 | In this walkthrough, you'll generate OAuth credentials in preparation for the deployment of pMaximizer. 11 | 12 | 13 | 14 | 15 | 16 | ## Google Cloud Project Setup 17 | 18 | GCP organizes resources into projects. This allows you to 19 | collect all of the related resources for a single application in one place. 20 | 21 | Begin by creating a new project or selecting an existing project for this 22 | dashboard. 23 | 24 | 25 | 26 | For details, see 27 | [Creating a project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project). 28 | 29 | ### Enable Google Cloud APIs 30 | 31 | Enable the Google Ads API and the BigQuery API so that they're incorporated in the credentials you will generate in the next step. 32 | 33 | 34 | 35 | 36 | 37 | ## Switch Off Ephemeral Mode 38 | 39 | First, let's switch off your shell's ephemeral mode. 40 | 41 | Click **More** and look for the `Ephemeral Mode` option. If it is turned on turn it off. This allows the dashboard code to persist across sessions. 42 | 43 | ## Authorize shell scripts commands 44 | 45 | Copy the following command into the shell, press enter and follow the instructions: 46 | ```bash 47 | cd 48 | git clone https://github.com/google/pmax_best_practices_dashboard.git 49 | cd pmax_best_practices_dashboard 50 | gcloud auth login 51 | ``` 52 | 53 | 54 | 55 | ## Configure OAuth Consent Screen 56 | 57 | An authorization token is needed for the dashboard to communicate with Google Ads. 58 | 59 | 1. Go to: 60 | **APIs & Services > OAuth Overview** 61 | 62 | 1. Click on "Get started". 63 | 64 | 1. Under *App information*, enter the **Application name** you want to display. 65 | You can copy the name below and enter it as the application name. 66 | 67 | ``` 68 | pMaximizer 69 | ``` 70 | 71 | 1. For the **Support email** dropdown menu, select any email address that you have access to. 72 | 73 | 1. In the Next section (Audience): 74 | 75 | * If you have an organization for your application, select **Internal**. 76 | * If you don't have an organization configured for your application, 77 | select **External**. 78 | 79 | 1. Under **Developer contact information**, enter a valid email address and continue to accept the Terms. 80 | 81 | 1. Click 82 | **Create** 83 | to continue. 84 | 85 | Click**Create OAuth client** 86 | 87 | ## Creating OAuth Credentials 88 | 89 | 1. You should land in the following section: 90 | **Clients** 91 | 92 | 1. Under 93 | **Application 94 | type**, select **Web application**. 95 | 96 | 1. Add a 97 | **Name** 98 | for your OAuth client ID. 99 | 100 | 1. Click Authorized redirect URI 101 | and copy the following: 102 | ``` 103 | https://developers.google.com/oauthplayground 104 | ``` 105 | 106 | 1. Click **Create**. Your OAuth client ID and client secret are generated and 107 | displayed on the OAuth client window. 108 | 109 | 1. Copy the **client_id** and **client_secret** as you will need those in a moment. (if you only see client_id, press OK and then find both id and secret after pressing the "edit" icon) 110 | 111 | ## Add Sensitive Scopes to Consent Screen 112 | 113 | 1. Go to: 114 | **Data Access** 115 | 116 | 1. Click Add or remove scopes 117 | 1. Now in Enter property name or value search for the BigQuery API, check the box for the first option to choose it. 118 | 1. Do the same for Google Ads API. 119 | 1. Click Update 120 | 121 | 122 | ## Generate Refresh Token 123 | 124 | 1. Go to the [OAuth2 Playground](https://developers.google.com/oauthplayground/#step1&scopes=https%3A//www.googleapis.com/auth/adwords&url=https%3A//&content_type=application/json&http_method=GET&useDefaultOauthCred=checked&oauthEndpointSelect=Google&oauthAuthEndpointValue=https%3A//accounts.google.com/o/oauth2/v2/auth&oauthTokenEndpointValue=https%3A//oauth2.googleapis.com/token&includeCredentials=unchecked&accessTokenType=bearer&autoRefreshToken=unchecked&accessType=offline&forceAprovalPrompt=checked&response_type=code) (opens in a new window) 125 | 2. On the right-hand pane, paste the client_id and client_secret in the appropriate fields ![paste credentials](https://services.google.com/fh/files/misc/pplayground_fields.png) 126 | 3. Then on the left hand side of the screen, click the blue **Authorize APIs** button ![Authorize APIs](https://services.google.com/fh/files/misc/authorize_apis.png) 127 | 128 | If you are prompted to authorize access, please choose your Google account that has access to Google Ads and approve. 129 | 130 | 5. Now, click the new blue button **Exchange authorization code for tokens** ![Exchange authorization code for tokens](https://services.google.com/fh/files/misc/exchange_authorization_code_for_token.png) 131 | 6. Finally, in the middle of the screen you'll see your refresh token on the last line. Copy it and save it for future reference. ![refresh_token](https://services.google.com/fh/files/misc/refresh_token.png) *Do not copy the quotation marks* 132 | 133 | ## Join Google Group to access dashboard template 134 | 135 | Use [this link](https://groups.google.com/g/pmax-dashboard-template-readers/) to access the group URL and click on "Join Group" 136 | 137 | ![join_group](https://services.google.com/fh/files/misc/join_group.png) 138 | 139 | ## Deploy Solution 140 | 141 | Run the following command and follow the steps: 142 | 143 | Make sure to have your developer token, your MCC ID and Merchant Center Id(*) on hand, in addition to the rest of the credentials generated in the previous steps. 144 | 145 | (*) if you would like to enhance your analysis for retail use cases. 146 | 147 | When prompted, choose N to enter credentials one by one. 148 | 149 | ```bash 150 | sh setup-wfs.sh 151 | ``` 152 | 153 | 154 | ## Conclusion 155 | 156 | Congratulations. You've set up the pMaximizer! 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pMaximizer (Formally pMax Best Practices Dashboard) 2 | 3 | In this README, you'll find: 4 | 5 | - [Problem Statement](#problem-statement) 6 | - [Solution](#solution) 7 | - [Installation](#installation) 8 | - [Prerequisites](#prerequisites) 9 | - [Deliverable (Implementation)](#deliverable-implementation) 10 | - [Architecture](#architecture) 11 | - [Troubleshooting / Q&A](#troubleshooting) 12 | - [Disclaimer](#disclaimer) 13 | 14 | ## Problem Statement 15 | 16 | Reporting for pMax campaigns is cumbersome and advertisers need a simple way to see an overview of their accounts and get a clear picture of their assets's performance in their pmax campaigns. 17 | 18 | ## Solution 19 | 20 | pMaximizer is a best practice dashboard that provides a centralized monitoring of the pMax campaigns' performance and the assets uploaded. Built in Looker Studio, It helps clearly identify if the campaigns and assets comply with the best practice guidelines and gives actionable insights to enhance asset groups' and feed quality. 21 | 22 | Moreover, assets' performance is displayed and conveniently presented so advertisers can refresh poorly performing assets. 23 | 24 | ## Deliverable (Implementation) 25 | 26 | A Looker Studio dashboard based on your Google Ads data. After joining the group below, [click here](https://lookerstudio.google.com/c/reporting/755d5896-5c56-4f5a-9075-79249137c9ea/page/i5YsC) to see it in action. 27 | 28 | [![pMaximizer](https://services.google.com/fh/files/misc/pmaximizer-screenshots.png)](https://lookerstudio.google.com/c/reporting/755d5896-5c56-4f5a-9075-79249137c9ea/page/i5YsC) 29 | 30 | - LookerStudio dashboard based on your Google Ads data. 31 | 32 | ## Prerequisites 33 | 34 | 1. Join [this group](https://groups.google.com/g/pmax-dashboard-template-readers/) 35 | 36 | 1. Obtain a Developer token 37 | 38 | a. This can be found in [Google Ads](ads.google.com) on the MCC level 39 | 40 | b. Go to Tools & Settings > Setup > API Center. If you do not see a developer token there, please complete the details and request one. 41 | 42 | c. By default, your access level is 'Test Account'; please apply for 'Basic access' if you don't have it. Follow [these instructions](https://developers.google.com/google-ads/api/docs/access-levels) 43 | 44 | 1. Create a new Google Cloud Project on the [Google Cloud Console](https://console.cloud.google.com/), **make sure it is connected to a billing account** 45 | 46 | ### Installation 47 | 48 | Click [this link](https://console.cloud.google.com/?cloudshell=true&cloudshell_git_repo=https://github.com/google/pmax_best_practices_dashboard&cloudshell_tutorial=walkthrough.md) to be redirected to a step-by-step Google Cloud tutorial on deploying pMaximizer. 49 | 50 | ### Update to the Latest Version 51 | 52 | To update the code and produce a new updated dashboard link, follow these steps. (If you wish to keep the same dashboard as you previously produced, you can, but in that case, only backend updates will be implemented.) 53 | 54 | 1. Enter the [Google Cloud Platform (GCP)](https://console.cloud.google.com/). 55 | 2. Make sure you’re in the project where you deployed the pMaximizer. 56 | 3. Activate the Cloud Shell by clicking on the "Activate Cloud Shell" icon on the upper right side of the screen: ![Activate Cloud Shell”](https://services.google.com/fh/files/misc/pmaximizer-impl-img5.png) 57 | 4. Execute (copy to the Cloud Shell and press Enter) the following commands in your Cloud Shell: 58 | 59 | ``` 60 | cd pmax_best_practices_dashboard 61 | ``` 62 | 63 | ``` 64 | git pull 65 | ``` 66 | 67 | ``` 68 | sh upgrade_pmaximizer.sh 69 | ``` 70 | 71 | Follow the link at the end of the deployment process to see access the upgraded dashboard, or use your previous link if you wish to only update the backend of the dashboard. 72 | 73 | ## Architecture 74 | 75 | ### What happens during installation 76 | 77 | ![What happens during installation](https://services.google.com/fh/files/misc/pmaximizer-arch-1.png) 78 | 79 | ### What Google Cloud components are deployed automatically 80 | 81 | ![What Google Cloud components are deployed automatically](https://services.google.com/fh/files/misc/pmaximizer-arch-2.png) 82 | 83 | ### In depth: Gaarf → Storage 84 | 85 | ![In depth: Gaarf → Storage](https://services.google.com/fh/files/misc/pmaximizer-arch-3.png) 86 | 87 | ### In depth: Gaarf → Scheduler + Workflow 88 | 89 | ![In depth: Gaarf → Scheduler + Workflow](https://services.google.com/fh/files/misc/pmaximizer-arch-4.png) 90 | 91 | ### In depth: Gaarf → Run 92 | 93 | ![In depth: Gaarf → Run](https://services.google.com/fh/files/misc/pmaximizer-arch-5.png) 94 | 95 | ### In depth: Gaarf → BigQuery 96 | 97 | ![In depth: Gaarf → BigQuery](https://services.google.com/fh/files/misc/pmaximizer-arch-6.png) 98 | 99 | ### What happens daily post installation 100 | 101 | ![What happens daily post installation](https://services.google.com/fh/files/misc/pmaximizer-arch-7.png) 102 | 103 | ## Troubleshooting 104 | 105 | ### What technical skills do I need to deploy the dashboard? 106 | 107 | You do not need any technical skills to deploy the dashboard as it’s fully driven by clicks and “copy and paste” commands. However, you do need the Owner level permission in the Google Cloud project you’re deploying it to. 108 | 109 | ### What Google Cloud components will be added to my project? 110 | 111 | - Storage 112 | - Scheduler 113 | - Workflows 114 | - Run 115 | - BigQuery (Datasets and Data Transfer) 116 | 117 | ### The deployment was not successful, what do I do? 118 | 119 | If the deployment was unsuccessful please follow these steps to try and troubleshoot: 120 | 121 | 1. Check that all credentials in the google-ads.yaml are correct: 122 | - In The Google Cloud Platform, under the project you deployed the pMax Dashboard too, Click the “Activate Cloud Shell” icon: ![“Activate Cloud Shell](https://services.google.com/fh/files/misc/pmaximizer-impl-img1.png) 123 | - Click the “Open Editor” icon: ![Open Editor](https://services.google.com/fh/files/misc/pmaximizer-impl-img2.png) 124 | - In the File System, find the pmax_best_practices directory. 125 | - In the pmax_best_practices directory, find the google-ads.yaml file and click on it. 126 | - Review the credentials in the google-ads.yaml file. Make sure that they are correct, and that there are no quotation marks before any credential. 127 | - Check that the login_customer_id is all digits, with no dashes (i.e: 123456789 and not 123-456-789) 128 | - If you find a mistake, edit it in place and be sure to save, and follow the next steps. If not, please refer to “How do I see logs from my deployment?” in the next section. 129 | - Click the “Open Terminal” icon: ![Open Terminal](https://services.google.com/fh/files/misc/pmaximizer-impl-img3.png) 130 | - In the Cloud shell, copy and paste the green code, and press the Enter key when specified: 131 | - `cd pmax_best_practices_dashboard` Press Enter 132 | - `sh upgrade_pmaximizer.sh` Press Enter 133 | - After the run finishes (may take 15-30 minutes) Check the dashboard URL to see if the deployment succeeded. (you can see instructions on how to find the dashboard URL in this document). 134 | 135 | ### How do I see the logs from my deployment? 136 | 137 | - In the Google Cloud Platform, under the project you deployed the pMax Dashboard too, click on the “Search” bar in the central upper part of the screen 138 | - Type “Logs Explorer” in the search bar and click on the following: 139 | ![Logs Explorer](https://services.google.com/fh/files/misc/pmaximizer-impl-img4.png) 140 | 141 | ### I lost the dashboard URL in the process, how can I access or find it? 142 | 143 | You can find the dashboard_url.txt file in the folder of the cloned repository or in your GCS bucket. Please see these instructions on how to access the URL through the cloud shell: 144 | 145 | - In The Google Cloud Platform, under the project you deployed too, Click the “Activate Cloud Shell” icon: ![Activate Cloud Shell”](https://services.google.com/fh/files/misc/pmaximizer-impl-img5.png) 146 | - In the Cloud shell, copy and paste the green code, and press the Enter key when specified: 147 | - Non-Retailers: 148 | - `cd pmax_best_practices_dashboard` Press Enter 149 | - `cat dashboard_url.txt` Press Enter 150 | - Retailers: 151 | - `cd pmax_best_practices_dashboard/work_retail` Press Enter 152 | - `cat dashboard_url.txt` Press Enter 153 | 154 | The dashboard URL should then appear in the Shell. 155 | 156 | ### What access level does my access token must have? 157 | 158 | Your Access Token has to have "Basic Access" or "Standard". Level "Test Account" will not work: 159 | 160 | ![Access Token Level](https://services.google.com/fh/files/misc/pmaximizer-impl-img6.png) 161 | 162 | ### How Do I save and share the Finished Dashboard with teammates? 163 | 164 | After clicking the dashboard URL for the first time, you will see the LookerStudio dashboard. In order to save and share it you need to follow these steps: 165 | 166 | - On the upper right side of the screen, click "Save and Share" 167 | - Review the Credentials permissions of the different data sources. If you would like to give other colleagues permission to view all parts of the dashboard, even if they don’t have permissions to the Google Cloud Project it was created in, you need to change the credentials to Owner’s. 168 | - To change the credentials to Owner’s, you need to click Edit on the very most right column: 169 | 170 | ![Edit Views / Owner Credentials](https://services.google.com/fh/files/misc/pmaximizer-impl-img7.png) 171 | 172 | - Click on Data Credentials: 173 | 174 | ![Owner Credentials](https://services.google.com/fh/files/misc/pmaximizer-impl-img8.png) 175 | 176 | - Choose Owner’s credentials, and then Update: 177 | 178 | ![Owner Credentials](https://services.google.com/fh/files/misc/pmaximizer-impl-img9.png) 179 | 180 | - Click Done on the upper right. 181 | - Do this for all data sources in the Dashboard. 182 | - Click “Save and Share” again, and “Acknowledge and save” 183 | - Click “Add to Report”. 184 | - On the upper right, Click Share: ![Share Dashboard](https://services.google.com/fh/files/misc/pmaximizer-impl-img10.png) to share with teammates. 185 | 186 | ### How much does it cost? 187 | It heavily depends on how much data you have and how often it's used. If you check the Architecture of Components section, there are 5 cloud components: Run, Scheduler, Workflows, Storage and BigQuery. For a large amount of data (e.g. thousands of accounts, campaigns and products), we do not expect more than 10-15 USD/month in Google Cloud, mainly driven by Big Query. 188 | 189 | ### How do I edit the dashboard? 190 | 191 | Please find this Looker Studio [tutorial](https://support.google.com/looker-studio/answer/9171315?hl=en). 192 | 193 | ### What Oauth credential user type should I choose? Internal or external? 194 | 195 | If you’re a google workspace user: Internal 196 | If you’re not a google-workspace user: External. You should be ok to use it in "Test mode" instead of requesting your app to be approved by Google. 197 | 198 | ### How do I modify the results I get the dates of the data being pulled / how do I modify the hour the data is getting pulled? 199 | 200 | You can modify the answers.json file. In the GCP, open the cloud shell. 201 | 202 | ### Can I create a dashboard on paused pMax campaigns that we ran in the past? 203 | 204 | Yes! You can just change the ads_macro.start_date in answers.json file (shown above) while deploying to a value that covers the dates where the campaigns were active. By default it sets start_date to 90 days ago. 205 | 206 | ### Can I deploy it in an existing Cloud Project or do I need to create a new one just for this dashboard? 207 | 208 | You can use an existing Project if you want to. However, please remember that the best practice for clients is to create a new project dedicated to this solution (or any new solution). 209 | 210 | ### Which GAQL queries are executed? 211 | 212 | Please refer to the folder google_ads_queries. 213 | 214 | ### Can I create a dashboard on paused pMax campaigns that we ran in the past? 215 | 216 | Yes! You can just change the ads_macro.start_date in answers.json file while deploying 217 | to a value that covers the dates where the campaigns were active. By default it sets start_date to 90 days ago. 218 | 219 | ### Can I deploy it in an existing Cloud Project or do I need to create a new one just for this dashboard? 220 | 221 | You can use an existing Project if you want to. However, please remember that the best practice for clients is to create a new project dedicated to this solution (or any new solution). 222 | 223 | ### How can I modify the dashboard from a non-retail version to a retail version? 224 | 225 | 1. Enter the [Google Cloud Platform (GCP)](https://console.cloud.google.com/). 226 | 2. Make sure you’re in the project you deployed the pMaximizer to. 227 | 3. Activate the Cloud Shell by clicking on the "Activate Cloud Shell" icon on the upper right side of the screen: ![Activate Cloud Shell”](https://services.google.com/fh/files/misc/pmaximizer-impl-img5.png) 228 | 4. Execute (copy to the cloud shell and press enter) the following commands in your Cloud Shell: 229 | 230 | ``` 231 | cd pmax_best_practices_dashboard 232 | ``` 233 | 234 | ``` 235 | git pull 236 | ``` 237 | 238 | ``` 239 | sh non_retail_to_retail_upgrade.sh 240 | ``` 241 | 242 | Follow the link at the end of the deployment process to access your new retail pMaximizer. 243 | 244 | **If you can’t find an answer for your question/a solution to your problem here, please reach out to pmax_bpdash@google.com.** 245 | 246 | ## Disclaimer 247 | 248 | ** This is not an officially supported Google product.** 249 | 250 | Copyright 2021 Google LLC. This solution, including any related sample code or data, is made available on an “as is,” “as available,” and “with all faults” basis, solely for illustrative purposes, and without warranty or representation of any kind. This solution is experimental, unsupported and provided solely for your convenience. Your use of it is subject to your agreements with Google, as applicable, and may constitute a beta feature as defined under those agreements. To the extent that you make any data available to Google in connection with your use of the solution, you represent and warrant that you have all necessary and appropriate rights, consents and permissions to permit Google to use and process that data. By using any portion of this solution, you acknowledge, assume and accept all risks, known and unknown, associated with its usage, including with respect to your deployment of any portion of this solution in your systems, or usage in connection with your business, if at all. 251 | --------------------------------------------------------------------------------