├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── marketing-analytics ├── README.md ├── activation │ ├── common-libs │ │ └── nodejs-common │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── RELEASE_NOTES.md │ │ │ ├── bin │ │ │ ├── apps_scripts.sh │ │ │ ├── bigquery.sh │ │ │ ├── google_ads.sh │ │ │ └── install_functions.sh │ │ │ ├── index.js │ │ │ ├── jasmine.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── src │ │ │ ├── apis │ │ │ ├── ads_data_hub.js │ │ │ ├── analytics.js │ │ │ ├── auth_client.js │ │ │ ├── base │ │ │ │ ├── ads_api_common.js │ │ │ │ ├── auth_restful_api.js │ │ │ │ ├── google_api_client.js │ │ │ │ └── restful_api_base.js │ │ │ ├── bigquery.js │ │ │ ├── cloud │ │ │ │ └── google_cloud_api.js │ │ │ ├── cloud_platform_apis.js │ │ │ ├── dfa_reporting.js │ │ │ ├── display_video.js │ │ │ ├── doubleclick_bidmanager.js │ │ │ ├── doubleclick_search.js │ │ │ ├── gmail.js │ │ │ ├── google_ads_api.js │ │ │ ├── index.js │ │ │ ├── measurement_protocol.js │ │ │ ├── measurement_protocol_ga4.js │ │ │ ├── search_ads.js │ │ │ ├── sendgrid.js │ │ │ ├── spreadsheets.js │ │ │ └── youtube.js │ │ │ └── components │ │ │ ├── automl.js │ │ │ ├── cloudfunctions_utils.js │ │ │ ├── firestore │ │ │ ├── access_base.js │ │ │ ├── data_access_object.js │ │ │ ├── datastore_mode_access.js │ │ │ ├── firestore_api.js │ │ │ ├── index.js │ │ │ └── native_mode_access.js │ │ │ ├── pubsub.js │ │ │ ├── scheduler.js │ │ │ ├── secret_manager.js │ │ │ ├── storage.js │ │ │ ├── utils.js │ │ │ └── vertex_ai.js │ ├── data-tasks-coordinator │ │ ├── .gcloudignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── config_task.json.template │ │ ├── deploy.sh │ │ ├── index.js │ │ ├── jasmine.json │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ │ ├── sentinel.js │ │ │ ├── sentinel_helper.js │ │ │ ├── task_config │ │ │ └── task_config_dao.js │ │ │ ├── task_log │ │ │ └── task_log_dao.js │ │ │ ├── task_manager.js │ │ │ ├── tasks │ │ │ ├── base_task.js │ │ │ ├── bigquery │ │ │ │ ├── bigquery_abstract_task.js │ │ │ │ ├── create_external_table.js │ │ │ │ ├── data_transfer_task.js │ │ │ │ ├── delete_bigquery_task.js │ │ │ │ ├── export_schema_task.js │ │ │ │ ├── export_task.js │ │ │ │ ├── load_task.js │ │ │ │ └── query_task.js │ │ │ ├── copy_gcs_task.js │ │ │ ├── download_task.js │ │ │ ├── error │ │ │ │ ├── error_handled_status.js │ │ │ │ └── retryable_error.js │ │ │ ├── external │ │ │ │ └── notify_task.js │ │ │ ├── gmc │ │ │ │ ├── gmc_webpage_fetcher.js │ │ │ │ └── gmc_xml_feed_to_jsonl_task.js │ │ │ ├── index.js │ │ │ ├── internal │ │ │ │ ├── speed_controlled_task.js │ │ │ │ └── status_check_task.js │ │ │ ├── knot_task.js │ │ │ ├── multiple_task.js │ │ │ ├── predict │ │ │ │ ├── automl_predict.js │ │ │ │ ├── base_predict.js │ │ │ │ └── vertex_predict.js │ │ │ ├── predict_task.js │ │ │ ├── query_adh_task.js │ │ │ ├── report │ │ │ │ ├── base_report.js │ │ │ │ ├── campaign_manager_report.js │ │ │ │ ├── doubleclick_bidmanager_report.js │ │ │ │ ├── doubleclick_search_report.js │ │ │ │ ├── general_api_result.js │ │ │ │ ├── googleads_report_api.js │ │ │ │ ├── googleads_report_helper.js │ │ │ │ ├── index.js │ │ │ │ └── search_ads_report.js │ │ │ └── report_task.js │ │ │ └── utils │ │ │ ├── adapter │ │ │ └── mermaid_flowchart.js │ │ │ ├── cronjob_helper.js │ │ │ └── node_loader │ │ │ ├── task_config_node_loader.js │ │ │ └── task_log_node_loader.js │ ├── gmp-googleads-connector │ │ ├── .gcloudignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── RELEASE_NOTES.md │ │ ├── config_api.json.template │ │ ├── deploy.sh │ │ ├── index.js │ │ ├── jasmine.json │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── sql │ │ │ └── visualization_views.sql │ │ ├── src │ │ │ ├── api_config │ │ │ │ └── api_config_dao.js │ │ │ ├── api_handlers │ │ │ │ ├── api_handler.js │ │ │ │ ├── cloud_function_invoke.js │ │ │ │ ├── cm_conversions_upload.js │ │ │ │ ├── ga4_measurement_protocol.js │ │ │ │ ├── ga_data_import.js │ │ │ │ ├── ga_measurement_protocol.js │ │ │ │ ├── google_ads_call_conversions_upload.js │ │ │ │ ├── google_ads_click_conversions_upload.js │ │ │ │ ├── google_ads_conversion_adjustments_upload.js │ │ │ │ ├── google_ads_customer_match_upload.js │ │ │ │ ├── google_ads_offline_userdata_job.js │ │ │ │ ├── index.js │ │ │ │ ├── pubsub_message_send.js │ │ │ │ ├── sa_conversions_insert.js │ │ │ │ ├── sftp_upload.js │ │ │ │ └── sheets_load_csv.js │ │ │ ├── api_lock │ │ │ │ └── api_lock_dao.js │ │ │ ├── tentacles.js │ │ │ ├── tentacles_file │ │ │ │ └── tentacles_file_dao.js │ │ │ ├── tentacles_helper.js │ │ │ └── tentacles_task │ │ │ │ └── tentacles_task_dao.js │ │ └── tutorials │ │ │ ├── campaign_manager_offline_conversions_uploading.md │ │ │ ├── data_source_reconnect_bigquery.md │ │ │ ├── google_analytics_measurement_protocol.md │ │ │ ├── images │ │ │ ├── cm_floodlight_report.png │ │ │ ├── cm_user_roles.png │ │ │ ├── cyborg │ │ │ │ └── Cyborg_start_installation.gif │ │ │ ├── datastudio │ │ │ │ ├── completed_dashboard.png │ │ │ │ ├── confirm_reconnect_table.png │ │ │ │ ├── edit_data_source.png │ │ │ │ ├── finish_editting.png │ │ │ │ ├── select_table.png │ │ │ │ └── unavailable_data_sources.png │ │ │ └── ga_measurement_protocol_result.gif │ │ │ └── install_tentacles_in_google_sheets.md │ └── sheets-based-installer │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bin │ │ └── cyborg.js │ │ ├── package-lock.json │ │ ├── package.json │ │ └── src │ │ ├── apps_script │ │ ├── 0_common │ │ │ ├── api_base.js │ │ │ ├── enhanced_sheet.js │ │ │ ├── explicit_auth.js │ │ │ ├── timezone.js │ │ │ └── utils.js │ │ ├── 1_gcp │ │ │ ├── api_keys.js │ │ │ ├── bigquery.js │ │ │ ├── bigquery_data_transfer.js │ │ │ ├── cloud_billing.js │ │ │ ├── cloud_functions.js │ │ │ ├── cloud_functions_invoker.js │ │ │ ├── cloud_resource_manager.js │ │ │ ├── cloud_scheduler.js │ │ │ ├── datastore.js │ │ │ ├── firestore.js │ │ │ ├── iam_credentials.js │ │ │ ├── logging.js │ │ │ ├── pub_sub.js │ │ │ ├── secret_manager.js │ │ │ ├── service_usage.js │ │ │ └── storage.js │ │ ├── 2_base │ │ │ ├── colab_solution.js │ │ │ ├── external_api.js │ │ │ ├── mojo.js │ │ │ └── prime_solution.js │ │ ├── 3_api │ │ │ ├── analytics.js │ │ │ ├── campaign_manager.js │ │ │ ├── display_video.js │ │ │ ├── doubleclick_bid_manager.js │ │ │ ├── drive.js │ │ │ ├── gmail.js │ │ │ ├── google_ads.js │ │ │ ├── search_ads.js │ │ │ └── sheets.js │ │ ├── 4_artifact │ │ │ ├── gcloud.js │ │ │ ├── sentinel.js │ │ │ └── tentacles.js │ │ ├── 5_sheet_element │ │ │ ├── data_table_sheet.js │ │ │ ├── mojo_constant.js │ │ │ ├── mojo_sheet.js │ │ │ ├── oauth.js │ │ │ ├── plain_sheet.js │ │ │ └── trigger_functions.js │ │ ├── 6_sheet │ │ │ ├── api_access_checker.js │ │ │ ├── colab_solution_sheet.js │ │ │ ├── file_to_storage.js │ │ │ ├── secret_manager_sheet.js │ │ │ ├── sentinel_cron_job.js │ │ │ ├── sentinel_mojo_sheet.js │ │ │ ├── sentinel_task_config.js │ │ │ ├── tentacles_api_config.js │ │ │ └── tentacles_mojo_sheet.js │ │ ├── appsscript.json │ │ └── pages │ │ │ ├── explicit_auth.html │ │ │ └── oauth.html │ │ └── dependency.json ├── integration │ └── crmint │ │ └── README.md ├── personalization │ ├── dataproc-recommendation │ │ └── README.md │ └── tensorflow-recommendation │ │ └── README.md ├── predicting │ ├── automl-tables-in-sheets │ │ ├── CONTRIBUTING │ │ ├── Code.gs │ │ ├── LICENSE │ │ ├── README.md │ │ ├── appsscript.json │ │ └── architecture_diagram.png │ ├── clustering │ │ └── README.md │ ├── future-customer-value-segments │ │ ├── .gitignore │ │ ├── .markdownlintrc │ │ ├── CONTRIBUTING.md │ │ ├── FoCVS-bq_metadata │ │ ├── FoCVS-csv_metadata │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bigquery_mod │ │ │ └── __init__.py │ │ ├── common │ │ │ └── __init__.py │ │ ├── fcvs_pipeline_bq.py │ │ ├── fcvs_pipeline_csv.py │ │ ├── focvs_automation.ipynb │ │ ├── images │ │ │ ├── repeat_cumulative_transactions_over_time.png │ │ │ └── repeat_transactions_over_time.png │ │ ├── install.sh │ │ ├── lifetimes_ext │ │ │ ├── __init__.py │ │ │ └── custom_beta_geo_beta_binom_fitter.py │ │ ├── pyenv-installer.sh │ │ ├── requirements.in │ │ ├── requirements.txt │ │ ├── requirements_local.in │ │ ├── requirements_local.txt │ │ ├── run_locally.sh │ │ ├── samples │ │ │ └── input_cdnow.csv │ │ └── setup.py │ ├── kfp_pipeline │ │ ├── Propensity_Pipeline.ipynb │ │ ├── README.md │ │ ├── base_sql.txt │ │ └── output_component │ │ │ └── component_file.txt │ ├── ml-data-windowing-pipeline │ │ ├── CONTRIBUTING │ │ ├── DataVisualizationPipeline.java │ │ ├── DataVisualizationPipelineOptions.java │ │ ├── DataVisualizationPipeline_metadata │ │ ├── GenerateFeaturesPipeline.java │ │ ├── GenerateFeaturesPipelineOptions.java │ │ ├── GenerateFeaturesPipeline_metadata │ │ ├── LICENSE │ │ ├── README.md │ │ ├── SessionBasedWindowPipeline.java │ │ ├── SessionBasedWindowPipelineOptions.java │ │ ├── SessionBasedWindowPipeline_metadata │ │ ├── SlidingWindowPipeline.java │ │ ├── SlidingWindowPipelineOptions.java │ │ ├── SlidingWindowPipeline_metadata │ │ ├── UserSessionPipeline.java │ │ ├── UserSessionPipelineOptions.java │ │ ├── UserSessionPipeline_metadata │ │ ├── cloud_build.json │ │ ├── feature │ │ │ ├── accumulator │ │ │ │ ├── AccumulatorOptions.java │ │ │ │ ├── AccumulatorType.java │ │ │ │ ├── AverageByTenureValueFeatureAccumulator.java │ │ │ │ ├── AverageValueFeatureAccumulator.java │ │ │ │ ├── CountValueFeatureAccumulator.java │ │ │ │ ├── FeatureAccumulator.java │ │ │ │ ├── FeatureAccumulatorFactory.java │ │ │ │ ├── MostFrequentValueFeatureAccumulator.java │ │ │ │ ├── ProportionValueFeatureAccumulator.java │ │ │ │ ├── RecentValueFeatureAccumulator.java │ │ │ │ ├── SumValueFeatureAccumulator.java │ │ │ │ └── WindowBasedFeatureAccumulator.java │ │ │ └── transform │ │ │ │ ├── CreateAccumulatorOptionsFn.java │ │ │ │ ├── CreateTableSchemaFn.java │ │ │ │ └── ExtractFeatureFn.java │ │ ├── model │ │ │ ├── Fact.java │ │ │ ├── Field.java │ │ │ ├── LookbackWindow.java │ │ │ ├── Session.java │ │ │ └── UserActivity.java │ │ ├── pom.xml │ │ └── transform │ │ │ ├── DateUtil.java │ │ │ ├── MapFactToTableRow.java │ │ │ ├── MapGATableRowToSession.java │ │ │ ├── MapSessionToFacts.java │ │ │ ├── MapSortedSessionsIntoLookbackWindows.java │ │ │ ├── MapSortedSessionsIntoSessionLookbackWindows.java │ │ │ ├── MapSortedSessionsIntoSlidingLookbackWindows.java │ │ │ ├── MapSortedSessionsToUserActivities.java │ │ │ ├── MapUserActivityToTableRow.java │ │ │ ├── MapUserIdToSession.java │ │ │ ├── SortSessionsByTime.java │ │ │ ├── SortedSessionsUtil.java │ │ │ └── ValidateGATableRow.java │ └── tensorflow-lifetime-value │ │ └── README.md └── understanding │ ├── bigquery-exports-queries │ ├── README.md │ └── google │ │ ├── dv360 │ │ ├── click_through_rate.sql │ │ ├── date_range.sql │ │ ├── dbm_data_dbm_device_type.sql │ │ ├── effective_cpa.sql │ │ ├── extract_variables_values_in_activity_file.sql │ │ └── performance_aggregated_click_n_conversion_rates.sql │ │ └── ga360 │ │ ├── 3_days_using_union.sql │ │ ├── avg_amount_of_money_per_session.sql │ │ ├── avg_bounce_rate_per_traffic_source.sql │ │ ├── avg_product_pageviews_by_non_purchasers.sql │ │ ├── avg_product_pageviews_by_purchaser_type.sql │ │ ├── avg_transactions_per_purchaser.sql │ │ ├── last_1095_days_using_table_suffix.sql │ │ ├── last_36_months_using_table_suffix.sql │ │ ├── last_3_years_plus_today_using_union_all_and_table_suffix │ │ ├── last_3_years_using_table_suffix.sql │ │ ├── sequence_of_hits.sql │ │ ├── specific_date_range_using_table_suffix.sql │ │ └── total_transactions_per_device.sql │ └── oculi │ └── README.md └── marketing-technology ├── README.md └── gcs-pixel-tracking ├── 00_prework ├── README.md └── docker │ ├── Dockerfile │ └── load_no_create.sh ├── 01_prep ├── create_targets.sh ├── data │ ├── domains.txt │ ├── events.txt │ ├── page_names.txt │ └── products.txt └── targets_sample.txt ├── 02_load ├── load_scaleup.sh └── vegeta-deployment.yaml ├── CONTRIBUTING ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | gdpr_deletion/ 3 | pending/ 4 | *.iml 5 | node_modules/ 6 | .idea/ 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google.com/conduct/). -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Gathers various non-official Google products gathered around the theme of running marketing workloads using Google Cloud Platform's products. 2 | 3 | There are two main categories: 4 | - Marketing Analytics: mostly aimed at marketing departments. 5 | - Marketing Technology: mostly aimed that the vertical. -------------------------------------------------------------------------------- /marketing-analytics/README.md: -------------------------------------------------------------------------------- 1 | Folders that gathers various tools useful to perform some marketing tasks: 2 | - bigquery-export-queries: a set of queries that you can run in BigQuery on raw data such as [Data Transfer Service](https://cloud.google.com/bigquery/transfer/) data for example. 3 | - activation: includes various tools to do marketing activation such as a connector to send big amount of data to Google Marketing Platform and Google Ads. -------------------------------------------------------------------------------- /marketing-analytics/activation/common-libs/nodejs-common/bin/bigquery.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2022 Google Inc. 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 | # http://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 | ####################################### 18 | # Checks whether the BigQuery object (table or view) exists. 19 | # Globals: 20 | # GCP_PROJECT 21 | # DATASET 22 | # BIGQUERY_LOG_TABLE 23 | # Arguments: 24 | # None 25 | ####################################### 26 | check_existence_in_bigquery() { 27 | bq show "${1}" >/dev/null 2>&1 28 | printf '%d' $? 29 | } 30 | 31 | ####################################### 32 | # Creates or updates the BigQuery view. 33 | # Globals: 34 | # GCP_PROJECT 35 | # DATASET 36 | # Arguments: 37 | # The name of view. 38 | # The query of view. 39 | ####################################### 40 | create_or_update_view() { 41 | local viewName viewQuery 42 | viewName="${1}" 43 | viewQuery=${2} 44 | local action="mk" 45 | if [[ $(check_existence_in_bigquery "${DATASET}.${viewName}") -eq 0 ]]; then 46 | action="update" 47 | fi 48 | bq "${action}" \ 49 | --use_legacy_sql=false \ 50 | --view "${viewQuery}" \ 51 | --project_id ${GCP_PROJECT} \ 52 | "${DATASET}.${viewName}" 53 | } 54 | -------------------------------------------------------------------------------- /marketing-analytics/activation/common-libs/nodejs-common/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Nodejs common library export file. 17 | */ 18 | 19 | 'use strict'; 20 | 21 | exports.api = require('./src/apis/index.js'); 22 | exports.cloudfunctions = require('./src/components/cloudfunctions_utils.js'); 23 | exports.automl = require('./src/components/automl.js'); 24 | exports.firestore = require('./src/components/firestore/index.js'); 25 | exports.pubsub = require('./src/components/pubsub.js'); 26 | exports.scheduler = require('./src/components/scheduler.js'); 27 | exports.secretmanager = require('./src/components/secret_manager.js'); 28 | exports.storage = require('./src/components/storage.js'); 29 | exports.utils = require('./src/components/utils.js'); 30 | exports.vertexai = require('./src/components/vertex_ai.js'); 31 | -------------------------------------------------------------------------------- /marketing-analytics/activation/common-libs/nodejs-common/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "tests", 3 | "spec_files": [ 4 | "**/*_test.js" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /marketing-analytics/activation/common-libs/nodejs-common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@google-cloud/nodejs-common", 3 | "version": "2.3.0", 4 | "description": "A NodeJs common library for solutions based on Cloud Functions", 5 | "author": "Google Inc.", 6 | "license": "Apache-2.0", 7 | "files": [ 8 | "bin/", 9 | "src/" 10 | ], 11 | "main": "index.js", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/GoogleCloudPlatform/cloud-for-marketing", 15 | "directory": "marketing-analytics/activation/common-libs/nodejs-common" 16 | }, 17 | "homepage": "https://github.com/GoogleCloudPlatform/cloud-for-marketing/blob/master/marketing-analytics/activation/common-libs/nodejs-common/README.md", 18 | "dependencies": { 19 | "@google-cloud/aiplatform": "^3.25.0", 20 | "@google-cloud/automl": "^4.0.1", 21 | "@google-cloud/bigquery": "^7.8.0", 22 | "@google-cloud/datastore": "^9.1.0", 23 | "@google-cloud/firestore": "^7.9.0", 24 | "@google-cloud/logging-winston": "^6.0.0", 25 | "@google-cloud/pubsub": "^4.5.0", 26 | "@google-cloud/storage": "^7.12.0", 27 | "@google-cloud/scheduler": "^4.3.0", 28 | "@google-cloud/secret-manager": "^5.6.0", 29 | "gaxios": "^6.7.0", 30 | "google-ads-nodejs-client": "16.0.0", 31 | "google-auth-library": "^9.11.0", 32 | "googleapis": "^140.0.1", 33 | "winston": "^3.13.1", 34 | "@grpc/grpc-js": "^1.11.1", 35 | "lodash": "^4.17.21" 36 | }, 37 | "devDependencies": { 38 | "jasmine": "^5.2.0" 39 | }, 40 | "scripts": { 41 | "test": "node node_modules/jasmine/bin/jasmine" 42 | }, 43 | "cloud-repo-tools": { 44 | "requiresKeyFile": true, 45 | "requiresProjectId": true 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /marketing-analytics/activation/common-libs/nodejs-common/src/apis/base/auth_restful_api.js: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google Inc. 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 | // http://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 | * @fileoverview A base class for RESTful API with authorization client. 17 | */ 18 | 19 | const { RestfulApiBase } = require('./restful_api_base'); 20 | const AuthClient = require('../auth_client.js'); 21 | 22 | /** 23 | * A RESTful API class with authorization client. 24 | */ 25 | class AuthRestfulApi extends RestfulApiBase { 26 | 27 | constructor(env = process.env, options = {}) { 28 | super(env); 29 | /** 30 | * `authClient` can be consumed by cloud client library as the auth 31 | * client. By passing this in, we can offer more flexible auth clients in 32 | * test cases for API client library and cloud client library in future. 33 | */ 34 | if (options.authClient) { 35 | this.auth = options.authClient; 36 | } 37 | } 38 | 39 | /** 40 | * Returns the Api scope for authorization. 41 | * @return {!Array} 42 | * @abstract 43 | */ 44 | getScope() { } 45 | 46 | /** 47 | * Gets the auth object. 48 | * @return {!Promise<{!OAuth2Client|!JWT|!Compute}>} 49 | */ 50 | async getAuth() { 51 | if (this.auth) return this.auth; 52 | this.authClient = new AuthClient(this.getScope(), this.env); 53 | await this.authClient.prepareCredentials(); 54 | this.auth = this.authClient.getDefaultAuth(); 55 | return this.auth; 56 | } 57 | 58 | /** 59 | * Returns HTTP headers. By default, it contains access token. 60 | * @return {object} HTTP headers. 61 | * @override 62 | */ 63 | async getDefaultHeaders() { 64 | const auth = await this.getAuth(); 65 | const headers = await auth.getRequestHeaders(); 66 | return Object.assign({}, super.getDefaultHeaders(), headers); 67 | } 68 | 69 | } 70 | 71 | module.exports = { AuthRestfulApi }; 72 | -------------------------------------------------------------------------------- /marketing-analytics/activation/common-libs/nodejs-common/src/apis/base/google_api_client.js: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google Inc. 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 | // http://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 | * @fileoverview A base class for Google Api client library class. 17 | */ 18 | const { google } = require('googleapis'); 19 | const { getLogger } = require('../../components/utils.js'); 20 | const { AuthRestfulApi } = require('./auth_restful_api.js'); 21 | 22 | /** 23 | * A Google Api client library class. 24 | */ 25 | class GoogleApiClient extends AuthRestfulApi { 26 | 27 | /** @constructor */ 28 | constructor(env = process.env, options = {}) { 29 | super(env, options); 30 | this.logger = getLogger('API.default'); 31 | } 32 | 33 | /** 34 | * Returns the Api version of the Api in the current library. 35 | * @return {string} 36 | * @abstract 37 | */ 38 | getVersion() { } 39 | 40 | /** 41 | * Returns the Api instance. 42 | * @return {!Promise} The Api instance. 43 | */ 44 | async getApiClient() { 45 | if (this.apiClient) return this.apiClient; 46 | this.logger.info(`Initialized ${this.constructor.name} instance.`); 47 | this.apiClient = google[this.googleApi]({ 48 | version: this.getVersion(), 49 | auth: await this.getAuth(), 50 | }); 51 | return this.apiClient; 52 | } 53 | } 54 | 55 | module.exports = { GoogleApiClient }; 56 | -------------------------------------------------------------------------------- /marketing-analytics/activation/common-libs/nodejs-common/src/apis/display_video.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Google DoubleClick Search Ads Conversions uploading on Google 17 | * API Client Library. 18 | */ 19 | 20 | 'use strict'; 21 | 22 | const { GoogleApiClient } = require('./base/google_api_client.js'); 23 | const { getLogger } = require('../components/utils.js'); 24 | 25 | const API_SCOPES = Object.freeze([ 26 | 'https://www.googleapis.com/auth/display-video', 27 | ]); 28 | const API_VERSION = 'v3'; 29 | 30 | /** 31 | * Display and Video 360 API v3 stub. 32 | * @see https://developers.google.com/display-video/api/reference/rest/v3 33 | * This is not the same to Reports Display & Video 360 API which is from Google 34 | * Bid Manager API. 35 | * @see https://developers.google.com/bid-manager/reference/rest 36 | */ 37 | class DisplayVideo extends GoogleApiClient { 38 | 39 | /** 40 | * @constructor 41 | * @param {!Object=} env The environment object to hold env 42 | * variables. 43 | */ 44 | constructor(env = process.env) { 45 | super(env); 46 | this.googleApi = 'displayvideo'; 47 | this.logger = getLogger('API.DV3API'); 48 | } 49 | 50 | /** @override */ 51 | getScope() { 52 | return API_SCOPES; 53 | } 54 | 55 | /** @override */ 56 | getVersion() { 57 | return API_VERSION; 58 | } 59 | 60 | } 61 | 62 | module.exports = { 63 | DisplayVideo, 64 | API_VERSION, 65 | API_SCOPES, 66 | }; 67 | -------------------------------------------------------------------------------- /marketing-analytics/activation/common-libs/nodejs-common/src/apis/youtube.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google Inc. 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 | // http://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 | * @fileoverview Youtube API Client Library. 17 | */ 18 | 19 | 'use strict'; 20 | 21 | const {google} = require('googleapis'); 22 | const { GoogleApiClient } = require('./base/google_api_client.js'); 23 | const { 24 | Schema$Channel, 25 | Schema$Video, 26 | Schema$CommentThread, 27 | Schema$Playlist, 28 | Schema$Search, 29 | } = google.youtube; 30 | const { getLogger } = require('../components/utils.js'); 31 | 32 | const API_SCOPES = Object.freeze([ 33 | 'https://www.googleapis.com/auth/youtube.force-ssl' 34 | ]); 35 | const API_VERSION = 'v3'; 36 | 37 | /** 38 | * Youtube API v3 stub. 39 | * See: https://developers.google.com/youtube/v3/docs 40 | */ 41 | class YouTube extends GoogleApiClient { 42 | /** 43 | * @constructor 44 | * @param {!Object=} env The environment object to hold env 45 | * variables. 46 | */ 47 | constructor(env = process.env) { 48 | super(env); 49 | this.googleApi = 'youtube'; 50 | this.logger = getLogger('API.YT'); 51 | } 52 | 53 | /** @override */ 54 | getScope() { 55 | return API_SCOPES; 56 | } 57 | 58 | /** @override */ 59 | getVersion() { 60 | return API_VERSION; 61 | } 62 | } 63 | 64 | module.exports = { 65 | YouTube, 66 | API_VERSION, 67 | API_SCOPES, 68 | }; 69 | -------------------------------------------------------------------------------- /marketing-analytics/activation/common-libs/nodejs-common/src/components/firestore/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Firestore data access module. 17 | */ 18 | 19 | 'use strict'; 20 | 21 | /** 22 | * @const{{ 23 | * DataSource:!DataSource, 24 | * Entity:!Entity, 25 | * Transaction:!Transaction, 26 | * Filter:!Filter, 27 | * TransactionOperation:!TransactionOperation, 28 | * DatastoreModeAccess:!DatastoreModeAccess, 29 | * FirestoreAccessBase:!FirestoreAccessBase, 30 | * NativeModeAccess:!NativeModeAccess, 31 | * DatastoreModeAccess:!DatastoreModeAccess, 32 | * }} 33 | */ 34 | module.exports = Object.assign({ 35 | NativeModeAccess: require('./native_mode_access.js'), 36 | DatastoreModeAccess: require('./datastore_mode_access.js'), 37 | DataAccessObject: require('./data_access_object.js'), 38 | }, require('./access_base.js')); 39 | -------------------------------------------------------------------------------- /marketing-analytics/activation/common-libs/nodejs-common/src/components/secret_manager.js: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google Inc. 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 | // http://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 | * @fileoverview Secret Manager wrapper class. 17 | */ 18 | 19 | const { GoogleAuthOptions } = require('google-auth-library'); 20 | const { SecretManagerServiceClient } = require('@google-cloud/secret-manager'); 21 | 22 | /** 23 | * Google Cloud Secret Manager class based on cloud client library. 24 | * @see https://cloud.google.com/nodejs/docs/reference/secret-manager/latest 25 | * @see https://cloud.google.com/secret-manager/docs/reference/libraries 26 | */ 27 | class SecretManager { 28 | /** 29 | * @constructor 30 | * @param {GoogleAuthOptions=} options 31 | */ 32 | constructor(options = {}) { 33 | if (!options.projectId) { 34 | options.projectId = process.env['GCP_PROJECT']; 35 | } 36 | this.client = new SecretManagerServiceClient(options); 37 | } 38 | 39 | async access(secret, version = 'latest') { 40 | const projectId = await this.client.getProjectId(); 41 | const name = `projects/${projectId}/secrets/${secret}/versions/${version}`; 42 | try { 43 | const [secretObj] = await this.client.accessSecretVersion({ name }); 44 | return secretObj.payload.data.toString('utf8'); 45 | } catch (error) { 46 | if (error.details.indexOf('not found') > -1) { 47 | return; 48 | } 49 | throw error; 50 | } 51 | } 52 | } 53 | 54 | exports.SecretManager = SecretManager; 55 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | 10 | .git 11 | .gitignore 12 | .gcloudignore 13 | .idea 14 | .history 15 | *.template 16 | *.sh 17 | *.iml 18 | **/.DS_Store 19 | config*.json 20 | *.md 21 | node_modules 22 | jasmine.json 23 | package-lock.json 24 | tests 25 | keys/* 26 | !keys/oauth2.token.json 27 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/config_task.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "load_job": { 3 | "type": "load", 4 | "source": { 5 | "bucket": "${file.bucket}", 6 | "name": "${file.name}", 7 | "fileNamePattern": "[ANY_STRING_PATTERN_IN_FILENAME]" 8 | }, 9 | "destination": { 10 | "table": { 11 | "projectId": "[YOUR_CLOUD_PROJECT_ID]", 12 | "datasetId": "[YOUR_BIGQUERY_DATASET_ID]", 13 | "tableId": "[YOUR_BIGQUERY_TABLE_ID]" 14 | }, 15 | "tableSchema": { 16 | "schema": { 17 | "fields": [ 18 | { 19 | "mode": "NULLABLE", 20 | "name": "[YOUR_BIGQUERY_TABLE_COLUMN_1_NAME]", 21 | "type": "[YOUR_BIGQUERY_TABLE_COLUMN_1_TYPE]" 22 | }, 23 | { 24 | "mode": "NULLABLE", 25 | "name": "[YOUR_BIGQUERY_TABLE_COLUMN_2_NAME]", 26 | "type": "[YOUR_BIGQUERY_TABLE_COLUMN_2_TYPE]" 27 | } 28 | ] 29 | } 30 | } 31 | }, 32 | "options": { 33 | "sourceFormat": "CSV", 34 | "writeDisposition": "WRITE_TRUNCATE", 35 | "skipLeadingRows": 1, 36 | "autodetect": false 37 | }, 38 | "next": [ 39 | "query_job_sql" 40 | ] 41 | }, 42 | "query_job_sql": { 43 | "type": "query", 44 | "source": { 45 | "sql": "[YOUR_QUERY_SQL]" 46 | }, 47 | "destination": { 48 | "table": { 49 | "projectId": "[YOUR_CLOUD_PROJECT_ID]", 50 | "datasetId": "[YOUR_BIGQUERY_DATASET_ID]", 51 | "tableId": "[YOUR_BIGQUERY_TABLE_ID]" 52 | }, 53 | "writeDisposition": "WRITE_TRUNCATE" 54 | }, 55 | "next": "export_job" 56 | }, 57 | "query_job_gcs": { 58 | "type": "query", 59 | "source": { 60 | "file": { 61 | "bucket": "[YOUR_BUCKET_FOR_SQL_FILE]", 62 | "name": "[YOUR_SQL_FILE_FULL_PATH_NAME]" 63 | } 64 | }, 65 | "destination": { 66 | "table": { 67 | "projectId": "[YOUR_CLOUD_PROJECT_ID]", 68 | "datasetId": "[YOUR_BIGQUERY_DATASET_ID]", 69 | "tableId": "[YOUR_BIGQUERY_TABLE_ID]" 70 | }, 71 | "writeDisposition": "WRITE_TRUNCATE" 72 | } 73 | }, 74 | "export_job": { 75 | "type": "export", 76 | "source": { 77 | "projectId": "[YOUR_CLOUD_PROJECT_ID]", 78 | "datasetId": "[YOUR_BIGQUERY_DATASET_ID]", 79 | "tableId": "[YOUR_BIGQUERY_TABLE_ID]" 80 | }, 81 | "destination": { 82 | "bucket": "[YOUR_BUCKET_FOR_EXPORTED_FILE]", 83 | "name": "[YOUR_FULL_PATH_NAME_FOR_EXPORTED_FILE]" 84 | }, 85 | "options": { 86 | "destinationFormat": "NEWLINE_DELIMITED_JSON", 87 | "printHeader": false 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "tests", 3 | "spec_files": [ 4 | "**/*_test.js" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@google-cloud/data-tasks-coordinator", 3 | "version": "2.2.0", 4 | "description": "A data task coordinator based on Cloud Functions", 5 | "author": "Google Inc.", 6 | "license": "Apache-2.0", 7 | "files": [ 8 | "deploy.sh", 9 | "src/" 10 | ], 11 | "config": { 12 | "runtime": "nodejs18", 13 | "availableMemoryMb": "2048" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/GoogleCloudPlatform/cloud-for-marketing.git", 18 | "directory": "marketing-analytics/activation/data-tasks-coordinator" 19 | }, 20 | "homepage": "https://github.com/GoogleCloudPlatform/cloud-for-marketing/blob/master/marketing-analytics/activation/data-tasks-coordinator/README.md", 21 | "main": "index.js", 22 | "dependencies": { 23 | "@google-cloud/automl": "^4.0.1", 24 | "@google-cloud/bigquery": "^7.8.0", 25 | "@google-cloud/bigquery-data-transfer": "^4.3.0", 26 | "@google-cloud/firestore": "^7.9.0", 27 | "@google-cloud/nodejs-common": "^2.3.0", 28 | "@google-cloud/storage": "^7.12.0", 29 | "google-ads-api": "14.2.0", 30 | "google-ads-nodejs-client": "16.0.0", 31 | "google-auth-library": "^9.11.0", 32 | "jsdom": "^21.1.0", 33 | "lodash": "^4.17.21", 34 | "luxon": "^3.4.2", 35 | "nanoid": "^3.3.4", 36 | "xml-js": "^1.6.11" 37 | }, 38 | "devDependencies": { 39 | "@google-cloud/functions-framework": "^3.4.0", 40 | "jasmine": "^5.2.0" 41 | }, 42 | "scripts": { 43 | "test": "node node_modules/jasmine/bin/jasmine", 44 | "startReportFunction": "npx functions-framework --target=reportWorkflow" 45 | }, 46 | "cloud-repo-tools": { 47 | "requiresKeyFile": true, 48 | "requiresProjectId": true 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/src/tasks/bigquery/delete_bigquery_task.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Delete dataset task class. 17 | */ 18 | 19 | 'use strict'; 20 | 21 | const { Dataset } = require('@google-cloud/bigquery'); 22 | const { BigQueryAbstractTask } = require('./bigquery_abstract_task.js'); 23 | const { 24 | TaskType, 25 | BigQueryTableConfig 26 | } = require('../../task_config/task_config_dao.js'); 27 | 28 | /** 29 | * @typedef {{ 30 | * type:TaskType.DELETE_BIGQUERY, 31 | * source:!BigQueryTableConfig, 32 | * appendedParameters:(Object|undefined), 33 | * next:(string|!Array|undefined), 34 | * }} 35 | */ 36 | let DeleteBigQueryTaskConfig; 37 | 38 | /** BigQuery delete dataset/table task. */ 39 | class DeleteBigQueryTask extends BigQueryAbstractTask { 40 | 41 | /** @override */ 42 | getBigQueryForTask() { 43 | /** @const {BigQueryTableConfig} */ 44 | const sourceTable = this.config.source; 45 | return this.getBigQuery(sourceTable); 46 | } 47 | 48 | /** 49 | * Deletes the given dataset. 50 | * @override 51 | */ 52 | async doTask() { 53 | /** @const {BigQueryTableConfig} */ const source = this.config.source; 54 | /** @const {Dataset} */ 55 | const dataset = this.getBigQueryForTask().dataset(source.datasetId); 56 | if (source.tableId) { 57 | const table = dataset.table(source.tableId); 58 | const [tableExists] = await table.exists(); 59 | if (tableExists) { 60 | await table.delete({ ignoreNotFound: true }); 61 | this.logger.info(`Deleted table ${source.tableId}`); 62 | } else { 63 | this.logger.info( 64 | `Table [${source.tableId}] to be deleted doesn't exist.`); 65 | } 66 | } else { 67 | await dataset.delete({ force: true }); 68 | this.logger.info(`Deleted dataset ${source.datasetId}`); 69 | } 70 | } 71 | 72 | /** @override */ 73 | async isDone() { 74 | return true; 75 | } 76 | 77 | /** @override */ 78 | completeTask() { 79 | return {}; 80 | } 81 | } 82 | 83 | module.exports = { 84 | DeleteBigQueryTaskConfig, 85 | DeleteBigQueryTask, 86 | }; 87 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/src/tasks/bigquery/export_schema_task.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Export task class. 17 | */ 18 | 19 | 'use strict'; 20 | 21 | const {Table} = require('@google-cloud/bigquery'); 22 | const {File} = require('@google-cloud/storage'); 23 | const {BigQueryAbstractTask} = require('./bigquery_abstract_task.js'); 24 | const { 25 | TaskType, 26 | BigQueryTableConfig, 27 | StorageFileConfig, 28 | } = require('../../task_config/task_config_dao.js'); 29 | 30 | /** 31 | * @typedef {{ 32 | * type:TaskType.EXPORT_SCHEMA, 33 | * source:!BigQueryTableConfig, 34 | * destination:!StorageFileConfig, 35 | * appendedParameters:(Object|undefined), 36 | * next:(string|!Array|undefined), 37 | * }} 38 | */ 39 | let ExportSchemaTaskConfig; 40 | 41 | /** BigQuery get table schema to Cloud Storage task class. */ 42 | class ExportSchemaTask extends BigQueryAbstractTask { 43 | 44 | /** @override */ 45 | getBigQueryForTask() { 46 | /** @const {BigQueryTableConfig} */ 47 | const sourceTable = this.config.source; 48 | return this.getBigQuery(sourceTable); 49 | } 50 | 51 | /** 52 | * Exports a BigQuery Table schema into Cloud Storage file. 53 | * @override 54 | */ 55 | async doTask() { 56 | /** @const {BigQueryTableConfig} */ const source = this.config.source; 57 | /** @const {StorageFileConfig} */ 58 | const destination = this.config.destination; 59 | /** @const {Table} */ 60 | const sourceTable = this.getBigQueryForTask() 61 | .dataset(source.datasetId) 62 | .table(source.tableId); 63 | /** @const {File} */ 64 | const file = this.getStorage(destination) 65 | .bucket(destination.bucket) 66 | .file(destination.name); 67 | const [{metadata: {schema}}] = await sourceTable.get(); 68 | this.logger.debug('Get schema of ', source); 69 | this.logger.debug(schema); 70 | await file.save(JSON.stringify(schema)); 71 | return { 72 | parameters: this.appendParameter({schemaFile: destination}), 73 | }; 74 | } 75 | 76 | /** @override */ 77 | async isDone() { 78 | return true; 79 | } 80 | } 81 | 82 | module.exports = { 83 | ExportSchemaTaskConfig, 84 | ExportSchemaTask, 85 | }; 86 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/src/tasks/download_task.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this fileAccessObject except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://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 | * @fileoverview Download task class. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const { request } = require('gaxios'); 21 | const { BaseTask } = require('./base_task.js'); 22 | const { storage: { StorageFile } } = require('@google-cloud/nodejs-common'); 23 | 24 | /** 25 | * 26 | * @typedef {{ 27 | * type:TaskType.DOWNLOAD, 28 | * source: { 29 | * url: string, 30 | * userAgent: string|undefined, 31 | * }, 32 | * destination: !StorageFileConfig, 33 | * appendedParameters:(Object|undefined), 34 | * next:(string|!Array|undefined), 35 | * }} 36 | */ 37 | let DownloadTaskConfig; 38 | 39 | /** Download task class. */ 40 | class DownloadTask extends BaseTask { 41 | 42 | async doTask() { 43 | const content = await this.getContent_(this.parameters); 44 | /** @const {StorageFileConfig} */ 45 | const destination = this.config.destination; 46 | const { bucket, name } = destination; 47 | const storageFile = StorageFile.getInstance( 48 | bucket, 49 | name, 50 | { 51 | projectId: destination.projectId, 52 | keyFilename: destination.keyFilename, 53 | }); 54 | await storageFile.getFile().save(content); 55 | return { parameters: this.appendParameter({ destination }) }; 56 | } 57 | 58 | /** @override */ 59 | async isDone() { 60 | return true; 61 | } 62 | 63 | /** @override */ 64 | async getContent_(parameters) { 65 | const { url, userAgent } = this.config.source; 66 | const requestOptions = { url }; 67 | if (userAgent) { 68 | requestOptions.headers = { 'User-Agent': userAgent }; 69 | } 70 | const response = await request(requestOptions); 71 | return response.data; 72 | } 73 | 74 | } 75 | 76 | module.exports = { 77 | DownloadTaskConfig, 78 | DownloadTask, 79 | }; 80 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/src/tasks/error/error_handled_status.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Errors handled status. 17 | */ 18 | 19 | /** 20 | * Status of how errors handled. 21 | * @enum {string} 22 | */ 23 | const ErrorHandledStatus = Object.freeze({ 24 | // Retried the task for this error. 25 | RETRIED: 'RETRIED', 26 | // The workflow fails at this error. 27 | FAILED: 'FAILED', 28 | // The workflow continues ignoring this error. 29 | IGNORED: 'IGNORED', 30 | }); 31 | 32 | module.exports = { ErrorHandledStatus }; 33 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/src/tasks/error/retryable_error.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Retryable error class. 17 | */ 18 | 19 | /** 20 | * Tasks throw this error to let Sentinel retry this task. 21 | */ 22 | class RetryableError extends Error { 23 | 24 | } 25 | 26 | module.exports = { 27 | RetryableError, 28 | }; 29 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/src/tasks/predict/base_predict.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this fileAccessObject except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://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 | * @fileoverview Interface for a ML batch prediction task. 16 | */ 17 | 18 | 'use strict'; 19 | const { 20 | TaskType, 21 | BigQueryTableConfig, 22 | StorageFileConfig, 23 | } = require('../../task_config/task_config_dao.js'); 24 | 25 | /** 26 | * Types of prediction services. 27 | * @enum {string} 28 | */ 29 | const PREDICTION_SERVICE = Object.freeze({ 30 | AUTOML_TABLES: 'automl', 31 | VERTEX_AI: 'vertex', 32 | }); 33 | 34 | /** 35 | * @typedef {{ 36 | * type:TaskType.PREDICT, 37 | * model:{ 38 | * api:(PREDICTION_SERVICE|undefined), 39 | * projectId:string, 40 | * location:string, 41 | * modelId:string, 42 | * displayName:(string|undefined), 43 | * }, 44 | * source:{ 45 | * format:(string|undefined), 46 | * table:(!BigQueryTableConfig|undefined), 47 | * file:(!StorageFileConfig|undefined), 48 | * }, 49 | * destination:{ 50 | * format:(string|undefined), 51 | * table:!BigQueryTableConfig, 52 | * file:(!StorageFileConfig|undefined), 53 | * }, 54 | * appendedParameters:(Object|undefined), 55 | * next:(string|!Array|undefined), 56 | * }} 57 | */ 58 | let PredictTaskConfig; 59 | 60 | /** 61 | * The base class for a predict service what will be used in PredictTask to 62 | * do batch prediction. 63 | * @abstract 64 | */ 65 | class Predict { 66 | 67 | /** 68 | * Starts a BatchPredictionJob and returns the job name. 69 | * @param {ReportConfig} config 70 | * @return {string} Batch prediction job name 71 | */ 72 | batchPredict(config) {} 73 | 74 | /** 75 | * Returns whether the given prediction job is completed. 76 | * @param {string} jobName 77 | * @return {boolean} 78 | */ 79 | isPredictDone(jobName) {} 80 | 81 | /** 82 | * Returns the output results of a predict job. 83 | * @param {string} jobName 84 | * @return {{ 85 | * bucket: string, 86 | * name: string, 87 | * }|{ 88 | * projectId: string, 89 | * datasetId: string, 90 | * tableId: string 91 | * }} 92 | */ 93 | getPredictOutput(jobName) {} 94 | 95 | } 96 | 97 | module.exports = { 98 | PREDICTION_SERVICE, 99 | Predict, 100 | PredictTaskConfig, 101 | }; 102 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/src/tasks/predict_task.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Predict task class. 17 | */ 18 | 19 | 'use strict'; 20 | 21 | const {BaseTask} = require('./base_task.js'); 22 | const {PREDICTION_SERVICE} = require('./predict/base_predict.js'); 23 | const {AutoMlPredict} = require('./predict/automl_predict.js'); 24 | const {VertexPredict} = require('./predict/vertex_predict.js'); 25 | 26 | /** @const {string} The prediction job identity name. */ 27 | const JOB_ID_PARAMETER = 'operationName'; 28 | 29 | /** 30 | * Creates a batch prediction job and get the output information (BigQuery or 31 | * Cloud Storage). This task supports different external prediction services, 32 | * including AutoML Tables API, Vertex AI batch prediction. 33 | */ 34 | class PredictTask extends BaseTask { 35 | 36 | /** @constructor */ 37 | constructor(config, parameters, defaultProject = undefined) { 38 | super(config, parameters, defaultProject); 39 | this.predictService = this.getPredictService_(config.model.api); 40 | } 41 | 42 | /** @override */ 43 | isManualAsynchronous() { 44 | return true; 45 | } 46 | 47 | /** @override */ 48 | async doTask() { 49 | const jobName = await this.predictService.batchPredict(this.config); 50 | return { 51 | parameters: this.appendParameter({[JOB_ID_PARAMETER]: jobName}), 52 | }; 53 | } 54 | 55 | /** @override */ 56 | async isDone() { 57 | const jobName = this.parameters[JOB_ID_PARAMETER]; 58 | return this.predictService.isPredictDone(jobName); 59 | } 60 | 61 | /** @override */ 62 | async completeTask() { 63 | const jobName = this.parameters[JOB_ID_PARAMETER]; 64 | const predictOutput = await this.predictService.getPredictOutput(jobName); 65 | return { 66 | parameters: this.appendParameter({predictOutput}), 67 | }; 68 | } 69 | 70 | /** 71 | * Gets the corresponding prediction service based on config. 72 | * @param {PREDICTION_SERVICE=} api, default is AutoMl Tables API 73 | * @return {VertexPredict|AutoMlPredict} 74 | * @private 75 | */ 76 | getPredictService_(api = PREDICTION_SERVICE.AUTOML_TABLES) { 77 | switch (api) { 78 | case PREDICTION_SERVICE.AUTOML_TABLES: 79 | return new AutoMlPredict(); 80 | case PREDICTION_SERVICE.VERTEX_AI: 81 | return new VertexPredict(); 82 | default: 83 | throw new Error(`Unknown predict Api: ${api}`); 84 | } 85 | } 86 | } 87 | 88 | module.exports = { 89 | PredictTask, 90 | }; 91 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/src/tasks/report/googleads_report_api.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this fileAccessObject except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://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 | * @fileoverview Interface for Google Ads Reporting. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const { buildQuery } = require('google-ads-api/build/src/query.js'); 21 | const { api: { googleadsapi: { GoogleAdsApi } } } 22 | = require('@google-cloud/nodejs-common'); 23 | const { SearchAdsReport } = require('./search_ads_report.js'); 24 | 25 | /** 26 | * Error messages that the task should fail directly without retry process. 27 | * @type {Array} 28 | */ 29 | const FatalErrors = ['PERMISSION_DENIED: The caller does not have permission']; 30 | 31 | /** Google Ads Report class. */ 32 | class GoogleAdsReport extends SearchAdsReport { 33 | 34 | constructor(config, apiInstance) { 35 | super(config, apiInstance); 36 | } 37 | 38 | /** @override */ 39 | isFatalError(errorMessage) { 40 | return FatalErrors.some( 41 | (fatalErrorMessage) => errorMessage.indexOf(fatalErrorMessage) > -1 42 | ); 43 | } 44 | 45 | /** @override */ 46 | getApiInstance() { 47 | if (!this.apiInstance) { 48 | this.apiInstance = 49 | new GoogleAdsApi(this.config.developerToken, false, super.getOption()); 50 | } 51 | return this.apiInstance; 52 | } 53 | 54 | /** @override */ 55 | async getQuery(parameters) { 56 | if (this.config.reportQuery) { 57 | return buildQuery(this.config.reportQuery).gaqlQuery; 58 | } 59 | return super.getQuery(parameters); 60 | } 61 | } 62 | 63 | module.exports = { GoogleAdsReport }; 64 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/src/tasks/report/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Hosts all Reports used by Sentinel ReportTask. 17 | */ 18 | 19 | 'use strict'; 20 | 21 | const {Report, ReportConfig} = require('./base_report.js'); 22 | const {CampaignManagerReport} = require('./campaign_manager_report.js'); 23 | const {DoubleClickSearchReport} = require('./doubleclick_search_report.js'); 24 | const { SearchAdsReport } = require('./search_ads_report.js'); 25 | const {DoubleClickBidManagerReport} = 26 | require('./doubleclick_bidmanager_report.js'); 27 | const { GeneralApiResult } = require('./general_api_result.js'); 28 | const { GoogleAdsReport } = require('./googleads_report_api.js'); 29 | 30 | /** 31 | * All reports supported by ReportTask. 32 | * @const {!Object} 33 | */ 34 | const REPORTING_FACTORY = Object.freeze({ 35 | 'CM': CampaignManagerReport, 36 | 'CM360': CampaignManagerReport,// Alternative code support 37 | 'DS': DoubleClickSearchReport, // This will be discontinued after 2024/07 38 | 'SA360': SearchAdsReport, 39 | 'DV360': DoubleClickBidManagerReport, 40 | 'API': GeneralApiResult, 41 | 'ADS': GoogleAdsReport, 42 | }); 43 | 44 | /** 45 | * Gets the report implementation of the target system. 46 | * @param {!ReportConfig} reportConfig Report target system name. 47 | * @return {!Report} An implementation of the Report for the given system. 48 | */ 49 | const buildReport = (reportConfig) => { 50 | const report = REPORTING_FACTORY[reportConfig.target]; 51 | if (report) return new report(reportConfig.config); 52 | throw new Error(`Fail to find Report for: ${reportConfig.target}`); 53 | }; 54 | 55 | module.exports = { 56 | Report, 57 | ReportConfig, 58 | buildReport, 59 | }; 60 | -------------------------------------------------------------------------------- /marketing-analytics/activation/data-tasks-coordinator/src/utils/cronjob_helper.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | /** @fileoverview Cronjob helper functions. */ 16 | 17 | 'use strict'; 18 | 19 | const {scheduler: {CloudScheduler}} = require('@google-cloud/nodejs-common'); 20 | 21 | /** 22 | * Returns the cron job name of Status check task. 23 | * @param {string=} prefix, default value is the env var 'PROJECT_NAMESPACE' 24 | * @return {string} Cron job name. 25 | */ 26 | const getStatusCheckCronJobName = (prefix = process.env['PROJECT_NAMESPACE']) => { 27 | return `${prefix}-intrinsic-cronjob`; 28 | }; 29 | 30 | /** 31 | * Sentinel leverages Cloud Scheduler to trigger the Status Check Task (@link 32 | * '../tasks/internal/status_check_task.js') for those 'programmed asynchronous 33 | * tasks'. To reduce the TaskLogs generated by this task, use these two 34 | * functions to resume and pause the scheduler job when suitable. 35 | * 36 | * After Status Check Task finds there is no tasks need to be checked or just 37 | * created, it will use this function pause the job. 38 | * @return {boolean} 39 | */ 40 | const pauseStatusCheck = () => { 41 | return new CloudScheduler().pauseJob(getStatusCheckCronJobName()); 42 | }; 43 | 44 | /** 45 | * When a programmed asynchronous task starts, it will resume this Scheduler job 46 | * for the self status check in future. 47 | * @return {boolean} 48 | */ 49 | const resumeStatusCheck = () => { 50 | return new CloudScheduler().resumeJob(getStatusCheckCronJobName()); 51 | }; 52 | 53 | module.exports = { 54 | getStatusCheckCronJobName, 55 | pauseStatusCheck, 56 | resumeStatusCheck, 57 | }; 58 | -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | 10 | .git 11 | .gitignore 12 | .gcloudignore 13 | .idea 14 | .history 15 | *.template 16 | *.sh 17 | *.iml 18 | *.ndjson 19 | **/.DS_Store 20 | sql/ 21 | jasmine.json 22 | config*.json 23 | *.md 24 | node_modules 25 | package-lock.json 26 | tests 27 | tutorials 28 | terraform 29 | keys/* 30 | !keys/oauth2.token.json 31 | -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "tests", 3 | "spec_files": [ 4 | "**/*_test.js" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@google-cloud/gmp-googleads-connector", 3 | "version": "4.5.0", 4 | "description": "GMP & Google Ads connector based on Cloud Functions", 5 | "author": "Google Inc.", 6 | "license": "Apache-2.0", 7 | "files": [ 8 | "deploy.sh", 9 | "src/" 10 | ], 11 | "config": { 12 | "runtime": "nodejs18", 13 | "availableMemoryMb": "2048" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/GoogleCloudPlatform/cloud-for-marketing.git", 18 | "directory": "marketing-analytics/activation/gmp-googleads-connector" 19 | }, 20 | "homepage": "https://github.com/GoogleCloudPlatform/cloud-for-marketing/blob/master/marketing-analytics/activation/gmp-googleads-connector/README.md", 21 | "main": "index.js", 22 | "dependencies": { 23 | "@google-cloud/nodejs-common": "^2.3.0", 24 | "@google-cloud/storage": "^7.12.0", 25 | "lodash": "^4.17.21", 26 | "nanoid": "^3.3.4", 27 | "ssh2-sftp-client": "^8.0.0" 28 | }, 29 | "devDependencies": { 30 | "jasmine": "^5.2.0" 31 | }, 32 | "scripts": { 33 | "test": "node node_modules/jasmine/bin/jasmine" 34 | }, 35 | "cloud-repo-tools": { 36 | "requiresKeyFile": true, 37 | "requiresProjectId": true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/src/api_handlers/google_ads_call_conversions_upload.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Tentacles API handler for Google Ads Call Conversions 17 | * uploading (Google Ads API (unofficial) Wrapper). 18 | */ 19 | 20 | 'use strict'; 21 | 22 | const { 23 | api: { googleadsapi: { GoogleAdsApi: GoogleAds } }, 24 | utils: { BatchResult, changeObjectNamingFromSnakeToLowerCamel }, 25 | } = require('@google-cloud/nodejs-common'); 26 | const { 27 | GoogleAdsConversionConfig, 28 | GoogleAdsClickConversionUpload, 29 | } = require('./google_ads_click_conversions_upload.js'); 30 | 31 | /** 32 | * Call conversions upload for Google Ads. 33 | */ 34 | class GoogleAdsCallConversionUpload extends GoogleAdsClickConversionUpload { 35 | 36 | /** 37 | * Sends out the data as call conversions to Google Ads API. 38 | * This function exposes a googleAds parameter for test 39 | * @param {GoogleAds} googleAds Injected Google Ads instance. 40 | * @param {string} records Data to send out as call conversions. Expected JSON 41 | * string in each line. 42 | * @param {string} messageId Pub/sub message ID for log. 43 | * @param {!GoogleAdsConversionConfig} config 44 | * @return {!Promise} 45 | */ 46 | async sendDataInternal(googleAds, records, messageId, config) { 47 | const result = await this.setCustomVariable(googleAds, config); 48 | if (result) return result; 49 | const managedSend = this.getManagedSendFn(config); 50 | const { customerId, loginCustomerId, adsConfig } = 51 | changeObjectNamingFromSnakeToLowerCamel(config); 52 | const configuredUpload = googleAds.getUploadCallConversionFn(customerId, 53 | loginCustomerId, adsConfig); 54 | return managedSend(configuredUpload, records, messageId); 55 | }; 56 | } 57 | 58 | /** API name in the incoming file name. */ 59 | GoogleAdsCallConversionUpload.code = 'CALL'; 60 | 61 | module.exports = { GoogleAdsCallConversionUpload }; 62 | -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/src/api_handlers/google_ads_conversion_adjustments_upload.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | * @fileoverview Tentacles API handler for Google Ads Conversion Adjustments 17 | * uploading (Google Ads API (unofficial) Wrapper). 18 | */ 19 | 20 | 'use strict'; 21 | 22 | const { 23 | api: { googleadsapi: { GoogleAdsApi: GoogleAds } }, 24 | utils: { BatchResult, changeObjectNamingFromSnakeToLowerCamel }, 25 | } = require('@google-cloud/nodejs-common'); 26 | const { 27 | GoogleAdsConversionConfig, 28 | GoogleAdsClickConversionUpload, 29 | } = require('./google_ads_click_conversions_upload.js'); 30 | 31 | /** 32 | * Conversion adjustment upload for Google Ads. 33 | */ 34 | class GoogleAdsConversionAdjustment extends GoogleAdsClickConversionUpload { 35 | 36 | /** 37 | * Sends out the data as conversion adjustments to Google Ads API. 38 | * This function exposes a googleAds parameter for test 39 | * @param {GoogleAds} googleAds Injected Google Ads instance. 40 | * @param {string} records Data to send out as click conversions. Expected 41 | * JSON string in each line. 42 | * @param {string} messageId Pub/sub message ID for log. 43 | * @param {!GoogleAdsConversionConfig} config 44 | * @return {!Promise} 45 | */ 46 | async sendDataInternal(googleAds, records, messageId, config) { 47 | const managedSend = this.getManagedSendFn(config); 48 | const { customerId, loginCustomerId, adsConfig } = 49 | changeObjectNamingFromSnakeToLowerCamel(config); 50 | const configuredUpload = googleAds.getUploadConversionAdjustmentFn( 51 | customerId, loginCustomerId, adsConfig); 52 | return managedSend(configuredUpload, records, messageId); 53 | }; 54 | } 55 | 56 | /** API name in the incoming file name. */ 57 | GoogleAdsConversionAdjustment.code = 'ACA'; 58 | 59 | module.exports = { GoogleAdsConversionAdjustment }; 60 | -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/data_source_reconnect_bigquery.md: -------------------------------------------------------------------------------- 1 | # Reconnect data sources for your Data Studio dashboard 2 | 3 | After you have just made a copy of the dashboard, you will have you own dashboard with all tables unavailable because you don't have the access to the connected BigQuery data. 4 | 5 | ![Unavailable tables in Data Studio](./images/datastudio/unavailable_data_sources.png) 6 | 7 | You need to reconnect the data sources to your own BigQuery. 8 | 9 | 1. Click the Menu `Resource` - `Manage added data sources` 10 | 1. Click the `EDIT` under column `Actions` of `TentaclesReport` 11 | ![Edit data source](./images/datastudio/edit_data_source.png) 12 | 1. Input the ID of your Google Cloud Project and select the dataset and table. The table should be `TentaclesReport` here. 13 | ![Select table](./images/datastudio/select_table.png) 14 | 1. Click the button `RECONNECT` and confirm by click `Apply` in the popup window. 15 | ![Confirm reconnect](./images/datastudio/confirm_reconnect_table.png) 16 | 1. Click the button `FINISHED` in the page and it will go back to the page listing data sources. 17 | ![Finish editting](./images/datastudio/finish_editting.png) 18 | 1. Repeat the steps for data source `TentaclesBlockage` 19 | 1. Click `X Close`, then the button `View`. 20 | 21 | Congratulations! You have your own dashboard for your Tentacles instance. 22 | ![Complated dashboard](./images/datastudio/completed_dashboard.png) 23 | -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/images/cm_floodlight_report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/activation/gmp-googleads-connector/tutorials/images/cm_floodlight_report.png -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/images/cm_user_roles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/activation/gmp-googleads-connector/tutorials/images/cm_user_roles.png -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/images/cyborg/Cyborg_start_installation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/activation/gmp-googleads-connector/tutorials/images/cyborg/Cyborg_start_installation.gif -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/completed_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/completed_dashboard.png -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/confirm_reconnect_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/confirm_reconnect_table.png -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/edit_data_source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/edit_data_source.png -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/finish_editting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/finish_editting.png -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/select_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/select_table.png -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/unavailable_data_sources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/activation/gmp-googleads-connector/tutorials/images/datastudio/unavailable_data_sources.png -------------------------------------------------------------------------------- /marketing-analytics/activation/gmp-googleads-connector/tutorials/images/ga_measurement_protocol_result.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/activation/gmp-googleads-connector/tutorials/images/ga_measurement_protocol_result.gif -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/README.md: -------------------------------------------------------------------------------- 1 | # Sheets Based Installer 2 | 3 | 4 | 5 | Disclaimer: This is not an official Google product. 6 | 7 | Sheets Based Installer (code name **Cyborg**) is a framework that generate a 8 | manifest Google Sheets based on configurations of a Google Cloud solution*. 9 | 10 | This generated Google Sheets can be used to install or upgrade the solution. 11 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@google-cloud/sheets-based-installer", 3 | "version": "1.6.0", 4 | "description": "Google Sheets based installer", 5 | "type": "module", 6 | "author": "Google Inc.", 7 | "license": "Apache-2.0", 8 | "files": [ 9 | "src/" 10 | ], 11 | "bin": { 12 | "cyborg": "./bin/cyborg.js" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/GoogleCloudPlatform/cloud-for-marketing.git", 17 | "directory": "marketing-analytics/activation/sheets-based-installer" 18 | }, 19 | "homepage": "https://github.com/GoogleCloudPlatform/cloud-for-marketing/blob/master/marketing-analytics/activation/sheets-based-installer/README.md", 20 | "dependencies": { 21 | "fs-extra": "^11.2.0", 22 | "glob": "^10.3.10", 23 | "open": "^10.0.4", 24 | "uglify-js": "^3.17.4" 25 | }, 26 | "cloud-repo-tools": { 27 | "requiresKeyFile": true, 28 | "requiresProjectId": true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/src/apps_script/1_gcp/cloud_billing.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | /** @fileoverview Cloud Billing API handler class.*/ 16 | 17 | /** 18 | * This class is used to check the (GCP) project billing status. 19 | */ 20 | class CloudBilling extends ApiBase { 21 | 22 | constructor(projectId) { 23 | super(); 24 | this.name = 'Cloud Billing API'; 25 | this.api = 'cloudbilling.googleapis.com'; 26 | this.apiUrl = `https://${this.api}`; 27 | this.version = 'v1'; 28 | this.projectId = projectId; 29 | } 30 | 31 | /** @override */ 32 | getBaseUrl() { 33 | return `${this.apiUrl}/${this.version}/projects/${this.projectId}`; 34 | } 35 | 36 | /** 37 | * Gets the billing information for a project. 38 | * @see https://cloud.google.com/billing/docs/reference/rest/v1/projects/getBillingInfo 39 | * @return {ProjectBillingInfo} 40 | * @see https://cloud.google.com/billing/docs/reference/rest/v1/ProjectBillingInfo 41 | */ 42 | getBillingInfo() { 43 | return this.get('billingInfo'); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/src/apps_script/1_gcp/iam_credentials.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 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 | // http://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 | /** @fileoverview Service Account Credentials API handler class.*/ 16 | 17 | class IamCredentials extends ApiBase { 18 | 19 | constructor() { 20 | super(); 21 | this.name = 'Service Account Credentials API'; 22 | this.api = 'iamcredentials.googleapis.com'; 23 | this.apiUrl = `https://${this.api}`; 24 | this.version = 'v1'; 25 | } 26 | 27 | /** @override */ 28 | getBaseUrl() { 29 | return `${this.apiUrl}/${this.version}/projects/-`; 30 | } 31 | 32 | /** 33 | * Generates an OAuth 2.0 access token for a service account. 34 | * @see https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/generateAccessToken 35 | * @return {{ 36 | * accessToken: string, 37 | * expireTime: string 38 | * }} 39 | */ 40 | generateAccessToken(serviceAccount, scope) { 41 | return this.mutate( 42 | `serviceAccounts/${serviceAccount}:generateAccessToken`, { scope }); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/src/apps_script/3_api/analytics.js: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google Inc. 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 | // http://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 | /** @fileoverview Google Analytics API handler class.*/ 16 | 17 | class Analytics extends ExternalApi { 18 | 19 | constructor(option) { 20 | super(option); 21 | this.apiUrl = 'https://www.googleapis.com/analytics'; 22 | this.version = 'v3'; 23 | } 24 | 25 | /** @override */ 26 | getBaseUrl() { 27 | return `${this.apiUrl}/${this.version}/management`; 28 | } 29 | 30 | /** @override */ 31 | getScope() { 32 | return 'https://www.googleapis.com/auth/analytics'; 33 | } 34 | 35 | /** 36 | * Verifies the existence of the custom data source. 37 | * @see https://developers.google.com/analytics/devguides/config/mgmt/v3/mgmtReference/management/customDataSources/list 38 | * @param {string} accountId 39 | * @param {string} webPropertyId 40 | * @param {string} customDataSourceId 41 | * @return {VerifyResult} 42 | */ 43 | verifyDataSource(accountId, webPropertyId, customDataSourceId) { 44 | const customDataSources = []; 45 | let link = 46 | `accounts/${accountId}/webproperties/${webPropertyId}/customDataSources`; 47 | do { 48 | const { error, items, nextLink } = this.get(link); 49 | if (error) { 50 | return { 51 | valid: false, 52 | reason: error.content || error.errors[0].message, 53 | }; 54 | } 55 | customDataSources.push(...items); 56 | link = nextLink; 57 | } while (link) 58 | if (customDataSources.some(({ id }) => id === customDataSourceId)) { 59 | return { valid: true }; 60 | } 61 | return { 62 | valid: false, 63 | reason: `Can not find custom data sources ${customDataSourceId}`, 64 | }; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/src/apps_script/3_api/doubleclick_bid_manager.js: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google Inc. 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 | // http://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 | /** @fileoverview Display & Video 360 Reporting API handler class.*/ 16 | 17 | /** 18 | * Report DV360 is known as DoubleClick Bid Manager API. 19 | * @see https://developers.google.com/bid-manager/reference/rest 20 | */ 21 | class DoubleclickBidManager extends ExternalApi { 22 | 23 | constructor(option) { 24 | super(option); 25 | this.name = 'DoubleClick Bid Manager API'; 26 | this.api = 'doubleclickbidmanager.googleapis.com'; 27 | this.apiUrl = `https://${this.api}`; 28 | this.version = 'v2'; 29 | } 30 | 31 | /** @override */ 32 | getBaseUrl() { 33 | return `${this.apiUrl}/${this.version}`; 34 | } 35 | 36 | /** @override */ 37 | getScope() { 38 | return 'https://www.googleapis.com/auth/doubleclickbidmanager'; 39 | } 40 | 41 | /** 42 | * Verifies the existence of the query and the query is exported as CSV. 43 | * @see https://developers.google.com/bid-manager/reference/rest/v2/queries/get 44 | * @param {string} queryId 45 | * @return {VerifyResult} 46 | */ 47 | verifyQuery(queryId) { 48 | const { error, metadata } = this.getQuery(queryId); 49 | if (error) { 50 | return { 51 | valid: false, 52 | reason: error.message, 53 | }; 54 | } 55 | if (metadata.format !== 'CSV') { 56 | return { 57 | valid: false, 58 | reason: `Query[${queryId}]'s format is not CSV`, 59 | }; 60 | } 61 | return { valid: true }; 62 | } 63 | 64 | /** 65 | * Retrieves a query. 66 | * @see https://developers.google.com/bid-manager/reference/rest/v2/queries/get 67 | * @param {string} queryId 68 | * @return {Query} 69 | */ 70 | getQuery(queryId) { 71 | return this.get(`queries/${queryId}`); 72 | } 73 | 74 | 75 | /** 76 | * Creates a query. 77 | * @see https://developers.google.com/bid-manager/reference/rest/v2/queries/create 78 | * @param {string} query 79 | * @return {Query} 80 | */ 81 | createQuery(query) { 82 | return this.mutate('queries', query); 83 | } 84 | 85 | /** 86 | * Deletes a query as well as the associated reports. 87 | * @see https://developers.google.com/bid-manager/reference/rest/v2/queries/delete 88 | * @param {string} queryId 89 | * @return {object} Should be empty. 90 | */ 91 | deleteQuery(queryId) { 92 | return this.mutate(`queries/${queryId}`, undefined, 'DELETE'); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/src/apps_script/3_api/drive.js: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google Inc. 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 | // http://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 | /** @fileoverview Google Drive API handler class.*/ 16 | 17 | /** @const DRIVE_ROLE_MAP Drive file's role name and role vaules in API. */ 18 | const DRIVE_ROLE_MAP = { 19 | Viewer: 'reader', 20 | Commenter: 'commenter', 21 | Editor: 'writer', 22 | } 23 | 24 | class Drive extends ExternalApi { 25 | 26 | constructor(option) { 27 | super(option); 28 | this.name = 'Google Drive API'; 29 | this.api = 'drive.googleapis.com'; 30 | this.apiUrl = 'https://www.googleapis.com/drive'; 31 | this.version = 'v3'; 32 | } 33 | 34 | /** @override */ 35 | getBaseUrl() { 36 | return `${this.apiUrl}/${this.version}`; 37 | } 38 | 39 | /** @override */ 40 | getScope() { 41 | return 'https://www.googleapis.com/auth/drive'; 42 | } 43 | 44 | /** 45 | * Gets the content of a Drive file. 46 | * @see https://developers.google.com/drive/api/reference/rest/v3/files/get 47 | * @param {string} fileId 48 | * @return {VerifyResult} 49 | */ 50 | getFileContent(fileId) { 51 | const response = this.get(`files/${fileId}?alt=media`); 52 | if (response.error) { 53 | throw new Error(error.message); 54 | } 55 | return response; 56 | } 57 | 58 | /** 59 | * Gets the information of a Drive file. 60 | * @see https://developers.google.com/drive/api/reference/rest/v3/files/get 61 | * @param {string} fileId 62 | * @return {VerifyResult} 63 | */ 64 | getFileInfo(fileId) { 65 | const response = this.get(`files/${fileId}`); 66 | if (response.error) { 67 | throw new Error(error.message); 68 | } 69 | return response; 70 | } 71 | 72 | /** 73 | * Creates or updates a permission for a file. 74 | * @see https://developers.google.com/drive/api/reference/rest/v3/permissions/create 75 | * @param {string} fileId 76 | * @param {string} email 77 | * @param {string} role 'Viewer', 'Commenter' or 'Editor'. 78 | * @return {Permission} 79 | */ 80 | addUser(fileId, email, role) { 81 | const payload = { 82 | type: 'user', 83 | emailAddress: email, 84 | role: DRIVE_ROLE_MAP[role] || 'reader', 85 | } 86 | const response = this.mutate(`files/${fileId}/permissions`, payload); 87 | if (response.role !== role) { 88 | console.log(`User ${email} has role`, response.role); 89 | } 90 | return response; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/src/apps_script/3_api/gmail.js: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google Inc. 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 | // http://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 | /** @fileoverview Gmail API handler class.*/ 16 | 17 | class Gmail extends ExternalApi { 18 | 19 | constructor(option) { 20 | super(option); 21 | this.name = 'Gmail API'; 22 | this.api = 'gmail.googleapis.com'; 23 | this.apiUrl = 'https://gmail.googleapis.com/gmail'; 24 | this.version = 'v1'; 25 | } 26 | 27 | /** @override */ 28 | getBaseUrl() { 29 | return `${this.apiUrl}/${this.version}/users`; 30 | } 31 | 32 | /** 33 | * @see https://developers.google.com/gmail/api/reference/rest/v1/users.messages/send#authorization-scopes 34 | * @override 35 | */ 36 | getScope() { 37 | return 'https://www.googleapis.com/auth/gmail.send'; 38 | } 39 | 40 | /** 41 | * Sends a simple email (no attachment). 42 | * @see https://developers.google.com/gmail/api/reference/rest/v1/users.messages/send 43 | * @param {string} recipient 44 | * @param {string} subject 45 | * @param {string} content 46 | * @return {!Message} 47 | */ 48 | sendSimpleEmail(recipient, subject, content) { 49 | const message = `To: ${recipient} 50 | Subject: ${subject} 51 | 52 | ${content}`; 53 | return this.mutate('me/messages/send', 54 | { raw: Utilities.base64Encode(message) }); 55 | } 56 | 57 | /** 58 | * Verifies the existence of the custom data source. 59 | * @see https://developers.google.com/gmail/api/reference/rest/v1/users/getProfile 60 | * @param {string} userId 61 | * @return {{ 62 | * "emailAddress": string, 63 | * "messagesTotal": integer, 64 | * "threadsTotal": integer, 65 | * "historyId": string 66 | * }} 67 | */ 68 | getProfile(userId) { 69 | return this.get(`${userId}/profile`); 70 | } 71 | 72 | watch(userId, topicName) { 73 | const payload = { topicName }; 74 | return this.mutate(`${userId}/watch`, payload); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/src/apps_script/3_api/search_ads.js: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google Inc. 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 | // http://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 | /** @fileoverview Search Ads API handler class.*/ 16 | 17 | class SearchAds extends ExternalApi { 18 | 19 | constructor(option) { 20 | super(option); 21 | this.apiUrl = 'https://www.googleapis.com/doubleclicksearch'; 22 | this.version = 'v2'; 23 | } 24 | 25 | /** @override */ 26 | getBaseUrl() { 27 | return `${this.apiUrl}/${this.version}`; 28 | } 29 | 30 | /** @override */ 31 | getScope() { 32 | return 'https://www.googleapis.com/auth/doubleclicksearch'; 33 | } 34 | 35 | /** 36 | * Verifies floodlight activities by updating the availabilities. 37 | * @see https://developers.google.com/search-ads/v2/reference/conversion/updateAvailability 38 | * @param {Array<>} availabilities 39 | * @return {VerifyResult} 40 | */ 41 | verifyFloodlightActivities(availabilities) { 42 | const availabilityTimestamp = Date.now(); 43 | availabilities.forEach((activity) => { 44 | activity.availabilityTimestamp = availabilityTimestamp; 45 | }) 46 | const { error } = 47 | this.mutate('conversion/updateAvailability', { availabilities }); 48 | if (error) { 49 | return { 50 | valid: false, 51 | reason: error.message || error.errors[0].message, 52 | }; 53 | } 54 | return { valid: true }; 55 | } 56 | 57 | /** 58 | * Verifies the existence of the agency. 59 | * @see https://developers.google.com/search-ads/v2/reference/reports/generate 60 | * @param {string} agencyId 61 | * @return {VerifyResult} 62 | */ 63 | verifyAgency(agencyId) { 64 | const payload = { 65 | reportType: 'account', 66 | rowCount: 1, 67 | startRow: 0, 68 | statisticsCurrency: 'usd', 69 | columns: [{ columnName: 'agency' }], 70 | filters: [{ 71 | column: { columnName: 'agencyId' }, 72 | operator: 'equals', 73 | values: [agencyId], 74 | }], 75 | }; 76 | const response = this.mutate('reports/generate', payload); 77 | const { rowCount, error } = response; 78 | if (error) { 79 | return { 80 | valid: false, 81 | reason: error.message || error.errors[0].message, 82 | }; 83 | } 84 | if (rowCount === 0) { 85 | return { 86 | valid: false, 87 | reason: `Can not find Agency with ID ${agencyId}`, 88 | }; 89 | } 90 | return { valid: true }; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/src/apps_script/3_api/sheets.js: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google Inc. 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 | // http://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 | /** @fileoverview Google Sheets API handler class.*/ 16 | 17 | class Sheets extends ExternalApi { 18 | 19 | constructor(option) { 20 | super(option); 21 | this.apiUrl = 'https://sheets.googleapis.com'; 22 | this.version = 'v4'; 23 | } 24 | 25 | /** @override */ 26 | getBaseUrl() { 27 | return `${this.apiUrl}/${this.version}`; 28 | } 29 | 30 | /** @override */ 31 | getScope() { 32 | return 'https://www.googleapis.com/auth/spreadsheets'; 33 | } 34 | 35 | /** 36 | * Verifies the accessibililty of the spreadsheet. 37 | * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/get 38 | * @param {string} spreadsheetId 39 | * @return {VerifyResult} 40 | */ 41 | verifySpreadsheet(spreadsheetId) { 42 | const { error } = this.get(`spreadsheets/${spreadsheetId}`); 43 | if (error) { 44 | return { 45 | valid: false, 46 | reason: error.message, 47 | }; 48 | } 49 | return { valid: true }; 50 | } 51 | 52 | /** 53 | * Verifies the sheet by the spreadsheet id and sheet name. 54 | * @param {string} spreadsheetId 55 | * @param {string} sheetName 56 | * @return {VerifyResult} 57 | */ 58 | verifySheet(spreadsheetId, sheetName) { 59 | const { error } = this.get( 60 | `spreadsheets/${spreadsheetId}`, { ranges: sheetName }); 61 | if (error) { 62 | return { 63 | valid: false, 64 | reason: error.message, 65 | }; 66 | } 67 | return { valid: true }; 68 | } 69 | 70 | /** 71 | * Gets the values of given range of a spreadsheet. 72 | * @param {string} spreadsheetId 73 | * @param {string} sheetName 74 | * @param {number=} row 75 | * @return {Array>} 76 | */ 77 | getHeadline(spreadsheetId, sheetName, row = 1) { 78 | const range = `'${sheetName}'!A${row}:${row}` 79 | return this.get( 80 | `spreadsheets/${spreadsheetId}/values/${encodeURIComponent(range)}`); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/src/apps_script/appsscript.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeZone": "Australia/Sydney", 3 | "dependencies": { 4 | "enabledAdvancedServices": [] 5 | }, 6 | "exceptionLogging": "STACKDRIVER", 7 | "runtimeVersion": "V8", 8 | "oauthScopes": [ 9 | "https://www.googleapis.com/auth/script.external_request", 10 | "https://www.googleapis.com/auth/script.scriptapp", 11 | "https://www.googleapis.com/auth/script.container.ui", 12 | "https://www.googleapis.com/auth/spreadsheets", 13 | "https://www.googleapis.com/auth/drive", 14 | "https://www.googleapis.com/auth/cloud-platform", 15 | "https://www.googleapis.com/auth/userinfo.profile" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /marketing-analytics/activation/sheets-based-installer/src/dependency.json: -------------------------------------------------------------------------------- 1 | { 2 | "_default": [ 3 | "appsscript.json", 4 | "api_base.js", 5 | "external_api.js", 6 | "utils.js", 7 | "trigger_functions.js" 8 | ], 9 | "colab_solution_sheet.js": [ 10 | "plain_sheet.js", 11 | "colab_solution.js" 12 | ], 13 | "api_access_checker.js": [ 14 | "plain_sheet.js", 15 | "gcloud.js", 16 | "3_api" 17 | ], 18 | "file_to_storage.js": [ 19 | "plain_sheet.js", 20 | "storage.js" 21 | ], 22 | "secret_manager_sheet.js": [ 23 | "plain_sheet.js", 24 | "secret_manager.js" 25 | ], 26 | "sentinel_cron_job.js": [ 27 | "plain_sheet.js", 28 | "cloud_scheduler.js", 29 | "sentinel_mojo_sheet.js" 30 | ], 31 | "sentinel_mojo_sheet.js": [ 32 | "mojo_sheet.js", 33 | "gcloud.js", 34 | "sentinel.js", 35 | "cloud_scheduler.js" 36 | ], 37 | "sentinel_task_config.js": [ 38 | "plain_sheet.js", 39 | "gcloud.js", 40 | "sentinel.js", 41 | "pub_sub.js" 42 | ], 43 | "tentacles_mojo_sheet.js": [ 44 | "mojo_sheet.js", 45 | "gcloud.js", 46 | "tentacles.js", 47 | "pub_sub.js", 48 | "bigquery.js", 49 | "logging.js" 50 | ], 51 | "tentacles_api_config.js": [ 52 | "plain_sheet.js", 53 | "gcloud.js", 54 | "3_api", 55 | "storage.js" 56 | ], 57 | "plain_sheet.js": [ 58 | "data_table_sheet.js" 59 | ], 60 | "data_table_sheet.js": [ 61 | "enhanced_sheet.js" 62 | ], 63 | "oauth.js": [ 64 | "secret_manager.js", 65 | "secret_manager_sheet.js", 66 | "oauth.html" 67 | ], 68 | "mojo_sheet.js": [ 69 | "mojo.js", 70 | "mojo_constant.js" 71 | ], 72 | "mojo_constant.js": [ 73 | "timezone.js" 74 | ], 75 | "tentacles.js": [ 76 | "prime_solution.js" 77 | ], 78 | "sentinel.js": [ 79 | "prime_solution.js" 80 | ], 81 | "gcloud.js": [ 82 | "1_gcp" 83 | ], 84 | "colab_solution.js": [ 85 | "cloud_functions.js", 86 | "service_usage.js" 87 | ], 88 | "prime_solution.js": [ 89 | "cloud_functions.js", 90 | "service_usage.js" 91 | ], 92 | "mojo.js": [ 93 | "enhanced_sheet.js" 94 | ], 95 | "external_api.js": [ 96 | "api_base.js", 97 | "iam_credentials.js" 98 | ], 99 | "explicit_auth.js": [ 100 | "explicit_auth.html" 101 | ] 102 | } 103 | -------------------------------------------------------------------------------- /marketing-analytics/integration/crmint/README.md: -------------------------------------------------------------------------------- 1 | CRMint is a platform to help you bring your business data into Google Products. These products may generally be marketing-related products such as Google Ads and the Google Marketing Platform, but could also include Cloud products, for example. 2 | 3 | You can find more details in the following links: 4 | 5 | - [Documemntation](https://google.github.io/crmint/docs) 6 | - [Github code repository](https://github.com/google/crmint) 7 | 8 | Disclaimer: This is not an official Google product. -------------------------------------------------------------------------------- /marketing-analytics/personalization/dataproc-recommendation/README.md: -------------------------------------------------------------------------------- 1 | This page refers to a guide that shows how to perform collaborative filtering using Spark libraries on Cloud Dataproc. 2 | 3 | For more details: 4 | - Refer to the [guide](https://cloud.google.com/solutions/recommendations-using-machine-learning-on-compute-engine) 5 | - Try it yourself using this [Github repository](https://github.com/GoogleCloudPlatform/spark-recommendation-engine) 6 | 7 | 8 | Disclaimer: This is not an official Google product. -------------------------------------------------------------------------------- /marketing-analytics/personalization/tensorflow-recommendation/README.md: -------------------------------------------------------------------------------- 1 | This page refers to a multi-part tutorial that shows you how to implement a recommendation system with TensorFlow and AI Platform in Google Cloud Platform (GCP). 2 | 3 | For more details: 4 | 5 | - Refer to the [guide](https://cloud.google.com/solutions/machine-learning/recommendation-system-tensorflow-overview) 6 | - Try it with some [code examples](https://github.com/GoogleCloudPlatform/tensorflow-recommendation-wals) 7 | 8 | 9 | Disclaimer: This is not an official Google product. -------------------------------------------------------------------------------- /marketing-analytics/predicting/automl-tables-in-sheets/CONTRIBUTING: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows 28 | [Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). 29 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/automl-tables-in-sheets/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 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 | http://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 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/automl-tables-in-sheets/appsscript.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "libraries": [{ 4 | "userSymbol": "OAuth2", 5 | "libraryId": "1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF", 6 | "version": "33" 7 | }] 8 | }, 9 | "exceptionLogging": "STACKDRIVER", 10 | "runtimeVersion": "V8", 11 | "timeZone": "Etc/GMT" 12 | } 13 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/automl-tables-in-sheets/architecture_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/predicting/automl-tables-in-sheets/architecture_diagram.png -------------------------------------------------------------------------------- /marketing-analytics/predicting/clustering/README.md: -------------------------------------------------------------------------------- 1 | A common marketing analytics challenge is to understand consumer behavior and develop customer attributes or archetypes. As organizations get better at tackling this problem, they can activate marketing strategies to incorporate additional customer knowledge into their campaigns. Building customer profiles is now easier than ever with BigQuery ML. This solution uses BigQuery ML k-means clustering with Google Analytics raw data joined with synthetic CRM data to profile audience cohorts, group new users, and then import back to Google Analytics. 2 | 3 | 4 | You can find more details in the following links: 5 | 6 | - [Article](https://towardsdatascience.com/how-to-build-audience-clusters-with-website-data-using-bigquery-ml-6b604c6a084c) 7 | - [Solutions Guide](https://cloud.google.com/architecture/building-k-means-clustering-model) 8 | - [Github code repository](https://github.com/GoogleCloudPlatform/analytics-componentized-patterns/blob/master/retail/clustering/bqml/bqml_scaled_clustering.ipynb) 9 | 10 | Disclaimer: This is not an official Google product. -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/.gitignore: -------------------------------------------------------------------------------- 1 | **/__pycache__/ 2 | env/ 3 | output/ 4 | beam-temp-output*/ 5 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/.markdownlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "MD033": { 3 | "allowed_elements": ["img"] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows 28 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/images/repeat_cumulative_transactions_over_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/predicting/future-customer-value-segments/images/repeat_cumulative_transactions_over_time.png -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/images/repeat_transactions_over_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-for-marketing/8b1aef4eab43aa054a936ff90881b17809f54533/marketing-analytics/predicting/future-customer-value-segments/images/repeat_transactions_over_time.png -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/lifetimes_ext/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 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 | # http://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 | """Simplify importing of extensions.""" 16 | from .custom_beta_geo_beta_binom_fitter import BetaGeoBetaBinomFitter 17 | 18 | __all__ = ( 19 | "BetaGeoBetaBinomFitter", 20 | ) 21 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/lifetimes_ext/custom_beta_geo_beta_binom_fitter.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 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 | # http://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 | """Custom Beta-Geo Beta-Binomial Fitter. 16 | 17 | Fixes https://github.com/CamDavidsonPilon/lifetimes/issues/259 by overriding 18 | the `log_likelihood` and `negative_log_likelihood` static methods of the base 19 | `BetaGeoBetaBinomFitter`. 20 | """ 21 | import numpy as np 22 | from autograd.numpy import log, exp, logaddexp 23 | from autograd.scipy.special import betaln 24 | 25 | from lifetimes import BetaGeoBetaBinomFitter as BaseBetaGeoBetaBinomFitter 26 | 27 | 28 | class BetaGeoBetaBinomFitter(BaseBetaGeoBetaBinomFitter): 29 | def __init__(self, penalizer_coef=0.0): 30 | """Initialization, set penalizer_coef.""" 31 | super().__init__(penalizer_coef) 32 | 33 | @staticmethod 34 | def _loglikelihood(params, x, tx, T): 35 | """Log likelihood for optimizer.""" 36 | alpha, beta, gamma, delta = params 37 | 38 | betaln_ab = betaln(alpha, beta) 39 | betaln_gd = betaln(gamma, delta) 40 | 41 | A = betaln(alpha + x, beta + T - x) - betaln_ab + betaln(gamma, delta + T) - betaln_gd 42 | 43 | B = 1e-15 * np.ones_like(T) 44 | recency_T = T - tx - 1 45 | 46 | for j in np.arange(recency_T.max() + 1): 47 | ix = recency_T >= j 48 | B1 = betaln(alpha + x, beta + tx - x + j) 49 | B2 = betaln(gamma + 1, delta + tx + j) 50 | B = B + ix * (exp(B1 - betaln_ab)) * (exp(B2 - betaln_gd)) 51 | # v0.11.3 52 | # B = B + ix * betaf(alpha + x, beta + tx - x + j) * betaf(gamma + 1, delta + tx + j) 53 | 54 | log_B = log(B) 55 | # v0.11.3 56 | # B = log(B) - betaln_gd - betaln_ab 57 | result = logaddexp(A, log_B) 58 | return result 59 | 60 | @staticmethod 61 | def _negative_log_likelihood(log_params, frequency, recency, n_periods, weights, penalizer_coef=0): 62 | params = exp(log_params) 63 | penalizer_term = penalizer_coef * sum(params ** 2) 64 | return ( 65 | -(BetaGeoBetaBinomFitter._loglikelihood(params, frequency, recency, n_periods) * weights).sum() 66 | / weights.sum() 67 | + penalizer_term 68 | ) 69 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/pyenv-installer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | [ -n "$PYENV_DEBUG" ] && set -x 5 | 6 | if [ -z "$PYENV_ROOT" ]; then 7 | export PYENV_ROOT="${HOME}/.pyenv" 8 | fi 9 | 10 | colorize() { 11 | if [ -t 1 ]; then printf "\e[%sm%s\e[m" "$1" "$2" 12 | else echo -n "$2" 13 | fi 14 | } 15 | 16 | # Checks for `.pyenv` file, and suggests to remove it for installing 17 | if [ -d "${PYENV_ROOT}" ]; then 18 | { echo 19 | colorize 1 "WARNING" 20 | echo ": Can not proceed with installation. Kindly remove the '${PYENV_ROOT}' directory first." 21 | echo 22 | } >&2 23 | exit 1 24 | fi 25 | 26 | failed_checkout() { 27 | echo "Failed to git clone $1" 28 | exit -1 29 | } 30 | 31 | checkout() { 32 | [ -d "$2" ] || git clone --depth 1 "$1" "$2" || failed_checkout "$1" 33 | } 34 | 35 | if ! command -v git 1>/dev/null 2>&1; then 36 | echo "pyenv: Git is not installed, can't continue." >&2 37 | exit 1 38 | fi 39 | 40 | if [ -n "${USE_GIT_URI}" ]; then 41 | GITHUB="git://github.com" 42 | else 43 | GITHUB="https://github.com" 44 | fi 45 | 46 | checkout "${GITHUB}/pyenv/pyenv.git" "${PYENV_ROOT}" 47 | checkout "${GITHUB}/pyenv/pyenv-doctor.git" "${PYENV_ROOT}/plugins/pyenv-doctor" 48 | checkout "${GITHUB}/pyenv/pyenv-installer.git" "${PYENV_ROOT}/plugins/pyenv-installer" 49 | checkout "${GITHUB}/pyenv/pyenv-update.git" "${PYENV_ROOT}/plugins/pyenv-update" 50 | checkout "${GITHUB}/pyenv/pyenv-virtualenv.git" "${PYENV_ROOT}/plugins/pyenv-virtualenv" 51 | checkout "${GITHUB}/pyenv/pyenv-which-ext.git" "${PYENV_ROOT}/plugins/pyenv-which-ext" 52 | 53 | if ! command -v pyenv 1>/dev/null; then 54 | { echo 55 | colorize 1 "WARNING" 56 | echo ": seems you still have not added 'pyenv' to the load path." 57 | echo 58 | } >&2 59 | 60 | { # Without args, `init` commands print installation help 61 | "${PYENV_ROOT}/bin/pyenv" init || true 62 | "${PYENV_ROOT}/bin/pyenv" virtualenv-init || true 63 | } >&2 64 | fi 65 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/requirements.in: -------------------------------------------------------------------------------- 1 | annotated-types==0.7.0 2 | apache-beam==2.60.0 3 | apache-beam[gcp]==2.60.0 4 | attrs==24.2.0 5 | autograd==1.7.0 6 | avro-python3==1.10.2 7 | build==1.2.2.post1 8 | cachetools==5.5.0 9 | certifi==2024.8.30 10 | chardet==5.2.0 11 | charset-normalizer==3.4.0 12 | click==8.1.7 13 | cloudpickle==2.2.1 14 | contourpy==1.3.1 15 | crcmod==1.7 16 | cycler==0.12.1 17 | Deprecated==1.2.15 18 | dill==0.3.1.1 19 | dnspython==2.7.0 20 | docopt==0.6.2 21 | docstring_parser==0.16 22 | fastavro==1.9.7 23 | fasteners==0.19 24 | fonttools==4.55.0 25 | future==1.0.0 26 | google-api-core==2.23.0 27 | google-apitools==0.5.31 28 | google-auth==2.36.0 29 | google-auth-httplib2==0.2.0 30 | google-cloud-aiplatform==1.72.0 31 | google-cloud-bigquery==3.27.0 32 | google-cloud-bigquery-storage==2.27.0 33 | google-cloud-bigtable==2.27.0 34 | google-cloud-core==2.4.1 35 | google-cloud-datastore==2.20.1 36 | google-cloud-dlp==3.25.1 37 | google-cloud-language==2.15.1 38 | google-cloud-pubsub==2.27.1 39 | google-cloud-pubsublite==1.11.1 40 | google-cloud-recommendations-ai==0.10.14 41 | google-cloud-resource-manager==1.13.1 42 | google-cloud-spanner==3.50.1 43 | google-cloud-storage==2.18.2 44 | google-cloud-videointelligence==2.14.1 45 | google-cloud-vision==3.8.1 46 | google-crc32c==1.6.0 47 | google-resumable-media==2.7.2 48 | googleapis-common-protos==1.66.0 49 | grpc-google-iam-v1==0.13.1 50 | grpc-interceptor==0.15.4 51 | grpcio==1.65.5 52 | grpcio-status==1.62.3 53 | hdfs==2.7.3 54 | httplib2==0.22.0 55 | idna==3.10 56 | importlib_metadata==8.5.0 57 | jaraco.classes==3.4.0 58 | jaraco.context==6.0.1 59 | jaraco.functools==4.1.0 60 | jsonpickle==3.4.2 61 | jsonschema==4.23.0 62 | jsonschema-specifications==2024.10.1 63 | keyring==25.5.0 64 | keyrings.google-artifactregistry-auth==1.1.2 65 | kiwisolver==1.4.7 66 | Lifetimes==0.11.3 67 | matplotlib==3.9.2 68 | mock==5.1.0 69 | monotonic==1.6 70 | more-itertools==10.5.0 71 | numpy==1.26.4 72 | oauth2client==4.1.3 73 | objsize==0.7.0 74 | opentelemetry-api==1.28.1 75 | opentelemetry-sdk==1.28.1 76 | opentelemetry-semantic-conventions==0.49b1 77 | orjson==3.10.11 78 | overrides==7.7.0 79 | packaging==24.2 80 | pandas==2.2.3 81 | pbr==6.1.0 82 | pillow==11.0.0 83 | pip-tools==7.4.1 84 | pluggy==1.5.0 85 | proto-plus==1.25.0 86 | protobuf==4.25.5 87 | pyarrow==16.1.0 88 | pyarrow-hotfix==0.6 89 | pyasn1==0.6.1 90 | pyasn1_modules==0.4.1 91 | pydantic==2.9.2 92 | pydantic_core==2.23.4 93 | pydot==1.4.2 94 | pymongo==4.10.1 95 | pyparsing==3.2.0 96 | pyproject_hooks==1.2.0 97 | python-dateutil==2.9.0.post0 98 | python-dotenv==1.0.1 99 | pytz==2024.2 100 | redis==5.2.0 101 | referencing==0.35.1 102 | regex==2024.11.6 103 | requests==2.32.3 104 | rpds-py==0.21.0 105 | rsa==4.9 106 | scipy==1.14.1 107 | setuptools==75.5.0 108 | shapely==2.0.6 109 | six==1.16.0 110 | sqlparse==0.5.2 111 | typing_extensions==4.12.2 112 | tzdata==2024.2 113 | urllib3==2.2.3 114 | wheel==0.45.0 115 | wrapt==1.16.0 116 | zipp==3.21.0 117 | zstandard==0.23.0 118 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/requirements_local.in: -------------------------------------------------------------------------------- 1 | annotated-types==0.7.0 2 | apache-beam==2.60.0 3 | attrs==24.2.0 4 | autograd==1.7.0 5 | avro-python3==1.10.2 6 | build==1.2.2.post1 7 | cachetools==5.5.0 8 | certifi==2024.8.30 9 | chardet==5.2.0 10 | charset-normalizer==3.4.0 11 | click==8.1.7 12 | cloudpickle==2.2.1 13 | contourpy==1.3.1 14 | crcmod==1.7 15 | cycler==0.12.1 16 | Deprecated==1.2.15 17 | dill==0.3.1.1 18 | dnspython==2.7.0 19 | docopt==0.6.2 20 | docstring_parser==0.16 21 | fastavro==1.9.7 22 | fasteners==0.19 23 | fonttools==4.55.0 24 | future==1.0.0 25 | grpc-interceptor==0.15.4 26 | grpcio==1.65.5 27 | grpcio-status==1.62.3 28 | hdfs==2.7.3 29 | httplib2==0.22.0 30 | idna==3.10 31 | importlib_metadata==8.5.0 32 | jaraco.classes==3.4.0 33 | jaraco.context==6.0.1 34 | jaraco.functools==4.1.0 35 | jsonpickle==3.4.2 36 | jsonschema==4.23.0 37 | jsonschema-specifications==2024.10.1 38 | keyring==25.5.0 39 | keyrings.google-artifactregistry-auth==1.1.2 40 | kiwisolver==1.4.7 41 | Lifetimes==0.11.3 42 | matplotlib==3.9.2 43 | mock==5.1.0 44 | monotonic==1.6 45 | more-itertools==10.5.0 46 | numpy==1.26.4 47 | oauth2client==4.1.3 48 | objsize==0.7.0 49 | opentelemetry-api==1.28.1 50 | opentelemetry-sdk==1.28.1 51 | opentelemetry-semantic-conventions==0.49b1 52 | orjson==3.10.11 53 | overrides==7.7.0 54 | packaging==24.2 55 | pandas==2.2.3 56 | pbr==6.1.0 57 | pillow==11.0.0 58 | pip-tools==7.4.1 59 | pluggy==1.5.0 60 | proto-plus==1.25.0 61 | protobuf==4.25.5 62 | pyarrow==16.1.0 63 | pyarrow-hotfix==0.6 64 | pyasn1==0.6.1 65 | pyasn1_modules==0.4.1 66 | pydantic==2.9.2 67 | pydantic_core==2.23.4 68 | pydot==1.4.2 69 | pymongo==4.10.1 70 | pyparsing==3.2.0 71 | pyproject_hooks==1.2.0 72 | python-dateutil==2.9.0.post0 73 | python-dotenv==1.0.1 74 | pytz==2024.2 75 | redis==5.2.0 76 | referencing==0.35.1 77 | regex==2024.11.6 78 | requests==2.32.3 79 | rpds-py==0.21.0 80 | rsa==4.9 81 | scipy==1.14.1 82 | setuptools==75.5.0 83 | shapely==2.0.6 84 | six==1.16.0 85 | sqlparse==0.5.2 86 | typing_extensions==4.12.2 87 | tzdata==2024.2 88 | urllib3==2.2.3 89 | wheel==0.45.0 90 | wrapt==1.16.0 91 | zipp==3.21.0 92 | zstandard==0.23.0 93 | 94 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/run_locally.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2021 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | if [ -d "./output" ]; then 18 | rm -fr ./output 19 | fi 20 | mkdir ./output 21 | 22 | python fcvs_pipeline_csv.py --runner=DirectRunner \ 23 | --input_csv ./samples/input_cdnow.csv \ 24 | --output_folder ./output/ \ 25 | --customer_id_column_position 1 \ 26 | --transaction_date_column_position 2 \ 27 | --sales_column_position 4 \ 28 | --date_parsing_pattern YYYY-MM-DD \ 29 | --model_time_granularity weekly \ 30 | --penalizer_coef 0.0 \ 31 | --extra_dimension_column_position 3 32 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/future-customer-value-segments/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 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 | # http://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 | import setuptools 16 | 17 | setuptools.setup( 18 | name='Future-Customer-Value-Segments', 19 | version='1.0.0', 20 | install_requires=[ 21 | 'Lifetimes==0.11.3', 22 | 'matplotlib==3.3.4', 23 | ], 24 | packages=setuptools.find_packages(), 25 | ) 26 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/kfp_pipeline/README.md: -------------------------------------------------------------------------------- 1 | 2 | The purpose of this demo is to showcase KFP with Vertex Pipelines. The pipeline follows these steps: 3 | 1) Build a BigQuery view. This will copy SQL from a GCS bucket, and add parameters 4 | 2) Generate a BQML Logistic Regression Model --> Then review performance and save an image of precision/recall to GCS 5 | 3) Generate a BQML XGBoost Model --> Then review performance and save an image of precision/recall to GCS 6 | 4) Generate a BQML AutoML Model --> Then review performance and save an image of precision/recall to GCS 7 | 5) Generate an XGBoost model using XGB directly. The only purpose of me doing this is to showcase alternative approaches. 8 | 6) Push the XGBoost model to a Vertex AI Endpoint. 9 | 10 | In order to make this run, a few requirements or recommendations: 11 | 1) Using GCP's Vertex AI Workbench (Notebook Offering) will be the easiest way to run this code. Otherwise, you will need to explicitly authenticate 12 | 2) You will need to have access to or build a GCS Bucket 13 | 3) You will need Google BigQuery. This will build a BigQuery View and 3 BigQuery Machine Learning models. 14 | 4) This will also build a GCP Function, and schedule it with GCP Scheduler. 15 | 5) This will also build a model that gets pushed to an endpoint. -------------------------------------------------------------------------------- /marketing-analytics/predicting/kfp_pipeline/output_component/component_file.txt: -------------------------------------------------------------------------------- 1 | Component files will be loaded in this folder -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/CONTRIBUTING: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows 28 | [Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/DataVisualizationPipeline_metadata: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DataVisualizationPipeline", 3 | "description": "Step 2. This is an optional - but recommended - step in the pipeline to output data from Step 1 into BigQuery for exploration before processing it further.", 4 | "parameters": [{ 5 | "name": "inputAvroSessionsLocation", 6 | "label": "Location of the input user Sessions in AVRO format.", 7 | "help_text": "The location of the AVRO files from Step 1. e.g gs://yourbucket/usersession-output/", 8 | "is_optional": false 9 | }, 10 | { 11 | "name": "snapshotStartDate", 12 | "label": "Date of the first snapshot in dd/mm/yyyy format.", 13 | "help_text": "", 14 | "is_optional": false 15 | }, 16 | { 17 | "name": "snapshotEndDate", 18 | "label": "Date of the last snapshot (inclusive) in dd/mm/yyyy format.", 19 | "help_text": "", 20 | "is_optional": false 21 | }, 22 | { 23 | "name": "slideTimeInSeconds", 24 | "label": "Slide Length (seconds).", 25 | "help_text": "The time interval - in seconds - to slide snapshot dates by. e.g 604800 (for 7 days)", 26 | "is_optional": false 27 | }, 28 | { 29 | "name": "minimumLookaheadTimeInSeconds", 30 | "label": "Minimum lookahead time (seconds).", 31 | "help_text": "The time - in seconds - for the prediction window to start from, respective of the current date. e.g 86400 (for the prediction window to start from the next day)", 32 | "is_optional": false 33 | }, 34 | { 35 | "name": "maximumLookaheadTimeInSeconds", 36 | "label": "Maximum lookahead time (seconds).", 37 | "help_text": "The time - in seconds - for the prediction window to end on, respective of the current date. The length of the prediction window being the max - min times. e.g 1209600 (would have a prediction window of 14 days)", 38 | "is_optional": false 39 | }, 40 | { 41 | "name": "stopOnFirstPositiveLabel", 42 | "label": "Set true to stop window generation after the first positive label per user.", 43 | "help_text": "Stop considering a user once they have a positive label. e.g true", 44 | "is_optional": false 45 | }, 46 | { 47 | "name": "outputBigQueryFactsTable", 48 | "label": "Location to write the BigQuery Facts table.", 49 | "help_text": "The location of the BigQuery Facts table (Note: Ensure the pipeline has write access to this). e.g myproject.mydataset.ga_facts_table", 50 | "is_optional": false 51 | }, 52 | { 53 | "name": "outputBigQueryUserActivityTable", 54 | "label": "Location to write the BigQuery UserActivity table.", 55 | "help_text": "The location of the BigQuery Instance table (Note: Ensure the pipeline has write access to this). e.g myproject.mydataset.ga_instance_table", 56 | "is_optional": false 57 | }] 58 | } -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/SessionBasedWindowPipeline_metadata: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SessionBasedWindowPipeline", 3 | "description": "Step 3. Using the AVRO files from Step 1 build snapshots of effective dates sliding at set intervals, windowing the data based on a specified lookback and prediction window.", 4 | "parameters": [{ 5 | "name": "inputAvroSessionsLocation", 6 | "label": "Location of the input user Sessions in AVRO format.", 7 | "help_text": "The location of the AVRO files from Step 1. e.g gs://yourbucket/usersession-output/*.avro", 8 | "is_optional": false 9 | }, 10 | { 11 | "name": "snapshotStartDate", 12 | "label": "Date of the first snapshot in dd/mm/yyyy format.", 13 | "help_text": "", 14 | "is_optional": false 15 | }, 16 | { 17 | "name": "snapshotEndDate", 18 | "label": "Date of the last snapshot (inclusive) in dd/mm/yyyy format.", 19 | "help_text": "", 20 | "is_optional": false 21 | }, 22 | { 23 | "name": "minimumLookaheadTimeInSeconds", 24 | "label": "Minimum lookahead time (seconds).", 25 | "help_text": "The time - in seconds - for the prediction window to start from, respective of the current date. e.g 86400 (for the prediction window to start from the next day)", 26 | "is_optional": false 27 | }, 28 | { 29 | "name": "maximumLookaheadTimeInSeconds", 30 | "label": "Maximum lookahead time (seconds).", 31 | "help_text": "The time - in seconds - for the prediction window to end on, respective of the current date. The length of the prediction window being the max - min times. e.g 1209600 (would have a prediction window of 14 days)", 32 | "is_optional": false 33 | }, 34 | { 35 | "name": "stopOnFirstPositiveLabel", 36 | "label": "Set true to stop window generation after the first positive label per user.", 37 | "help_text": "Stop considering a user once they have a positive label. e.g true", 38 | "is_optional": false 39 | }, 40 | { 41 | "name": "lookbackGapInSeconds", 42 | "label": "Lookback gap (seconds). Sessions within the lookback gap before an effective date are not added to a LookbackWindow.", 43 | "help_text": "Gap - in seconds - to add between the current date and the lookback window, typically this is 1 day to simulate a real life scoring scenario. e.g 86400 (for 1 day)", 44 | "is_optional": false 45 | }, 46 | { 47 | "name": "windowTimeInSeconds", 48 | "label": "Window Length (seconds).", 49 | "help_text": "The size - in seconds - of the lookback window. e.g 7776000 (for 90 days)", 50 | "is_optional": false 51 | }, 52 | { 53 | "name": "outputSessionBasedWindowAvroPrefix", 54 | "label": "Location prefix to write the session-based lookback windows.", 55 | "help_text": "The location on Google Cloud Storage to output the AVRO files to, for windowed data at date intervals based on Google Analytics sessions. e.g gs://yourbucket/windowing-session-output/", 56 | "is_optional": false 57 | }] 58 | } -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/UserSessionPipeline.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline; 16 | 17 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.Session; 18 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform.MapGATableRowToSession; 19 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform.ValidateGATableRow; 20 | import org.apache.beam.sdk.Pipeline; 21 | import org.apache.beam.sdk.io.AvroIO; 22 | import org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO; 23 | import org.apache.beam.sdk.options.PipelineOptionsFactory; 24 | import org.apache.beam.sdk.transforms.ParDo; 25 | 26 | /** 27 | * Pipeline for converting Google Analytics BigQuery data into user Sessions. 28 | */ 29 | public class UserSessionPipeline { 30 | public static void main(String[] args) { 31 | UserSessionPipelineOptions options = 32 | PipelineOptionsFactory.fromArgs(args).withValidation().as(UserSessionPipelineOptions.class); 33 | Pipeline pipeline = Pipeline.create(options); 34 | pipeline 35 | .apply("Read BigQuery GA Table", 36 | BigQueryIO.readTableRows().withTemplateCompatibility().fromQuery( 37 | options.getInputBigQuerySQL()).usingStandardSql().withoutValidation()) 38 | .apply("ValidateGATableRow", ParDo.of(new ValidateGATableRow())) 39 | .apply("MapGATableRowToSession", ParDo.of(new MapGATableRowToSession( 40 | options.getFactsToExtract(), 41 | options.getPredictionFactName(), 42 | options.getPredictionFactValues()))) 43 | .apply(AvroIO.write(Session.class).to( 44 | options.getOutputSessionsAvroPrefix()).withSuffix(".avro")); 45 | pipeline.run(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/UserSessionPipeline_metadata: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UserSessionPipeline", 3 | "description": "Step 1. The pipeline has been optimised to read data directly from the Google Analytics 360 BigQuery output table between a set of specified dates. This stage digests 4 | the Analytics data into AVRO files.", 5 | "parameters": [{ 6 | "name": "inputBigQuerySQL", 7 | "label": "Input BigQuery SQL command for extracting GA Sessions columns.", 8 | "help_text": "The SQL to select from the Google Analytics table in BigQuery (Note: Ensure the pipeline has access to this). e.g SELECT * FROM 'bigquery-public-data.google_analytics_sample.ga_sessions_*`", 9 | "is_optional": false 10 | }, 11 | { 12 | "name": "factsToExtract", 13 | "label": "Comma separated list of fact names to extract. If empty, all facts extracted.", 14 | "help_text": "Comma separated fact names to extract: Example: channelGrouping,socialEngagementType,totals.visits,totals.hits,totals.pageviews,totals.timeOnSite,totals.bounces,totals.transactions,totals.transactionRevenue,totals.newVisits,totals.screenviews,totals.uniqueScreenviews,totals.timeOnScreen,totals.totalTransactionRevenue,totals.sessionQualityDim,trafficSource.source,trafficSource.medium,trafficSource.keyword,device.browser,device.operatingSystem,device.isMobile,device.mobileDeviceBranding,device.deviceCategory,geoNetwork.region,geoNetwork.metro,geoNetwork.city,hits.page.searchKeyword,hits.page.searchCategory,hits.page.pagePathLevel1,hits.page.pagePathLevel2,hits.page.pagePathLevel3,hits.page.pagePathLevel4", 15 | "is_optional": true 16 | }, 17 | { 18 | "name": "predictionFactName", 19 | "label": "Name of the Fact with the prediction target.", 20 | "help_text": "The name of the BigQuery column that contains the value we are trying to predict (can have multiple columns separated by commas). e.g hits.eventInfo.eventAction", 21 | "is_optional": false 22 | }, 23 | { 24 | "name": "predictionFactValues", 25 | "label": "Comma separated list of target values for the prediction Fact.", 26 | "help_text": "The value in the BigQuery column that represents a positive label of what we are trying to predict (can have multiple values separated by commas). e.g Add to Cart", 27 | "is_optional": false 28 | }, 29 | { 30 | "name": "outputSessionsAvroPrefix", 31 | "label": "Location prefix to write all user Sessions in AVRO format.", 32 | "help_text": "The location on Google Cloud Storage to output the AVRO files to. e.g gs://mldatawindowingpipeline/usersession-output/", 33 | "is_optional": false 34 | }] 35 | } -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/cloud_build.json: -------------------------------------------------------------------------------- 1 | { 2 | "steps": [ 3 | { 4 | "name": "gcr.io/cloud-builders/mvn", 5 | "args": 6 | [ 7 | "-Pdataflow-runner", 8 | "compile", 9 | "exec:java", 10 | "-D_MAIN_CLASS=UserSessionPipeline", 11 | "-D_PROJECT_ID=$PROJECT_ID", 12 | "-D_OUTPUT_LOCATION=gs://${_BUCKET_NAME}/mlwp_templates", 13 | "-D_TEMP_LOCATION=gs://${_BUCKET_NAME}/mlwp_templates/source", 14 | "-Ddataflow.use-public-ips=false" 15 | ], 16 | "id": "build-user-pipeline", 17 | "waitFor": ["-"] 18 | }, 19 | { 20 | "name": "gcr.io/cloud-builders/mvn", 21 | "args": 22 | [ 23 | "-Pdataflow-runner", 24 | "compile", 25 | "exec:java", 26 | "-D_MAIN_CLASS=DataVisualizationPipeline", 27 | "-D_PROJECT_ID=$PROJECT_ID", 28 | "-D_OUTPUT_LOCATION=gs://${_BUCKET_NAME}/mlwp_templates", 29 | "-D_TEMP_LOCATION=gs://${_BUCKET_NAME}/mlwp_templates/source", 30 | "-Ddataflow.use-public-ips=false" 31 | ], 32 | "id": "build-visualization-pipeline", 33 | "waitFor": ["-"] 34 | }, 35 | { 36 | "name": "gcr.io/cloud-builders/mvn", 37 | "args": 38 | [ 39 | "-Pdataflow-runner", 40 | "compile", 41 | "exec:java", 42 | "-D_MAIN_CLASS=SlidingWindowPipeline", 43 | "-D_PROJECT_ID=$PROJECT_ID", 44 | "-D_OUTPUT_LOCATION=gs://${_BUCKET_NAME}/mlwp_templates", 45 | "-D_TEMP_LOCATION=gs://${_BUCKET_NAME}/mlwp_templates/source", 46 | "-Ddataflow.use-public-ips=false" 47 | ], 48 | "id": "build-slidingwindowing-pipeline", 49 | "waitFor": ["-"] 50 | }, 51 | { 52 | "name": "gcr.io/cloud-builders/mvn", 53 | "args": 54 | [ 55 | "-Pdataflow-runner", 56 | "compile", 57 | "exec:java", 58 | "-D_MAIN_CLASS=SessionBasedWindowPipeline", 59 | "-D_PROJECT_ID=$PROJECT_ID", 60 | "-D_OUTPUT_LOCATION=gs://${_BUCKET_NAME}/mlwp_templates", 61 | "-D_TEMP_LOCATION=gs://${_BUCKET_NAME}/mlwp_templates/source", 62 | "-Ddataflow.use-public-ips=false" 63 | ], 64 | "id": "build-sessionwindowing-pipeline", 65 | "waitFor": ["-"] 66 | }, 67 | { 68 | "name": "gcr.io/cloud-builders/mvn", 69 | "args": 70 | [ 71 | "-Pdataflow-runner", 72 | "compile", 73 | "exec:java", 74 | "-D_MAIN_CLASS=GenerateFeaturesPipeline", 75 | "-D_PROJECT_ID=$PROJECT_ID", 76 | "-D_OUTPUT_LOCATION=gs://${_BUCKET_NAME}/mlwp_templates", 77 | "-D_TEMP_LOCATION=gs://${_BUCKET_NAME}/mlwp_templates/source", 78 | "-Ddataflow.use-public-ips=false" 79 | ], 80 | "id": "build-features-pipeline", 81 | "waitFor": ["-"] 82 | } 83 | ], 84 | "artifacts": { 85 | "objects": { 86 | "location": "gs://${_BUCKET_NAME}/mlwp_templates", 87 | "paths": [ 88 | "*_metadata" 89 | ] 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/accumulator/AccumulatorType.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator; 16 | 17 | /** Accumulator types for generating features in {@code GenerateFeaturesPipeline}. */ 18 | public enum AccumulatorType { 19 | PROPORTION(true), 20 | MOST_FREQUENT(false, true), 21 | SUM(false, true), 22 | AVERAGE(false, true), 23 | COUNT(false), 24 | AVERAGE_BY_TENURE(false /* valueListRequired */, true /* singleOutput */, true /* windowBased */), 25 | RECENT(false, true); 26 | 27 | private final boolean valueListRequired; 28 | private final boolean singleOutput; 29 | private final boolean windowBased; 30 | 31 | AccumulatorType(boolean valueListRequired) { 32 | this(valueListRequired, false, false); 33 | } 34 | 35 | AccumulatorType(boolean valueListRequired, boolean singleOutput) { 36 | this(valueListRequired, singleOutput, false); 37 | } 38 | 39 | AccumulatorType(boolean valueListRequired, boolean singleOutput, boolean windowBased) { 40 | this.valueListRequired = valueListRequired; 41 | this.singleOutput = singleOutput; 42 | this.windowBased = windowBased; 43 | } 44 | 45 | public boolean isValueListRequired() { 46 | return valueListRequired; 47 | } 48 | 49 | public boolean isSingleOutput() { 50 | return singleOutput; 51 | } 52 | 53 | public boolean isWindowBased() { 54 | return windowBased; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/accumulator/AverageByTenureValueFeatureAccumulator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator; 16 | 17 | import com.google.common.collect.ImmutableMap; 18 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.LookbackWindow; 19 | import java.util.Map; 20 | import org.joda.time.Duration; 21 | 22 | /** 23 | * Feature accumulator to extract the average by the tenure value of a fact. Example: Extract the 24 | * average value of the fact 'page_views' as a feature. 25 | */ 26 | public class AverageByTenureValueFeatureAccumulator extends WindowBasedFeatureAccumulator { 27 | 28 | private double sum; 29 | 30 | public AverageByTenureValueFeatureAccumulator( 31 | String column, ImmutableMap values, String defaultValue) { 32 | super(column, values, defaultValue); 33 | } 34 | 35 | @Override 36 | public Map getFeatures() { 37 | String featureName = getValueToFeatureName().values().iterator().next(); 38 | return ImmutableMap.of(featureName, roundedFeatureValue(sum / Math.max(getTenure(), 1))); 39 | } 40 | 41 | @Override 42 | public void accumulate(String value) { 43 | super.accumulate(value); 44 | sum += Double.parseDouble(value); 45 | } 46 | 47 | // Window size or the period the instance has been active in the window whichever is lesser. 48 | // Example: Window size is 30 days but the instance has only activity for the last 10 days of that 49 | // window. For this case, tenure is 10 days. 50 | private long getTenure() { 51 | final LookbackWindow window = getWindow(); 52 | 53 | // The difference of window's endTime and startTime. 54 | long windowSizeInDays = 55 | new Duration(window.getStartTime(), window.getEndTime()).getStandardDays(); 56 | long activityPeriodInDays = 57 | new Duration( 58 | window.getFirstActivityTime().getMillis(), window.getEffectiveDate().getMillis()) 59 | .getStandardDays(); 60 | 61 | return Math.min(windowSizeInDays, activityPeriodInDays); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/accumulator/AverageValueFeatureAccumulator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator; 16 | 17 | import com.google.common.collect.ImmutableMap; 18 | import java.util.Map; 19 | 20 | /** 21 | * Feature accumulator to extract the average value of a fact. Example: Extract the average value of 22 | * the fact 'page_views' as a feature. 23 | */ 24 | public class AverageValueFeatureAccumulator extends FeatureAccumulator { 25 | 26 | private double sum; 27 | private int count; 28 | 29 | public AverageValueFeatureAccumulator( 30 | String column, ImmutableMap valueToFeatureName, String defaultValue) { 31 | super(column, valueToFeatureName, defaultValue); 32 | } 33 | 34 | @Override 35 | public void accumulate(String value) { 36 | count++; 37 | try { 38 | sum += Double.parseDouble(value); 39 | } catch (NumberFormatException e) { 40 | // Ignore invalid value. 41 | } 42 | } 43 | 44 | @Override 45 | public Map getFeatures() { 46 | String columnName = getValueToFeatureName().values().iterator().next(); 47 | return ImmutableMap.of(columnName, roundedFeatureValue(sum / Math.max(count, 1))); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/accumulator/CountValueFeatureAccumulator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator; 16 | 17 | import com.google.common.base.Strings; 18 | import com.google.common.collect.ImmutableMap; 19 | import java.util.Map; 20 | import org.apache.commons.collections4.Bag; 21 | import org.apache.commons.collections4.bag.HashBag; 22 | 23 | /** 24 | * Feature accumulator to extract the sum of values of a fact. Example: Extract the count of 'New 25 | * York' from all the values of the fact 'city' as a feature. 26 | */ 27 | public class CountValueFeatureAccumulator extends FeatureAccumulator { 28 | 29 | private final Bag valueBag; 30 | 31 | public CountValueFeatureAccumulator( 32 | String column, ImmutableMap valueToFeatureName, String defaultValue) { 33 | super(column, valueToFeatureName, defaultValue); 34 | valueBag = new HashBag<>(); 35 | } 36 | 37 | @Override 38 | public void accumulate(String value) { 39 | if (!getValueToFeatureName().containsKey(value) && !Strings.isNullOrEmpty(getDefaultValue())) { 40 | valueBag.add(getDefaultValue()); 41 | } else { 42 | valueBag.add(value); 43 | } 44 | } 45 | 46 | @Override 47 | public Map getFeatures() { 48 | 49 | ImmutableMap.Builder features = ImmutableMap.builder(); 50 | 51 | for (Map.Entry valueEntry : getValueToFeatureName().entrySet()) { 52 | // Get all the count if valueToFeatureName's key and factName are equal and there is only 1 53 | // value. 54 | if (valueEntry.getKey().equals(getFactName()) && getValueToFeatureName().size() == 1) { 55 | features.put(valueEntry.getValue(), valueBag.size()); 56 | } else { 57 | features.put(valueEntry.getValue(), valueBag.getCount(valueEntry.getKey())); 58 | } 59 | } 60 | return features.build(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/accumulator/FeatureAccumulator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator; 16 | 17 | import java.math.BigDecimal; 18 | import java.util.Map; 19 | 20 | /** 21 | * An object that accumulates values to generate feature or features for a ML-ready dataset. 22 | * 23 | *

{@code Example: factName = "browser" valueToFeatureName = [("Chrome", 24 | * "PROPORTION_browser_Chrome"),("Safari", "PROPORTION_browser_Safari")]} 25 | */ 26 | public abstract class FeatureAccumulator { 27 | 28 | // Map of value and its corresponding feature name. 29 | private final Map valueToFeatureName; 30 | 31 | // Fact that the feature is based on. 32 | private final String factName; 33 | 34 | // Value to accumulate if value is not in the valueToFeatureName map. 35 | private final String defaultValue; 36 | 37 | protected FeatureAccumulator( 38 | String factName, Map valueToFeatureName, String defaultValue) { 39 | 40 | this.factName = factName; 41 | this.valueToFeatureName = valueToFeatureName; 42 | this.defaultValue = defaultValue; 43 | } 44 | 45 | /** Accumulates value of a fact. */ 46 | public abstract void accumulate(String value); 47 | 48 | /** Returns a map of features and their corresponding accumulated value. */ 49 | public abstract Map getFeatures(); 50 | 51 | protected String getFactName() { 52 | return factName; 53 | } 54 | 55 | protected Map getValueToFeatureName() { 56 | return valueToFeatureName; 57 | } 58 | 59 | protected String getDefaultValue() { 60 | return defaultValue; 61 | } 62 | 63 | protected static double roundedFeatureValue(double number) { 64 | return new BigDecimal(number).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/accumulator/MostFrequentValueFeatureAccumulator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator; 16 | 17 | import static java.util.Comparator.reverseOrder; 18 | 19 | import com.google.common.base.Strings; 20 | import com.google.common.collect.ImmutableMap; 21 | import java.util.Comparator; 22 | import java.util.Map; 23 | import java.util.Optional; 24 | import org.apache.commons.collections4.Bag; 25 | import org.apache.commons.collections4.bag.HashBag; 26 | 27 | /** 28 | * Feature accumulator to extract most frequent value among the values provided of a fact. Example: 29 | * Extract the most frequent value of the fact 'city' as a feature. 30 | */ 31 | public class MostFrequentValueFeatureAccumulator extends FeatureAccumulator { 32 | 33 | private final Bag valueBag; 34 | 35 | public MostFrequentValueFeatureAccumulator( 36 | String column, ImmutableMap valueToFeatureName, String defaultValue) { 37 | super(column, valueToFeatureName, defaultValue); 38 | this.valueBag = new HashBag<>(); 39 | } 40 | 41 | @Override 42 | public void accumulate(String value) { 43 | if (getValueToFeatureName().containsKey(getFactName()) 44 | || getValueToFeatureName().containsKey(value)) { 45 | valueBag.add(value); 46 | } else if (!Strings.isNullOrEmpty(getDefaultValue())) { 47 | valueBag.add(getDefaultValue()); 48 | } 49 | } 50 | 51 | @Override 52 | public Map getFeatures() { 53 | 54 | if (getValueToFeatureName().isEmpty()) { 55 | return ImmutableMap.of(); 56 | } 57 | 58 | Optional valueWithMaxCount = 59 | valueBag.stream() 60 | .max( 61 | Comparator.comparingInt(valueBag::getCount) 62 | .thenComparing(s -> s, reverseOrder())); 63 | String featureName = getValueToFeatureName().values().iterator().next(); 64 | return ImmutableMap.of(featureName, valueWithMaxCount.orElse("")); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/accumulator/ProportionValueFeatureAccumulator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator; 16 | 17 | import com.google.common.base.Strings; 18 | import com.google.common.collect.ImmutableMap; 19 | import java.util.Map; 20 | import org.apache.commons.collections4.Bag; 21 | import org.apache.commons.collections4.bag.HashBag; 22 | 23 | /** 24 | * Feature accumulator to extract the proportion of values of a fact. Example: Extract the 25 | * proportion value of New York from all the values of the fact 'city' as a feature. 26 | */ 27 | public class ProportionValueFeatureAccumulator extends FeatureAccumulator { 28 | 29 | private final Bag valueBag; 30 | 31 | public ProportionValueFeatureAccumulator( 32 | String column, ImmutableMap valueToFeatureName, String defaultValue) { 33 | super(column, valueToFeatureName, defaultValue); 34 | this.valueBag = new HashBag<>(); 35 | } 36 | 37 | @Override 38 | public void accumulate(String value) { 39 | if (!Strings.isNullOrEmpty(getDefaultValue()) && !getValueToFeatureName().containsKey(value)) { 40 | valueBag.add(getDefaultValue()); 41 | } else { 42 | valueBag.add(value); 43 | } 44 | } 45 | 46 | @Override 47 | public Map getFeatures() { 48 | 49 | ImmutableMap.Builder features = ImmutableMap.builder(); 50 | int totalValueCount = valueBag.size(); 51 | 52 | for (Map.Entry valueEntry : getValueToFeatureName().entrySet()) { 53 | int valueCount = valueBag.getCount(valueEntry.getKey()); 54 | features.put( 55 | valueEntry.getValue(), 56 | totalValueCount > 0 ? roundedFeatureValue((double) valueCount / totalValueCount) : 0); 57 | } 58 | return features.build(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/accumulator/RecentValueFeatureAccumulator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator; 16 | 17 | import static com.google.common.base.Strings.nullToEmpty; 18 | 19 | import com.google.common.base.Strings; 20 | import com.google.common.collect.ImmutableMap; 21 | import java.util.Map; 22 | 23 | /** Feature accumulator to extract the most recent value of a fact. */ 24 | public class RecentValueFeatureAccumulator extends FeatureAccumulator { 25 | 26 | private String value; 27 | 28 | public RecentValueFeatureAccumulator( 29 | String column, ImmutableMap values, String defaultValue) { 30 | super(column, values, defaultValue); 31 | } 32 | 33 | @Override 34 | public void accumulate(String value) { 35 | if (getValueToFeatureName().containsKey(getFactName()) 36 | || getValueToFeatureName().containsKey(value)) { 37 | this.value = value; 38 | } else if (!Strings.isNullOrEmpty(getDefaultValue())) { 39 | this.value = getDefaultValue(); 40 | } 41 | } 42 | 43 | @Override 44 | public Map getFeatures() { 45 | String featureName = getValueToFeatureName().values().iterator().next(); 46 | return ImmutableMap.of(featureName, nullToEmpty(value)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/accumulator/SumValueFeatureAccumulator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator; 16 | 17 | import com.google.common.collect.ImmutableMap; 18 | import java.util.Map; 19 | 20 | /** 21 | * Feature accumulator to extract the sum of values of a fact. Example: Extract the sum of all the 22 | * values of the fact 'page_views' as a feature. 23 | */ 24 | public class SumValueFeatureAccumulator extends FeatureAccumulator { 25 | 26 | private double sum; 27 | 28 | public SumValueFeatureAccumulator( 29 | String column, ImmutableMap valueToFeatureName, String defaultValue) { 30 | super(column, valueToFeatureName, defaultValue); 31 | } 32 | 33 | @Override 34 | public void accumulate(String value) { 35 | try { 36 | sum += Double.parseDouble(value); 37 | } catch (NumberFormatException e) { 38 | // Ignore invalid value. 39 | } 40 | } 41 | 42 | @Override 43 | public Map getFeatures() { 44 | String columnName = getValueToFeatureName().values().iterator().next(); 45 | return ImmutableMap.of(columnName, sum); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/accumulator/WindowBasedFeatureAccumulator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator; 16 | 17 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.LookbackWindow; 18 | import java.util.Map; 19 | 20 | /** 21 | * An object that accumulates values to generate feature or features based on {@link LookbackWindow} 22 | * for a ML-ready dataset. 23 | */ 24 | public abstract class WindowBasedFeatureAccumulator extends FeatureAccumulator { 25 | 26 | private LookbackWindow window; 27 | 28 | public WindowBasedFeatureAccumulator( 29 | String column, Map values, String defaultValue) { 30 | super(column, values, defaultValue); 31 | } 32 | 33 | @Override 34 | public void accumulate(String value) { 35 | if (window == null) { 36 | throw new IllegalStateException("LookbackWindow is not initialized."); 37 | } 38 | } 39 | 40 | public LookbackWindow getWindow() { 41 | return window; 42 | } 43 | 44 | public void setWindow(LookbackWindow window) { 45 | this.window = window; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/feature/transform/CreateAccumulatorOptionsFn.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.transform; 16 | 17 | import com.google.common.collect.Multimap; 18 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator.AccumulatorOptions; 19 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator.AccumulatorType; 20 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.feature.accumulator.FeatureAccumulatorFactory; 21 | import java.util.Map; 22 | import org.apache.beam.sdk.transforms.DoFn; 23 | import org.apache.beam.sdk.values.KV; 24 | 25 | /** Function to create @{link {@link AccumulatorOptions}} based string parameter. */ 26 | public class CreateAccumulatorOptionsFn extends DoFn> { 27 | private final FeatureAccumulatorFactory factory; 28 | private final AccumulatorType accumulatorType; 29 | 30 | public CreateAccumulatorOptionsFn( 31 | FeatureAccumulatorFactory factory, AccumulatorType accumulatorType) { 32 | this.factory = factory; 33 | this.accumulatorType = accumulatorType; 34 | } 35 | 36 | @ProcessElement 37 | public void processElement(ProcessContext c) { 38 | Multimap accumulators = 39 | factory.createAccumulatorOptions(accumulatorType, c.element()); 40 | for (Map.Entry accumulatorMapEntry : accumulators.entries()) { 41 | c.output(KV.of(accumulatorMapEntry.getKey(), accumulatorMapEntry.getValue())); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/model/Field.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model; 16 | 17 | import com.google.common.base.MoreObjects; 18 | import java.io.Serializable; 19 | import java.util.Arrays; 20 | import java.util.Objects; 21 | 22 | /** A Field records a feature column name, description and type. */ 23 | public class Field implements Serializable { 24 | 25 | private final String name; 26 | private final String description; 27 | private final String type; 28 | 29 | public Field(String name, String description, String type) { 30 | this.name = name; 31 | this.description = description; 32 | this.type = type; 33 | } 34 | 35 | public String getName() { 36 | return name; 37 | } 38 | 39 | public String getDescription() { 40 | return description; 41 | } 42 | 43 | public String getType() { 44 | return type; 45 | } 46 | 47 | @Override 48 | public boolean equals(Object other) { 49 | if (this == other) { 50 | return true; 51 | } 52 | if (!(other instanceof Field)) { 53 | return false; 54 | } 55 | Field otherField = (Field) other; 56 | return Objects.equals(this.name, otherField.name) 57 | && Objects.equals(this.type, otherField.type) 58 | && Objects.equals(this.description, otherField.description); 59 | } 60 | 61 | @Override 62 | public int hashCode() { 63 | return Arrays.hashCode(new Object[] {name, description, type}); 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return MoreObjects.toStringHelper(this) 69 | .addValue(name) 70 | .addValue(description) 71 | .addValue(type) 72 | .toString(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/transform/DateUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform; 16 | 17 | import java.time.Duration; 18 | import java.time.LocalDate; 19 | import java.time.format.DateTimeFormatter; 20 | import org.joda.time.Instant; 21 | 22 | /** 23 | * Utility class for parsing command line date strings into Instants. 24 | */ 25 | final class DateUtil { 26 | private DateUtil() { 27 | } 28 | 29 | // Returns the given dateString in dd/MM/yyyy format as an Instant. 30 | private static Instant parseDateStringToInstantOrDie(String dateString) { 31 | try { 32 | return new Instant( 33 | 1000 * LocalDate.parse(dateString, DateTimeFormatter.ofPattern("dd/MM/yyyy")).toEpochDay() 34 | * Duration.ofDays(1).getSeconds()); 35 | } catch (Exception e) { 36 | throw new RuntimeException(e); 37 | } 38 | } 39 | 40 | // Returns the given stateDateString in dd/MM/yyyy format as an Instant. If the input is null or 41 | // empty, returns the Epoch Instant. Dies if the input is invalid. 42 | public static Instant parseStartDateStringToInstant(String stateDateString) { 43 | Instant startInstant = new Instant(0); 44 | if (stateDateString != null && !stateDateString.isEmpty()) { 45 | startInstant = parseDateStringToInstantOrDie(stateDateString); 46 | } 47 | return startInstant; 48 | } 49 | 50 | // Returns the given endDateString in dd/MM/yyyy format as an Instant. If the input is null or 51 | // empty, returns the maximum possible Instant. Dies if the input is invalid. 52 | public static Instant parseEndDateStringToInstant(String endDateString) { 53 | Instant endInstant = new Instant(Long.MAX_VALUE); 54 | if (endDateString != null && !endDateString.isEmpty()) { 55 | endInstant = parseDateStringToInstantOrDie(endDateString); 56 | } 57 | return endInstant; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/transform/MapFactToTableRow.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform; 16 | 17 | import com.google.api.services.bigquery.model.TableFieldSchema; 18 | import com.google.api.services.bigquery.model.TableRow; 19 | import com.google.api.services.bigquery.model.TableSchema; 20 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.Fact; 21 | import java.util.Arrays; 22 | import org.apache.beam.sdk.transforms.DoFn; 23 | 24 | /** 25 | * Converts a Fact to a BigQuery TableRow. 26 | */ 27 | public class MapFactToTableRow extends DoFn { 28 | 29 | // Returns the table schema for Fact TableRows. 30 | public static TableSchema getTableSchema() { 31 | TableSchema schema = new TableSchema(); 32 | schema.setFields(Arrays.asList( 33 | new TableFieldSchema().setName("sessionId").setType("STRING"), 34 | new TableFieldSchema().setName("userId").setType("STRING"), 35 | new TableFieldSchema().setName("timeInMillis").setType("INTEGER"), 36 | new TableFieldSchema().setName("name").setType("STRING"), 37 | new TableFieldSchema().setName("value").setType("STRING"), 38 | new TableFieldSchema().setName("hasPositiveLabel").setType("BOOLEAN"))); 39 | return schema; 40 | } 41 | 42 | @ProcessElement 43 | public void processElement(ProcessContext context) { 44 | Fact fact = context.element(); 45 | TableRow tablerow = new TableRow(); 46 | tablerow.set("sessionId", fact.getSessionId()); 47 | tablerow.set("userId", fact.getUserId()); 48 | tablerow.set("timeInMillis", fact.getTime().getMillis()); 49 | tablerow.set("name", fact.getName()); 50 | tablerow.set("value", fact.getValue()); 51 | tablerow.set("hasPositiveLabel", fact.getHasPositiveLabel()); 52 | context.output(tablerow); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/transform/MapSessionToFacts.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform; 16 | 17 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.Fact; 18 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.Session; 19 | import org.apache.beam.sdk.transforms.DoFn; 20 | 21 | /** 22 | * Maps a Session into its Facts. 23 | */ 24 | public class MapSessionToFacts extends DoFn { 25 | @ProcessElement 26 | public void processElement(ProcessContext context) { 27 | Session session = context.element(); 28 | for (Fact fact : session.getFacts()) { 29 | context.output(fact); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/transform/MapSortedSessionsIntoLookbackWindows.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform; 16 | 17 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.LookbackWindow; 18 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.Session; 19 | import java.util.List; 20 | import org.apache.beam.sdk.options.ValueProvider; 21 | import org.apache.beam.sdk.transforms.DoFn; 22 | import org.apache.beam.sdk.values.KV; 23 | 24 | /** 25 | * Parent class for mapping a user's sessions time into LookbackWindows. 26 | */ 27 | public class MapSortedSessionsIntoLookbackWindows extends DoFn< 28 | KV>, LookbackWindow> { 29 | protected ValueProvider snapshotStartDateProvider; 30 | protected ValueProvider snapshotEndDateProvider; 31 | protected ValueProvider lookbackGapInSecondsProvider; 32 | protected ValueProvider windowTimeInSecondsProvider; 33 | protected ValueProvider minimumLookaheadTimeInSecondsProvider; 34 | protected ValueProvider maximumLookaheadTimeInSecondsProvider; 35 | protected ValueProvider stopOnFirstPositiveLabelProvider; 36 | 37 | public MapSortedSessionsIntoLookbackWindows( 38 | ValueProvider snapshotStartDate, 39 | ValueProvider snapshotEndDate, 40 | ValueProvider lookbackGapInSeconds, 41 | ValueProvider windowTimeInSeconds, 42 | ValueProvider minimumLookaheadTimeInSeconds, 43 | ValueProvider maximumLookaheadTimeInSeconds, 44 | ValueProvider stopOnFirstPositiveLabel) { 45 | snapshotStartDateProvider = snapshotStartDate; 46 | snapshotEndDateProvider = snapshotEndDate; 47 | lookbackGapInSecondsProvider = lookbackGapInSeconds; 48 | windowTimeInSecondsProvider = windowTimeInSeconds; 49 | minimumLookaheadTimeInSecondsProvider = minimumLookaheadTimeInSeconds; 50 | maximumLookaheadTimeInSecondsProvider = maximumLookaheadTimeInSeconds; 51 | stopOnFirstPositiveLabelProvider = stopOnFirstPositiveLabel; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/transform/MapUserActivityToTableRow.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform; 16 | 17 | import com.google.api.services.bigquery.model.TableFieldSchema; 18 | import com.google.api.services.bigquery.model.TableRow; 19 | import com.google.api.services.bigquery.model.TableSchema; 20 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.UserActivity; 21 | import java.util.Arrays; 22 | import org.apache.beam.sdk.transforms.DoFn; 23 | 24 | /** 25 | * Converts a UserActivity to a BigQuery TableRow if the user has had some activity as of the 26 | * UserActivty's snapshotTime. 27 | */ 28 | public class MapUserActivityToTableRow extends DoFn { 29 | 30 | // Returns the table schema for UserActivity TableRows. 31 | public static TableSchema getTableSchema() { 32 | TableSchema schema = new TableSchema(); 33 | schema.setFields(Arrays.asList( 34 | new TableFieldSchema().setName("userId").setType("STRING"), 35 | new TableFieldSchema().setName("hasPositiveLabel").setType("BOOLEAN"), 36 | new TableFieldSchema().setName("daysSinceStartDate").setType("INTEGER"), 37 | new TableFieldSchema().setName("daysSinceFirstActivity").setType("INTEGER"), 38 | new TableFieldSchema().setName("daysSinceLatestActivity").setType("INTEGER"), 39 | new TableFieldSchema().setName("snapshotTimeInMillis").setType("INTEGER"))); 40 | return schema; 41 | } 42 | 43 | @ProcessElement 44 | public void processElement(ProcessContext context) { 45 | UserActivity userActivity = context.element(); 46 | if (userActivity.getDurationSinceFirstActivity() == null) { 47 | return; 48 | } 49 | TableRow tablerow = new TableRow(); 50 | tablerow.set("userId", userActivity.getUserId()); 51 | tablerow.set("hasPositiveLabel", userActivity.getHasPositiveLabel()); 52 | tablerow.set("daysSinceStartDate", userActivity.getDurationSinceStartDate().getStandardDays()); 53 | tablerow.set( 54 | "daysSinceFirstActivity", userActivity.getDurationSinceFirstActivity().getStandardDays()); 55 | tablerow.set( 56 | "daysSinceLatestActivity", userActivity.getDurationSinceLatestActivity().getStandardDays()); 57 | tablerow.set("snapshotTimeInMillis", userActivity.getSnapshotTime().getMillis()); 58 | context.output(tablerow); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/transform/MapUserIdToSession.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform; 16 | 17 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.Session; 18 | import org.apache.beam.sdk.transforms.DoFn; 19 | import org.apache.beam.sdk.values.KV; 20 | 21 | /** 22 | * Maps from a Session to pairs of (userId, Session), so that Sessions can be grouped by userId. 23 | */ 24 | public class MapUserIdToSession extends DoFn> { 25 | public MapUserIdToSession() { 26 | } 27 | 28 | @ProcessElement 29 | public void processElement(ProcessContext context) { 30 | Session session = context.element(); 31 | context.output(KV.of(session.getUserId(), session)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/transform/SortSessionsByTime.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform; 16 | 17 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.Session; 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.Comparator; 21 | import java.util.List; 22 | import org.apache.beam.sdk.transforms.DoFn; 23 | import org.apache.beam.sdk.values.KV; 24 | 25 | /** 26 | * Given a collection of Sessions for a user, outputs the Sessions sorted by time. 27 | */ 28 | public class SortSessionsByTime extends DoFn< 29 | KV>, KV>> { 30 | 31 | public SortSessionsByTime() { 32 | } 33 | 34 | @ProcessElement 35 | public void processElement(ProcessContext context) { 36 | KV> kv = context.element(); 37 | ArrayList sessions = new ArrayList<>(); 38 | for (Session session : kv.getValue()) { 39 | sessions.add(session); 40 | } 41 | if (sessions.isEmpty()) { 42 | return; 43 | } 44 | Collections.sort(sessions, new Comparator() { 45 | @Override 46 | public int compare(Session lhs, Session rhs) { 47 | int result = lhs.getLastHitTime().compareTo(rhs.getLastHitTime()); 48 | if (result == 0) { 49 | return lhs.getVisitStartTime().compareTo(rhs.getVisitStartTime()); 50 | } 51 | return result; 52 | } 53 | }); 54 | context.output(KV.of(kv.getKey(), sessions)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/transform/SortedSessionsUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform; 16 | 17 | import com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.model.Session; 18 | import java.util.ArrayList; 19 | import org.joda.time.Duration; 20 | import org.joda.time.Instant; 21 | 22 | /** 23 | * Utility functions for processing Sessions sorted by time. 24 | */ 25 | public class SortedSessionsUtil { 26 | 27 | private SortedSessionsUtil() { 28 | } 29 | 30 | // Returns the collection of positive label time instants for the given Sessions between the 31 | // given start (inclusive) and end instant (not inclusive). 32 | public static ArrayList getPositiveLabelTimes( 33 | ArrayList sessions, Instant startTime, Instant endTime) { 34 | ArrayList positiveLabelTimes = new ArrayList<>(); 35 | for (Session session : sessions) { 36 | if (!session.hasPositiveLabel()) { 37 | continue; 38 | } 39 | if (session.getVisitStartTime().isBefore(startTime) 40 | || !session.getLastHitTime().isBefore(endTime)) { 41 | continue; 42 | } 43 | positiveLabelTimes.add(session.getVisitStartTime()); 44 | } 45 | return positiveLabelTimes; 46 | } 47 | 48 | // Returns the first Instant in the collection between the start and end time inclusive. 49 | // Assumes the given instants collection is in sorted order. 50 | public static Instant getFirstInstantInInterval( 51 | ArrayList instants, Instant start, Instant finish) { 52 | for (Instant instant : instants) { 53 | if (instant.isAfter(finish)) { 54 | break; 55 | } 56 | if (!instant.isBefore(start)) { 57 | return instant; 58 | } 59 | } 60 | return null; 61 | } 62 | 63 | // Returns the day offset from Epoch for the given instant. 64 | public static long getEpochDay(Instant instant) { 65 | return Duration.millis(instant.getMillis()).getStandardDays(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/ml-data-windowing-pipeline/transform/ValidateGATableRow.java: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | // http://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 | package com.google.corp.gtech.ads.datacatalyst.components.mldatawindowingpipeline.transform; 16 | 17 | import com.google.api.services.bigquery.model.TableRow; 18 | import org.apache.beam.sdk.transforms.DoFn; 19 | 20 | /** 21 | * Validates the BigQuery TableRows contains required field names. 22 | */ 23 | public class ValidateGATableRow extends DoFn { 24 | public ValidateGATableRow() { 25 | } 26 | 27 | // Converts BigQuery TableRows from Google Analytics to Sessions. 28 | @ProcessElement 29 | public void processElement(ProcessContext context) { 30 | TableRow tablerow = context.element(); 31 | if (!tablerow.containsKey("fullVisitorId") 32 | || !tablerow.containsKey("visitId") 33 | || !tablerow.containsKey("visitStartTime")) { 34 | throw new IllegalArgumentException(String.format( 35 | "Input tablerow [%s] is missing at least one of the following required fields: %s", 36 | tablerow, "fullVisitorId, visitId or visitStartTime")); 37 | } 38 | context.output(tablerow); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /marketing-analytics/predicting/tensorflow-lifetime-value/README.md: -------------------------------------------------------------------------------- 1 | This folder refers to a solution published in another Github repository about predicting Customer Lifetime Value using two main approaches: 2 | 3 | 1. RFM statistical models 4 | 2. Neural network models 5 | 6 | You can find the code at [https://github.com/GoogleCloudPlatform/tensorflow-lifetime-value](https://github.com/GoogleCloudPlatform/tensorflow-lifetime-value) 7 | 8 | The Github repository is a companion code to a four-part series that discusses how you can predict customer lifetime value (CLV) by using AI Platform (AI Platform) on Google Cloud. The articles in this series include the following: 9 | 10 | - [Part 1: Introduction](https://cloud.google.com/solutions/machine-learning/clv-prediction-with-offline-training-intro). Introduces customer lifetime value (CLV) and two modeling techniques for predicting CLV. 11 | - [Part 2: Training the model](https://cloud.google.com/solutions/machine-learning/clv-prediction-with-offline-training-train). Discusses how to prepare the data and train the models. 12 | - [Part 3: Deploying to production](https://cloud.google.com/solutions/machine-learning/clv-prediction-with-offline-training-deploy). Describes how to deploy the models discussed in Part 2 to a production system. 13 | - [Part 4: Using AutoML Tables](https://cloud.google.com/solutions/machine-learning/clv-prediction-with-automl-tables). Shows how to use AutoML Tables to build and deploy a model. 14 | 15 | Disclaimer: This is not an official Google product. -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/README.md: -------------------------------------------------------------------------------- 1 | This repository exists to facilitate the access to BigQuery queries on various data sources. 2 | 3 | Several of those data sources are imported into BigQuery using [BigQuery Data Transfer Service](https://cloud.google.com/bigquery/transfer/) but can also be done using various [BigQuery loading tools](https://cloud.google.com/bigquery/docs/loading-data). 4 | 5 | Note: This is not an officially supported Google product. -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/dv360/click_through_rate.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2018 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 | -- This script computes the impression and click count as well as the 16 | -- click-through rate per ad. It uses the impression ID field to join the 17 | -- impression and click table. 18 | WITH 19 | ads AS ( 20 | SELECT 21 | Ad_ID, 22 | ANY_VALUE(Ad) As Ad 23 | FROM 24 | `project.dataset.match_table_ads` 25 | GROUP BY 26 | Ad_ID) 27 | SELECT 28 | impressions.Ad_ID, 29 | ANY_VALUE(ads.Ad) AS Ad, 30 | COUNT(*) AS impression_count, 31 | COUNTIF(clicks.Impression_ID IS NOT NULL) AS click_count, 32 | COUNTIF(clicks.Impression_ID IS NOT NULL) / COUNT(*) AS click_through_rate 33 | FROM 34 | `project.dataset.impression` AS impressions 35 | LEFT JOIN 36 | `project.dataset.click` AS clicks 37 | ON 38 | impressions.Impression_ID = clicks.Impression_ID 39 | JOIN 40 | ads 41 | ON 42 | impressions.Ad_ID = ads.Ad_ID 43 | WHERE 44 | impressions._DATA_DATE BETWEEN DATE(2018, 5, 10) 45 | AND DATE(2018, 6, 10) 46 | GROUP BY 47 | impressions.Ad_ID 48 | ORDER BY 49 | click_through_rate DESC -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/dv360/date_range.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2018 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 | -- This script obtains the partition and event date ranges for each of the view, 16 | -- click, and activity tables. It is meant to give you a range of dates that you 17 | -- can use in the other queries to process only limited data. 18 | SELECT 19 | "VIEW" AS Event_Type, 20 | FORMAT_DATE("%F", MIN(_DATA_DATE)) AS Start_Date_Partition, 21 | FORMAT_DATE("%F", MAX(_DATA_DATE)) AS End_Date_Partition, 22 | FORMAT_TIMESTAMP("%F %T", TIMESTAMP_MICROS(MIN(Event_Time))) AS Start_Date_Event, 23 | FORMAT_TIMESTAMP("%F %T", TIMESTAMP_MICROS(MAX(Event_Time))) AS End_Date_Event 24 | FROM 25 | `project.dataset.impression` 26 | UNION ALL 27 | SELECT 28 | "CLICK" AS Event_Type, 29 | FORMAT_DATE("%F", MIN(_DATA_DATE)) AS Start_Date_Partition, 30 | FORMAT_DATE("%F", MAX(_DATA_DATE)) AS End_Date_Partition, 31 | FORMAT_TIMESTAMP("%F %T", TIMESTAMP_MICROS(MIN(Event_Time))) AS Start_Date_Event, 32 | FORMAT_TIMESTAMP("%F %T", TIMESTAMP_MICROS(MAX(Event_Time))) AS End_Date_Event 33 | FROM 34 | `project.dataset.click` 35 | UNION ALL 36 | SELECT 37 | "CONVERSION" AS Event_Type, 38 | FORMAT_DATE("%F", MIN(_DATA_DATE)) AS Start_Date_Partition, 39 | FORMAT_DATE("%F", MAX(_DATA_DATE)) AS End_Date_Partition, 40 | FORMAT_TIMESTAMP("%F %T", TIMESTAMP_MICROS(MIN(Event_Time))) AS Start_Date_Event, 41 | FORMAT_TIMESTAMP("%F %T", TIMESTAMP_MICROS(MAX(Event_Time))) AS End_Date_Event 42 | FROM 43 | `project.dataset.activity` -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/dv360/dbm_data_dbm_device_type.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2018 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 | -- I want to learn... 16 | -- How do I determine whether an event happened on mobile vs desktop for DBM impressions? 17 | -- 18 | -- What can I do? 19 | -- Add mobile vs desktop breakouts in your analyses. 20 | -- 21 | -- Tips 22 | -- * Include this with other queries to get performance, reach, and more by DBM Device Type. 23 | 24 | SELECT 25 | DBM_Device_Type, 26 | CASE 27 | WHEN DBM_Device_Type=0 THEN "Computer" 28 | WHEN DBM_Device_Type=1 THEN "Other" 29 | WHEN DBM_Device_Type=2 THEN "Smartphone" 30 | WHEN DBM_Device_Type=3 THEN "Tablet" 31 | WHEN DBM_Device_Type=4 THEN "Smart TV" 32 | END DBM_Device_Type_Name, 33 | COUNT(*) AS Impressions 34 | FROM `project.dataset.impression` 35 | WHERE DBM_Advertiser_ID IS NOT NULL 36 | GROUP BY DBM_Device_Type, DBM_Device_Type_Name 37 | ORDER BY Impressions DESC 38 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/dv360/effective_cpa.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2018 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 | -- This script can be used to compute the effective CPA for any activity and any 16 | -- DBM line item. Currently in the DBM UI it is only possible to report on the 17 | -- eCPA for line items and activities that have been explictly linked for bid 18 | -- optimization. WIth this script you can report on all line items and activities. 19 | SELECT 20 | DBM_Line_Item_ID, 21 | ANY_VALUE(DBM_Insertion_Order_ID) AS DBM_Insertion_Order_ID, 22 | SUM(DBM_Billable_Cost_USD) / 1000000000 AS Total_Cost_USD, 23 | COUNTIF(activities.DBM_Auction_ID IS NOT NULL) AS activity_count, 24 | (SUM(DBM_Billable_Cost_USD) / COUNTIF(activities.DBM_Auction_ID IS NOT NULL)) / 1000000000 AS eCPA 25 | FROM 26 | `project.dataset.impression` AS impressions 27 | LEFT JOIN 28 | `project.dataset.activity` AS activities 29 | ON 30 | impressions.DBM_Auction_ID = activities.DBM_Auction_ID 31 | AND 32 | _DATA_DATE BETWEEN DATE(2018, 5, 10) AND DATE(2018, 6, 10) 33 | WHERE 34 | DBM_Billable_Cost_USD != 0 35 | GROUP BY 36 | DBM_Line_Item_ID -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/dv360/extract_variables_values_in_activity_file.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2018 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 | -- I want to learn... 16 | -- How do I extract additional values that I pass through u-variables in conversion data? 17 | -- 18 | -- What can I do? 19 | -- Analyze conversion data at a more granular level based on client-provided data. For example, analyze the most common conversion paths for a specific product. 20 | -- 21 | -- Tips 22 | -- Use REGEXP_EXTRACT to extract u-variable data from the “Other_Data” field. Create new columns for each extracted u-variable. 23 | 24 | SELECT 25 | TRIM(REGEXP_EXTRACT(Other_Data, r"u4=(.+?);")) AS Product_Purchased, 26 | COUNT(*) AS Conversions 27 | FROM `project.dataset.activity` 28 | WHERE Activity_ID IN ("12345","56789") 29 | GROUP BY 1 30 | ORDER BY 2 DESC 31 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/dv360/performance_aggregated_click_n_conversion_rates.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2018 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 | -- I want to learn... 16 | -- How do I join impression, click, and conversion data at an aggregated level (e.g. campaign level)? 17 | -- 18 | -- What can I do? 19 | -- Automate custom reporting tables that power business insights dashboards. 20 | -- 21 | -- Tips 22 | -- * Try replicating this method at other aggregation levels such as Advertiser, Site, Placement, Ad, Creative, etc. 23 | -- * Matching to the activity table using Advertiser ID, Campaign ID, Site, ID, etc. will assume your default attribution methodology and lookback windows like in DCM UI Reporting. 24 | -- * This method is the simplest method to join impression, click, and activity tables, but is less flexible. Other join methods include joining with Impression ID or User ID (see following slides). 25 | 26 | WITH impression_data AS ( 27 | SELECT 28 | Campaign_ID, 29 | COUNT(*) AS Impressions 30 | FROM `project.dataset.impression` 31 | GROUP BY Campaign_ID 32 | ), 33 | 34 | click_data AS ( 35 | SELECT 36 | Campaign_ID, 37 | COUNT(*) AS Clicks 38 | FROM `project.dataset.click` 39 | GROUP BY Campaign_ID 40 | ), 41 | 42 | conversion_data AS ( 43 | SELECT 44 | Campaign_ID, 45 | COUNT(*) AS Conversions 46 | FROM `project.dataset.activity` 47 | WHERE Activity_ID IN ("12345","67890") 48 | GROUP BY Campaign_ID 49 | ) 50 | 51 | SELECT 52 | *, 53 | Clicks/Impressions AS Click_Rate, 54 | Conversions/Impressions AS Conversion_Rate 55 | FROM impression_data 56 | LEFT JOIN click_data USING(Campaign_ID) 57 | LEFT JOIN conversion_data USING(Campaign_ID) 58 | ORDER BY Impressions DESC 59 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/3_days_using_union.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 using the Google analytics sample data set 18 | -- This script aggregates 3 days worth of data (visits, pageviews, transactions, revenue) into one table using a UNION ALL 19 | 20 | WITH ga_tables AS ( 21 | SELECT 22 | date, 23 | SUM(totals.visits) AS visits, 24 | SUM(totals.pageviews) AS pageviews, 25 | SUM(totals.transactions) AS transactions, 26 | SUM(totals.transactionRevenue)/1000000 AS revenue 27 | FROM `bigquery-public-data.google_analytics_sample.ga_sessions_20160801` 28 | GROUP BY date 29 | 30 | UNION ALL 31 | 32 | SELECT 33 | date, 34 | SUM(totals.visits) AS visits, 35 | SUM(totals.pageviews) AS pageviews, 36 | SUM(totals.transactions) AS transactions, 37 | SUM(totals.transactionRevenue)/1000000 AS revenue 38 | FROM `bigquery-public-data.google_analytics_sample.ga_sessions_20160802` 39 | GROUP BY date 40 | 41 | UNION ALL 42 | 43 | SELECT 44 | date, 45 | SUM(totals.visits) AS visits, 46 | SUM(totals.pageviews) AS pageviews, 47 | SUM(totals.transactions) AS transactions, 48 | SUM(totals.transactionRevenue)/1000000 AS revenue 49 | FROM `bigquery-public-data.google_analytics_sample.ga_sessions_20160803` 50 | GROUP BY date 51 | 52 | ) 53 | SELECT 54 | date, 55 | visits, 56 | pageviews, 57 | transactions, 58 | revenue, 59 | FROM ga_tables 60 | ORDER BY date ASC 61 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/avg_amount_of_money_per_session.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script calculates the average amount of money spent per session 19 | 20 | WITH Sessions AS ( 21 | SELECT 22 | fullVisitorId, 23 | SUM(totals.visits) AS total_visits_per_user, 24 | SUM(totals.transactionRevenue) AS total_transactionrevenue_per_user 25 | FROM 26 | `bigquery-public-data.google_analytics_sample.ga_sessions_*` 27 | WHERE 28 | _TABLE_SUFFIX BETWEEN '20170701' AND '20170731' 29 | AND totals.visits > 0 30 | AND totals.transactions >= 1 31 | AND totals.transactionRevenue IS NOT NULL 32 | GROUP BY fullVisitorId 33 | ) 34 | SELECT 35 | (SUM(total_transactionrevenue_per_user/1e6) / 36 | SUM(total_visits_per_user)) AS avg_revenue_by_user_per_visit 37 | FROM Sessions 38 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/avg_bounce_rate_per_traffic_source.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script calculates the average bounce rate per traffic source 19 | 20 | SELECT 21 | source, 22 | total_visits, 23 | total_no_of_bounces, 24 | ( ( total_no_of_bounces / total_visits ) * 100 ) AS bounce_rate 25 | FROM ( 26 | SELECT 27 | trafficSource.source AS source, 28 | COUNT ( trafficSource.source ) AS total_visits, 29 | SUM ( totals.bounces ) AS total_no_of_bounces 30 | FROM `bigquery-public-data.google_analytics_sample.ga_sessions_*` 31 | WHERE 32 | _TABLE_SUFFIX BETWEEN '20170701' AND '20170731' 33 | GROUP BY 34 | source ) 35 | ORDER BY 36 | total_visits DESC 37 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/avg_product_pageviews_by_non_purchasers.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script calculates the average number of product page views for users who did not make a purchase 19 | 20 | SELECT 21 | ( SUM(total_pagesviews_per_user) / COUNT(users) ) AS avg_pageviews_per_user 22 | FROM ( 23 | SELECT 24 | fullVisitorId AS users, 25 | SUM(totals.pageviews) AS total_pagesviews_per_user 26 | FROM`bigquery-public-data.google_analytics_sample.ga_sessions_*` 27 | WHERE 28 | _TABLE_SUFFIX BETWEEN '20170701' AND '20170731' 29 | AND 30 | totals.transactions IS NULL 31 | GROUP BY 32 | users ) 33 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/avg_product_pageviews_by_purchaser_type.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script calculates the average number of product page views by purchaser type (purchasers vs non-purchasers) 19 | 20 | 21 | SELECT 22 | ( SUM(total_pagesviews_per_user) / COUNT(users) ) AS avg_pageviews_per_user 23 | FROM 24 | SELECT 25 | fullVisitorId AS users, 26 | SUM(totals.pageviews) AS total_pagesviews_per_user 27 | FROM`bigquery-public-data.google_analytics_sample.ga_sessions_*` 28 | WHERE 29 | _TABLE_SUFFIX BETWEEN '20170701' AND '20170731' 30 | AND 31 | totals.transactions >=1 32 | GROUP BY 33 | users ) 34 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/avg_transactions_per_purchaser.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script calculates the average number of transactions per purchaser 19 | 20 | SELECT 21 | (SUM (total_transactions_per_user) / COUNT(fullVisitorId) ) AS avg_total_transactions_per_user 22 | FROM ( 23 | SELECT 24 | fullVisitorId, 25 | SUM (totals.transactions) AS total_transactions_per_user 26 | FROM 27 | `bigquery-public-data.google_analytics_sample.ga_sessions_*` 28 | WHERE 29 | _TABLE_SUFFIX BETWEEN '20170701' AND '20170731' 30 | AND totals.transactions IS NOT NULL 31 | GROUP BY 32 | fullVisitorId ) 33 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/last_1095_days_using_table_suffix.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script aggregates 1095 days worth of data (visits, pageviews, transactions, revenue) into one table using _TABLE_SUFFIX 19 | 20 | SELECT 21 | date, 22 | SUM(totals.visits) AS visits, 23 | SUM(totals.pageviews) AS pageviews, 24 | SUM(totals.transactions) AS transactions, 25 | SUM(totals.transactionRevenue)/1000000 AS revenue 26 | FROM `bigquery-public-data.google_analytics_sample.ga_sessions_*` 27 | WHERE 28 | _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d',DATE_SUB(CURRENT_DATE(), INTERVAL 1095 DAY)) 29 | AND 30 | FORMAT_DATE('%Y%m%d',DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)) 31 | GROUP BY date 32 | ORDER BY date ASC 33 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/last_36_months_using_table_suffix.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script aggregates the last 36 months worth of data (visits, pageviews, transactions, revenue) into one table using _TABLE_SUFFIX 19 | 20 | SELECT 21 | date, 22 | SUM(totals.visits) AS visits, 23 | SUM(totals.pageviews) AS pageviews, 24 | SUM(totals.transactions) AS transactions, 25 | SUM(totals.transactionRevenue)/1000000 AS revenue 26 | FROM 27 | (TABLE_DATE_RANGE([bigquery-public-data.google_analytics_sample.ga_sessions_], 28 | DATE_ADD(CURRENT_TIMESTAMP(), -36, 'MONTH'), DATE_ADD(CURRENT_TIMESTAMP(), -1, 'DAY'))) 29 | GROUP BY 30 | date 31 | ORDER BY 32 | date ASC 33 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/last_3_years_plus_today_using_union_all_and_table_suffix: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script aggregates the last 3 years worth of data including today (visits, pageviews, transactions, revenue) into one table using UNION ALL and _TABLE_SUFFIX 19 | 20 | WITH ga_tables AS ( SELECT 21 | date, 22 | SUM(totals.visits) AS visits, 23 | SUM(totals.pageviews) AS pageviews, 24 | SUM(totals.transactions) AS transactions, 25 | SUM(totals.transactionRevenue)/1000000 AS revenue 26 | FROM `bigquery-public-data.google_analytics_sample.ga_sessions_*` 27 | WHERE 28 | _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d',DATE_SUB(CURRENT_DATE(), INTERVAL 3 YEAR)) 29 | AND 30 | FORMAT_DATE('%Y%m%d',DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)) 31 | GROUP BY date 32 | UNION ALL 33 | 34 | SELECT 35 | date, 36 | SUM(totals.visits) AS visits, 37 | SUM(totals.pageviews) AS pageviews, 38 | SUM(totals.transactions) AS transactions, 39 | SUM(totals.transactionRevenue)/1000000 AS revenue 40 | FROM `bigquery-public-data.google_analytics_sample.ga_sessions_*` 41 | WHERE 42 | _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d',DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)) 43 | AND 44 | FORMAT_DATE('%Y%m%d',DATE_SUB(CURRENT_DATE(), INTERVAL 0 DAY)) 45 | GROUP BY date 46 | ) 47 | SELECT 48 | date, 49 | visits, 50 | pageviews, 51 | transactions, 52 | revenue, 53 | FROM ga_tables 54 | ORDER BY date ASC 55 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/last_3_years_using_table_suffix.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script aggregates the last 3 years worth of data (visits, pageviews, transactions, revenue) into one table using _TABLE_SUFFIX 19 | 20 | SELECT 21 | date, 22 | SUM(totals.visits) AS visits, 23 | SUM(totals.pageviews) AS pageviews, 24 | SUM(totals.transactions) AS transactions, 25 | SUM(totals.transactionRevenue)/1000000 AS revenue 26 | FROM `bigquery-public-data.google_analytics_sample.ga_sessions_*` 27 | WHERE 28 | _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d',DATE_SUB(CURRENT_DATE(), INTERVAL 3 YEAR)) 29 | AND 30 | FORMAT_DATE('%Y%m%d',DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)) 31 | GROUP BY date 32 | ORDER BY date ASC 33 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/sequence_of_hits.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script calculates the sequence of hits 19 | 20 | SELECT 21 | fullVisitorId, 22 | visitId, 23 | visitNumber, 24 | hits.hitNumber AS hitNumber, 25 | hits.page.pagePath AS pagePath 26 | FROM 27 | `bigquery-public-data.google_analytics_sample.ga_sessions_*`, 28 | UNNEST(hits) as hits 29 | WHERE 30 | _TABLE_SUFFIX BETWEEN '20170701' AND '20170731' 31 | AND 32 | hits.type="PAGE" 33 | ORDER BY 34 | fullVisitorId, 35 | visitId, 36 | visitNumber, 37 | hitNumber 38 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/specific_date_range_using_table_suffix.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script aggregates data for a specific date range (visits, pageviews, transactions, revenue) into one table using _TABLE_SUFFIX 19 | 20 | SELECT 21 | date, 22 | SUM(totals.visits) AS visits, 23 | SUM(totals.pageviews) AS pageviews, 24 | SUM(totals.transactions) AS transactions, 25 | SUM(totals.transactionRevenue)/1000000 AS revenue 26 | FROM `bigquery-public-data.google_analytics_sample.ga_sessions_*` 27 | WHERE 28 | _TABLE_SUFFIX BETWEEN '20160801' AND '20170731' 29 | GROUP BY date 30 | ORDER BY date ASC 31 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/bigquery-exports-queries/google/ga360/total_transactions_per_device.sql: -------------------------------------------------------------------------------- 1 | #standardSQL 2 | 3 | -- Copyright 2018 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 example is using the Google analytics sample data set 18 | -- This script calculates the total transactions per device for a given date range 19 | 20 | SELECT 21 | (SUM (total_transactions_per_user) / COUNT(fullVisitorId) ) AS avg_total_transactions_per_user 22 | FROM ( 23 | SELECT 24 | fullVisitorId, 25 | SUM (totals.transactions) AS total_transactions_per_user 26 | FROM 27 | `bigquery-public-data.google_analytics_sample.ga_sessions_*` 28 | WHERE 29 | _TABLE_SUFFIX BETWEEN '20170701' AND '20170731' 30 | AND totals.transactions IS NOT NULL 31 | GROUP BY 32 | fullVisitorId ) 33 | -------------------------------------------------------------------------------- /marketing-analytics/understanding/oculi/README.md: -------------------------------------------------------------------------------- 1 | **Note:** This is not an officially supported Google product. It is a reference 2 | implementation. 3 | 4 | # Oculi 5 | 6 | Oculi is a Google Cloud-based pipeline for tagging large sets of images or 7 | videos with labels based on their content, generating a BigQuery dataset for 8 | further analysis. Content tagging is done through Cloud's pre-trained computer 9 | vision models (Vision API and Video Intelligence API). 10 | 11 | The primary use case is for analyzing creatives (images and videos) in digital 12 | advertising. Combined with creative performance data, the output from this 13 | pipeline can be used to explore correlations between advertising content and 14 | performance (e.g. creatives with a human model tend to perform better). 15 | 16 | **Oculi is available in its own repository at [github.com/google/oculi](https://github.com/google/oculi).** 17 | -------------------------------------------------------------------------------- /marketing-technology/README.md: -------------------------------------------------------------------------------- 1 | Folder for Marketing Technology sample codes. -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/00_prework/README.md: -------------------------------------------------------------------------------- 1 | # Prework (optional) 2 | 3 | ## Files 4 | ### load_no_create.sh: 5 | This file 6 | 1. Downloads the file from gs://[YOUR_PREFIX]-gcs-pixel-tracking 7 | 2. Uses that file to attack through Vegeta 8 | 9 | Note: Targets.txt is about 12MB but the download should not take more than 1 sec from GCS to Pods. This is faster than creating a file every time we create a pod 10 | 11 | It will be called using ENTRYPOINT in the Docker file and its parameters will be passed using args from the Deployments. Passed parameters are: 12 | - V_DURATION: This is the time that the attack will last for 13 | - V_RATE: This is the amount of requests sent per second 14 | - TARGETS_FILE: This is the location of the file container the urls to attack similar to "GET http://LB_IP_OR_DOMAIN/pixel.png?params" 15 | 16 | ### Dockerfile 17 | The Dockerfile will help us create the container that we need. It will use golang base image and we will: 18 | 1. Install Vegeta 19 | 2. Launch the load script through ENTRYPOINT so we will be able to send it some parameters through the replication controller 20 | 21 | To make it available through your gcr.io: 22 | ``` 23 | docker build --no-cache -t USERNAME/vegeta docker 24 | docker tag matthieum/vegeta gcr.io/CLOUD_REPOSITORY/vegeta 25 | gcloud docker -- push gcr.io/CLOUD_REPOSITORY/vegeta 26 | ``` 27 | 28 | The one that we make available for this tutorial is available at gcr.io/cloud-solutions-images/vegeta 29 | 30 | If you want to test it Locally 31 | ``` 32 | docker build --no-cache -t vegeta docker 33 | docker run vegeta 1s 1000 gcs-pixel-tracking/targets.txt 34 | ``` 35 | -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/00_prework/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 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 | # http://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 | FROM golang 15 | 16 | MAINTAINER Matthieu Mayran "mayran@google.com" 17 | 18 | RUN go get github.com/tsenart/vegeta 19 | 20 | ADD load_no_create.sh /usr/local/bin 21 | 22 | ENTRYPOINT ["sh", "-C", "/usr/local/bin/load_no_create.sh"] 23 | -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/00_prework/docker/load_no_create.sh: -------------------------------------------------------------------------------- 1 | #!bin/bash 2 | # Copyright 2017 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | V_DURATION="$1" #5s 16 | V_RATE=$2 #1000 17 | TARGETS_FILE=$3 #gcs-pixel-tracking 18 | 19 | echo $V_DURATION 20 | echo $V_RATE 21 | echo $TARGETS_FILE 22 | 23 | curl https://storage.googleapis.com/$TARGETS_FILE > /tmp/targets.txt 24 | 25 | vegeta attack -targets=/tmp/targets.txt -duration=$V_DURATION -rate=$V_RATE | tee /tmp/report_targets.bin | vegeta report 26 | cat /tmp/report_targets.bin | vegeta report -reporter=plot >| /tmp/plot_targets.html 27 | -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/01_prep/create_targets.sh: -------------------------------------------------------------------------------- 1 | #!bin/bash 2 | # Copyright 2017 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # bash create_targets.sh LB_IP NB_URLS OUTPUT_FILE 17 | 18 | PIXEL_DOMAIN_OR_IP="$1" 19 | NB_URLS=$2 20 | OUTPUT="$3" 21 | 22 | DATA_PATH=data 23 | rm $3 24 | u=1 25 | while [[ $u -le $NB_URLS ]] 26 | do 27 | # Create the simple params 28 | PAGE_NAME=$(gshuf -n 1 "$DATA_PATH/page_names.txt") 29 | EVENT=$(gshuf -n 1 "$DATA_PATH/events.txt") 30 | 31 | # Create the url param http%3A%2F%2Fexample.com%2Fcart 32 | RANDOM_DOMAIN=$(gshuf -n 1 "$DATA_PATH/domains.txt") 33 | PAGE_URL="http%3A%2F%2F$RANDOM_DOMAIN%2F$PAGE_NAME" 34 | 35 | # Create the product string which has at least one product for the product 36 | # page and an extra possibl3 if in page related to cart 37 | #declare -a PAGES_NEED_PRODUCT=(cart shopping_cart checkout products) 38 | PAGES_NEED_PRODUCT[0]="cart" 39 | PAGES_NEED_PRODUCT[1]="shopping_cart" 40 | PAGES_NEED_PRODUCT[2]="checkout" 41 | PAGES_NEED_PRODUCT[3]="products" 42 | 43 | if [[ " ${PAGES_NEED_PRODUCT[*]} " == *"$PAGE_NAME"* ]]; then 44 | RANDOM_PRODUCT=$(gshuf -n 1 "$DATA_PATH/products.txt") 45 | PRODUCT="&pr=$RANDOM_PRODUCT;" 46 | if [ "$PAGE_NAME" != "products" ]; then 47 | # Add random amount of product (between 1 and 3) 48 | ADD_X_PRODUCTS=$(( ( RANDOM % 3 ) + 1 )) 49 | RANDOM_X_PRODUCTS=$(gshuf -n $ADD_X_PRODUCTS "$DATA_PATH/products.txt") 50 | for i in ${RANDOM_X_PRODUCTS[@]}; 51 | do 52 | PRODUCT+="${i};" 53 | done 54 | fi 55 | # Remove the last ";" 56 | PRODUCT=${PRODUCT%?} 57 | fi 58 | 59 | # Create a random user id between 10000 and 100000 60 | USER_ID=$(( ( RANDOM%90000 ) + 10000 )) 61 | echo "GET http://$PIXEL_DOMAIN_OR_IP/pixel.png?uid=$USER_ID&pn=$PAGE_NAME&purl=$PAGE_URL&e=${EVENT}${PRODUCT}" >> $3 62 | ((u = u + 1)) 63 | done 64 | -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/01_prep/data/domains.txt: -------------------------------------------------------------------------------- 1 | hinneng.com 2 | berpoe.net 3 | watifart.org 4 | prishis.fr 5 | ansignt.com 6 | bidept.com 7 | stinalrat.net 8 | butince.biz 9 | volings.com 10 | wardern.org 11 | grodark.com 12 | sibibus.biz 13 | chumbing.net 14 | recapok.fr 15 | wollearl.ca 16 | coatchis.ie 17 | parectier.ie 18 | muludifte.fr 19 | linewite.com 20 | nateark.net 21 | jecult.com 22 | figive.ie 23 | distioun.ca 24 | niethas.fr 25 | ocitand.net -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/01_prep/data/events.txt: -------------------------------------------------------------------------------- 1 | pv 2 | pl -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/01_prep/data/page_names.txt: -------------------------------------------------------------------------------- 1 | cart 2 | welcome 3 | home 4 | shopping_cart 5 | checkout 6 | product -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/01_prep/data/products.txt: -------------------------------------------------------------------------------- 1 | ozerwarm 2 | zaamcom 3 | stringplus 4 | roundin 5 | strongdanstock 6 | zertraxfan 7 | sandom 8 | movehotlux 9 | stimflex 10 | solzamin 11 | zontone 12 | ranfix 13 | meding 14 | ontocore 15 | kan-tom 16 | funlex 17 | holdbam 18 | quotop 19 | blackron 20 | hotfresh 21 | vivatonit 22 | conron 23 | geokix 24 | silvertam 25 | tanfresh 26 | groovesoft 27 | zerlotzap 28 | ecodonhold 29 | toplux 30 | lightflex -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/01_prep/targets_sample.txt: -------------------------------------------------------------------------------- 1 | GET http://IP_OF_LB/pixel.png?uid=19679&pn=checkout&purl=http%3A%2F%2Fnateark.net%2Fcheckout&e=pl&pr=stringplus;hotfresh 2 | GET http://IP_OF_LB/pixel.png?uid=29281&pn=welcome&purl=http%3A%2F%2Fnateark.net%2Fwelcome&e=pl&pr=stringplus;hotfresh 3 | GET http://IP_OF_LB/pixel.png?uid=12772&pn=home&purl=http%3A%2F%2Fmuludifte.fr%2Fhome&e=pv&pr=stringplus;hotfresh 4 | GET http://IP_OF_LB/pixel.png?uid=29313&pn=home&purl=http%3A%2F%2Fjecult.com%2Fhome&e=pl&pr=stringplus;hotfresh 5 | GET http://IP_OF_LB/pixel.png?uid=38594&pn=checkout&purl=http%3A%2F%2Fwollearl.ca%2Fcheckout&e=pl&pr=vivatonit;zontone;tanfresh 6 | GET http://IP_OF_LB/pixel.png?uid=12292&pn=cart&purl=http%3A%2F%2Fjecult.com%2Fcart&e=pv&pr=tanfresh;zontone;sandom 7 | GET http://IP_OF_LB/pixel.png?uid=32965&pn=welcome&purl=http%3A%2F%2Fbidept.com%2Fwelcome&e=pl&pr=tanfresh;zontone;sandom 8 | GET http://IP_OF_LB/pixel.png?uid=28108&pn=checkout&purl=http%3A%2F%2Fberpoe.net%2Fcheckout&e=pv&pr=movehotlux;ozerwarm 9 | GET http://IP_OF_LB/pixel.png?uid=37628&pn=cart&purl=http%3A%2F%2Fprishis.fr%2Fcart&e=pl&pr=groovesoft;ozerwarm;zaamcom;conron 10 | GET http://IP_OF_LB/pixel.png?uid=34312&pn=checkout&purl=http%3A%2F%2Focitand.net%2Fcheckout&e=pl&pr=stringplus;lightflex;quotop 11 | -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/02_load/load_scaleup.sh: -------------------------------------------------------------------------------- 1 | #!bin/bash 2 | # Copyright 2017 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # This is based on the ReplicationController args (currently 30000 urls in 30s at 1000/s) 17 | # We want to get to 100000/s so we will need to go up to 100 pods with doubling every 30s 18 | NB_PODS=100 19 | COUNTER=1 20 | SLEEP=15 #It takes a while to create the target.txt file 21 | while [ $COUNTER -le $NB_PODS ]; 22 | do 23 | kubectl scale deployment vegeta --replicas=$COUNTER 24 | echo $COUNTER 25 | if [ $COUNTER -eq $NB_PODS ] 26 | then 27 | echo 'break' 28 | break 29 | fi 30 | sleep $(($COUNTER*$SLEEP)) 31 | COUNTER=$((2*$COUNTER)) 32 | 33 | if [ $COUNTER -gt $NB_PODS ] 34 | then 35 | COUNTER=$NB_PODS 36 | fi 37 | 38 | done 39 | -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/02_load/vegeta-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 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 | # http://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 | kind: Deployment 16 | apiVersion: extensions/v1beta1 17 | metadata: 18 | name: vegeta 19 | spec: 20 | template: 21 | metadata: 22 | labels: 23 | run: vegeta 24 | spec: 25 | containers: 26 | - name: vegeta 27 | image: gcr.io/cloud-solutions-images/vegeta:latest 28 | resources: 29 | limits: 30 | cpu: 400m 31 | args: 32 | - "0" # The load will last forever 33 | - "1000" # We attack 1000 urls per second per pod 34 | - "mam-ext/cdn_load.txt" 35 | #- "[YOUR_PREFIX]-gcs-pixel-tracking/targets.txt" # Targets 36 | -------------------------------------------------------------------------------- /marketing-technology/gcs-pixel-tracking/CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement] 6 | (https://cla.developers.google.com/about/google-individual) 7 | (CLA), which you can do online. The CLA is necessary mainly because you own the 8 | copyright to your changes, even after your contribution becomes part of our 9 | codebase, so we need your permission to use and distribute your code. We also 10 | need to be sure of various other things—for instance that you'll tell us if you 11 | know that your code infringes on other people's patents. You don't have to sign 12 | the CLA until after you've submitted your code for review and a member has 13 | approved it, but you must do it before we can put your code into our codebase. 14 | Before you start working on a larger contribution, you should get in touch with 15 | us first through the issue tracker with your idea so that we can help out and 16 | possibly guide you. Coordinating up front makes it much easier to avoid 17 | frustration later on. 18 | 19 | ### Code reviews 20 | All submissions, including submissions by project members, require review. We 21 | use Github pull requests for this purpose. 22 | 23 | ### The small print 24 | Contributions made by corporations are covered by a different agreement than 25 | the one above, the 26 | [Software Grant and Corporate Contributor License Agreement] 27 | (https://cla.developers.google.com/about/google-corporate). 28 | --------------------------------------------------------------------------------