Ver Fonte

Country stats

wfansh há 3 meses atrás
pai
commit
cf81bbd812

+ 299 - 0
src/main/java/com/wechi/adweb/bridge/google/ads/common/CountryCode.java

@@ -0,0 +1,299 @@
+package com.wechi.adweb.bridge.google.ads.common;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * See https://developers.google.com/google-ads/api/reference/rpc/v18/GeoTargetConstant.
+ *
+ * @author wfansh
+ */
+@Getter
+@AllArgsConstructor
+public enum CountryCode {
+    // geo_target_constant.target_type = 'Country'
+    AF(2004, "AF", "Afghanistan"),
+    AL(2008, "AL", "Albania"),
+    AQ(2010, "AQ", "Antarctica"),
+    DZ(2012, "DZ", "Algeria"),
+    AS(2016, "AS", "American Samoa"),
+    AD(2020, "AD", "Andorra"),
+    AO(2024, "AO", "Angola"),
+    AG(2028, "AG", "Antigua and Barbuda"),
+    AZ(2031, "AZ", "Azerbaijan"),
+    AR(2032, "AR", "Argentina"),
+    AU(2036, "AU", "Australia"),
+    AT(2040, "AT", "Austria"),
+    BS(2044, "BS", "The Bahamas"),
+    BH(2048, "BH", "Bahrain"),
+    BD(2050, "BD", "Bangladesh"),
+    AM(2051, "AM", "Armenia"),
+    BB(2052, "BB", "Barbados"),
+    BE(2056, "BE", "Belgium"),
+    BT(2064, "BT", "Bhutan"),
+    BO(2068, "BO", "Bolivia"),
+    BA(2070, "BA", "Bosnia and Herzegovina"),
+    BW(2072, "BW", "Botswana"),
+    BR(2076, "BR", "Brazil"),
+    BZ(2084, "BZ", "Belize"),
+    SB(2090, "SB", "Solomon Islands"),
+    BN(2096, "BN", "Brunei"),
+    BG(2100, "BG", "Bulgaria"),
+    MM(2104, "MM", "Myanmar (Burma)"),
+    BI(2108, "BI", "Burundi"),
+    BY(2112, "BY", "Belarus"),
+    KH(2116, "KH", "Cambodia"),
+    CM(2120, "CM", "Cameroon"),
+    CA(2124, "CA", "Canada"),
+    CV(2132, "CV", "Cabo Verde"),
+    CF(2140, "CF", "Central African Republic"),
+    LK(2144, "LK", "Sri Lanka"),
+    TD(2148, "TD", "Chad"),
+    CL(2152, "CL", "Chile"),
+    CN(2156, "CN", "China"),
+    CX(2162, "CX", "Christmas Island"),
+    CC(2166, "CC", "Cocos (Keeling) Islands"),
+    CO(2170, "CO", "Colombia"),
+    KM(2174, "KM", "Comoros"),
+    CG(2178, "CG", "Republic of the Congo"),
+    CD(2180, "CD", "Democratic Republic of the Congo"),
+    CK(2184, "CK", "Cook Islands"),
+    CR(2188, "CR", "Costa Rica"),
+    HR(2191, "HR", "Croatia"),
+    CY(2196, "CY", "Cyprus"),
+    CZ(2203, "CZ", "Czechia"),
+    BJ(2204, "BJ", "Benin"),
+    DK(2208, "DK", "Denmark"),
+    DM(2212, "DM", "Dominica"),
+    DO(2214, "DO", "Dominican Republic"),
+    EC(2218, "EC", "Ecuador"),
+    SV(2222, "SV", "El Salvador"),
+    GQ(2226, "GQ", "Equatorial Guinea"),
+    ET(2231, "ET", "Ethiopia"),
+    ER(2232, "ER", "Eritrea"),
+    EE(2233, "EE", "Estonia"),
+    GS(2239, "GS", "South Georgia and the South Sandwich Islands"),
+    FJ(2242, "FJ", "Fiji"),
+    FI(2246, "FI", "Finland"),
+    FR(2250, "FR", "France"),
+    PF(2258, "PF", "French Polynesia"),
+    TF(2260, "TF", "French Southern and Antarctic Lands"),
+    DJ(2262, "DJ", "Djibouti"),
+    GA(2266, "GA", "Gabon"),
+    GE(2268, "GE", "Georgia"),
+    GM(2270, "GM", "The Gambia"),
+    DE(2276, "DE", "Germany"),
+    GH(2288, "GH", "Ghana"),
+    KI(2296, "KI", "Kiribati"),
+    GR(2300, "GR", "Greece"),
+    GD(2308, "GD", "Grenada"),
+    GU(2316, "GU", "Guam"),
+    GT(2320, "GT", "Guatemala"),
+    GN(2324, "GN", "Guinea"),
+    GY(2328, "GY", "Guyana"),
+    HT(2332, "HT", "Haiti"),
+    HM(2334, "HM", "Heard Island and McDonald Islands"),
+    VA(2336, "VA", "Vatican City"),
+    HN(2340, "HN", "Honduras"),
+    HU(2348, "HU", "Hungary"),
+    IS(2352, "IS", "Iceland"),
+    IN(2356, "IN", "India"),
+    ID(2360, "ID", "Indonesia"),
+    IQ(2368, "IQ", "Iraq"),
+    IE(2372, "IE", "Ireland"),
+    IL(2376, "IL", "Israel"),
+    IT(2380, "IT", "Italy"),
+    CI(2384, "CI", "Cote d'Ivoire"),
+    JM(2388, "JM", "Jamaica"),
+    JP(2392, "JP", "Japan"),
+    KZ(2398, "KZ", "Kazakhstan"),
+    JO(2400, "JO", "Jordan"),
+    KE(2404, "KE", "Kenya"),
+    KR(2410, "KR", "South Korea"),
+    KW(2414, "KW", "Kuwait"),
+    KG(2417, "KG", "Kyrgyzstan"),
+    LA(2418, "LA", "Laos"),
+    LB(2422, "LB", "Lebanon"),
+    LS(2426, "LS", "Lesotho"),
+    LV(2428, "LV", "Latvia"),
+    LR(2430, "LR", "Liberia"),
+    LY(2434, "LY", "Libya"),
+    LI(2438, "LI", "Liechtenstein"),
+    LT(2440, "LT", "Lithuania"),
+    LU(2442, "LU", "Luxembourg"),
+    MG(2450, "MG", "Madagascar"),
+    MW(2454, "MW", "Malawi"),
+    MY(2458, "MY", "Malaysia"),
+    MV(2462, "MV", "Maldives"),
+    ML(2466, "ML", "Mali"),
+    MT(2470, "MT", "Malta"),
+    MR(2478, "MR", "Mauritania"),
+    MU(2480, "MU", "Mauritius"),
+    MX(2484, "MX", "Mexico"),
+    MC(2492, "MC", "Monaco"),
+    MN(2496, "MN", "Mongolia"),
+    MD(2498, "MD", "Moldova"),
+    ME(2499, "ME", "Montenegro"),
+    MA(2504, "MA", "Morocco"),
+    MZ(2508, "MZ", "Mozambique"),
+    OM(2512, "OM", "Oman"),
+    NA(2516, "NA", "Namibia"),
+    NR(2520, "NR", "Nauru"),
+    NP(2524, "NP", "Nepal"),
+    NL(2528, "NL", "Netherlands"),
+    CW(2531, "CW", "Curacao"),
+    SX(2534, "SX", "Sint Maarten"),
+    BQ(2535, "BQ", "Caribbean Netherlands"),
+    NC(2540, "NC", "New Caledonia"),
+    VU(2548, "VU", "Vanuatu"),
+    NZ(2554, "NZ", "New Zealand"),
+    NI(2558, "NI", "Nicaragua"),
+    NE(2562, "NE", "Niger"),
+    NG(2566, "NG", "Nigeria"),
+    NU(2570, "NU", "Niue"),
+    NF(2574, "NF", "Norfolk Island"),
+    NO(2578, "NO", "Norway"),
+    MP(2580, "MP", "Northern Mariana Islands"),
+    UM(2581, "UM", "United States Minor Outlying Islands"),
+    FM(2583, "FM", "Micronesia"),
+    MH(2584, "MH", "Marshall Islands"),
+    PW(2585, "PW", "Palau"),
+    PK(2586, "PK", "Pakistan"),
+    PA(2591, "PA", "Panama"),
+    PG(2598, "PG", "Papua New Guinea"),
+    PY(2600, "PY", "Paraguay"),
+    PE(2604, "PE", "Peru"),
+    PH(2608, "PH", "Philippines"),
+    PN(2612, "PN", "Pitcairn Islands"),
+    PL(2616, "PL", "Poland"),
+    PT(2620, "PT", "Portugal"),
+    GW(2624, "GW", "Guinea-Bissau"),
+    TL(2626, "TL", "Timor-Leste"),
+    QA(2634, "QA", "Qatar"),
+    RO(2642, "RO", "Romania"),
+    RU(2643, "RU", "Russia"),
+    RW(2646, "RW", "Rwanda"),
+    BL(2652, "BL", "Saint Barthelemy"),
+    SH(2654, "SH", "Saint Helena, Ascension and Tristan da Cunha"),
+    KN(2659, "KN", "Saint Kitts and Nevis"),
+    LC(2662, "LC", "Saint Lucia"),
+    MF(2663, "MF", "Saint Martin"),
+    PM(2666, "PM", "Saint Pierre and Miquelon"),
+    VC(2670, "VC", "Saint Vincent and the Grenadines"),
+    SM(2674, "SM", "San Marino"),
+    ST(2678, "ST", "Sao Tome and Principe"),
+    SA(2682, "SA", "Saudi Arabia"),
+    SN(2686, "SN", "Senegal"),
+    RS(2688, "RS", "Serbia"),
+    SC(2690, "SC", "Seychelles"),
+    SL(2694, "SL", "Sierra Leone"),
+    SG(2702, "SG", "Singapore"),
+    SK(2703, "SK", "Slovakia"),
+    VN(2704, "VN", "Vietnam"),
+    SI(2705, "SI", "Slovenia"),
+    SO(2706, "SO", "Somalia"),
+    ZA(2710, "ZA", "South Africa"),
+    ZW(2716, "ZW", "Zimbabwe"),
+    ES(2724, "ES", "Spain"),
+    SS(2728, "SS", "South Sudan"),
+    SD(2736, "SD", "Sudan"),
+    SR(2740, "SR", "Suriname"),
+    SZ(2748, "SZ", "Eswatini"),
+    SE(2752, "SE", "Sweden"),
+    CH(2756, "CH", "Switzerland"),
+    TJ(2762, "TJ", "Tajikistan"),
+    TH(2764, "TH", "Thailand"),
+    TG(2768, "TG", "Togo"),
+    TK(2772, "TK", "Tokelau"),
+    TO(2776, "TO", "Tonga"),
+    TT(2780, "TT", "Trinidad and Tobago"),
+    AE(2784, "AE", "United Arab Emirates"),
+    TN(2788, "TN", "Tunisia"),
+    TR(2792, "TR", "Turkiye"),
+    TM(2795, "TM", "Turkmenistan"),
+    TV(2798, "TV", "Tuvalu"),
+    UG(2800, "UG", "Uganda"),
+    UA(2804, "UA", "Ukraine"),
+    MK(2807, "MK", "North Macedonia"),
+    EG(2818, "EG", "Egypt"),
+    GB(2826, "GB", "United Kingdom"),
+    GG(2831, "GG", "Guernsey"),
+    JE(2832, "JE", "Jersey"),
+    IM(2833, "IM", "Isle of Man"),
+    TZ(2834, "TZ", "Tanzania"),
+    US(2840, "US", "United States"),
+    BF(2854, "BF", "Burkina Faso"),
+    UY(2858, "UY", "Uruguay"),
+    UZ(2860, "UZ", "Uzbekistan"),
+    VE(2862, "VE", "Venezuela"),
+    WF(2876, "WF", "Wallis and Futuna"),
+    WS(2882, "WS", "Samoa"),
+    YE(2887, "YE", "Yemen"),
+    ZM(2894, "ZM", "Zambia"),
+
+    // geo_target_constant.target_type = 'Region'
+    BM(2060, "BM", "Bermuda"),
+    BV(2074, "BV", "Bouvet Island"),
+    IO(2086, "IO", "British Indian Ocean Territory"),
+    VG(2092, "VG", "British Virgin Islands"),
+    KY(2136, "KY", "Cayman Islands"),
+    TW(2158, "TW", "Taiwan"),
+    YT(2175, "YT", "Mayotte"),
+    FO(2234, "FO", "Faroe Islands"),
+    FK(2238, "FK", "Falkland Islands (Islas Malvinas)"),
+    GF(2254, "GF", "French Guiana"),
+    PS(2275, "PS", "Palestine"),
+    GI(2292, "GI", "Gibraltar"),
+    GL(2304, "GL", "Greenland"),
+    GP(2312, "GP", "Guadeloupe"),
+    HK(2344, "HK", "Hong Kong"),
+    MO(2446, "MO", "Macao"),
+    MQ(2474, "MQ", "Martinique"),
+    MS(2500, "MS", "Montserrat"),
+    AW(2533, "AW", "Aruba"),
+    PR(2630, "PR", "Puerto Rico"),
+    RE(2638, "RE", "Reunion"),
+    AI(2660, "AI", "Anguilla"),
+    EH(2732, "EH", "Western Sahara"),
+    SJ(2744, "SJ", "Svalbard and Jan Mayen"),
+    TC(2796, "TC", "Turks and Caicos Islands"),
+    VI(2850, "VI", "U.S. Virgin Islands"),
+    XK(2900, "XK", "Kosovo"),
+
+    // Unknown
+    UNK(-1, "UNK", "UNKNOWN");
+
+    private static final Map<Long, CountryCode> ID_MAP =
+            Stream.of(CountryCode.values())
+                    .collect(Collectors.toMap(CountryCode::getId, Function.identity()));
+
+    private static final Map<String, CountryCode> CODE_MAP =
+            Stream.of(CountryCode.values())
+                    .collect(Collectors.toMap(CountryCode::getCode, Function.identity()));
+
+    public static CountryCode valueOfId(long id) {
+        return ID_MAP.getOrDefault(id, UNK);
+    }
+
+    @JsonCreator
+    public static CountryCode valueOfCode(String code) {
+        return CODE_MAP.getOrDefault(code, UNK);
+    }
+
+    @JsonValue
+    public String getCode() {
+        return code;
+    }
+
+    private final long id;
+    private final String code;
+    private final String name;
+}

+ 1 - 1
src/main/java/com/wechi/adweb/bridge/google/ads/util/PlacementUtils.java → src/main/java/com/wechi/adweb/bridge/google/ads/common/PlacementUtils.java

@@ -1,4 +1,4 @@
-package com.wechi.adweb.bridge.google.ads.util;
+package com.wechi.adweb.bridge.google.ads.common;
 
 import com.google.ads.googleads.v18.resources.GroupPlacementView;
 

+ 23 - 0
src/main/java/com/wechi/adweb/bridge/google/ads/controller/GoogleAdsController.java

@@ -126,6 +126,29 @@ public class GoogleAdsController extends BaseController {
                 .build();
     }
 
+    @PostMapping("/countryStats")
+    @ResponseBody
+    public OpenAPIResponse<Map<String, MetricsDTO>> getCountryStats(
+            @RequestBody OpenAPIRequest<ReportRequestDTO> apiRequest)
+            throws BadRequestException, DataException {
+        long start = System.currentTimeMillis();
+        log.info("****** getCountryStats() ****** apiRequest = {}", JsonUtils.toJson(apiRequest));
+        ReportRequestDTO reportRequest = apiRequest.getData();
+
+        // 1. Validates the request parameters.
+        if (!validateReportRequest(reportRequest)) {
+            throw new BadRequestException(apiRequest);
+        }
+
+        // 2. Executes the API request.
+        Map<String, MetricsDTO> countryStats = googleAdsService.getCountryStats(reportRequest);
+        log.info("****** getCountryStats() ****** duration = {} seconds", getElapsedSeconds(start));
+        return OpenAPIResponse.<Map<String, MetricsDTO>>builder()
+                .status(APIStatus.SUCCESS)
+                .data(countryStats)
+                .build();
+    }
+
     private boolean validateReportRequest(ReportRequestDTO reportRequest) {
         return StringUtils.isNotEmpty(reportRequest.getCustomerId())
                 && StringUtils.isNotEmpty(reportRequest.getStartDate())

+ 41 - 1
src/main/java/com/wechi/adweb/bridge/google/ads/service/GoogleAdsService.java

@@ -7,10 +7,11 @@ import com.google.ads.googleads.v18.services.GoogleAdsServiceClient.SearchPagedR
 import com.google.ads.googleads.v18.services.SearchGoogleAdsRequest;
 import com.google.api.core.ApiFuture;
 import com.wechi.adweb.bridge.exception.DataException;
+import com.wechi.adweb.bridge.google.ads.common.CountryCode;
+import com.wechi.adweb.bridge.google.ads.common.PlacementUtils;
 import com.wechi.adweb.bridge.google.ads.dto.CustomerStatsDTO;
 import com.wechi.adweb.bridge.google.ads.dto.MetricsDTO;
 import com.wechi.adweb.bridge.google.ads.dto.ReportRequestDTO;
-import com.wechi.adweb.bridge.google.ads.util.PlacementUtils;
 
 import lombok.extern.slf4j.Slf4j;
 
@@ -207,4 +208,43 @@ public class GoogleAdsService {
                                     LinkedHashMap::new));
         }
     }
+
+    public Map<String, MetricsDTO> getCountryStats(ReportRequestDTO reportRequest)
+            throws DataException {
+        try (GoogleAdsServiceClient googleAdsServiceClient =
+                googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
+            String query =
+                    String.format(
+                            "SELECT geographic_view.country_criterion_id, geographic_view.location_type, "
+                                    + "metrics.impressions, metrics.clicks, metrics.ctr, metrics.average_cpc, "
+                                    + "metrics.average_cpm, metrics.conversions, metrics.cost_micros "
+                                    + "FROM geographic_view "
+                                    + "WHERE %s "
+                                    + "ORDER BY metrics.impressions DESC, metrics.clicks DESC LIMIT %d",
+                            reportRequest.toDateClause(), reportRequest.getLimit());
+
+            SearchGoogleAdsRequest request =
+                    SearchGoogleAdsRequest.newBuilder()
+                            .setCustomerId(reportRequest.getCustomerId())
+                            .setQuery(query)
+                            .build();
+
+            SearchPagedResponse response = googleAdsServiceClient.search(request);
+            return StreamSupport.stream(response.iterateAll().spliterator(), false)
+                    .collect(
+                            Collectors.toMap(
+                                    googleAdsRow ->
+                                            CountryCode.valueOfId(
+                                                            googleAdsRow
+                                                                    .getGeographicView()
+                                                                    .getCountryCriterionId())
+                                                    .getCode(),
+                                    googleAdsRow ->
+                                            MetricsDTO.fromMetrics(googleAdsRow.getMetrics()),
+                                    // For duplicated countries.
+                                    MetricsDTO::merge,
+                                    // Preserves the original order in the response.
+                                    LinkedHashMap::new));
+        }
+    }
 }