Search code examples
c#.net-coregoogle-analytics-apigoogle-reporting-api

Google Reporting API V4 Missing Values


I've been having a problem with Google's analytic reporting api v4. When I make a request, i can get data back, but some dimension and metric values are missing and/or inconsistent.

For example if i wanted the fullRefferer, it would return (not set). Or when i do get values my page views value could be 1312 and my sessions could be 26.

My code for making the request is below:

public GetReportsResponse Get(string viewId, DateTime startDate, DateTime endDate, string nextPageToken = null)
    {
        try
        {
            var credential = GetCredential();
            using (var svc = new AnalyticsReportingService(
                new BaseClientService.Initializer
                {
                    HttpClientInitializer = credential
                }))
            {
                var mets = new List<Metric>
                {
                    new Metric
                    {
                        Alias = "Users",
                        Expression = "ga:users"
                    },
                    new Metric
                    {
                        Alias = "Bounce Rate",
                        Expression = "ga:bounceRate"
                    },
                    new Metric
                    {
                        Alias = "Page Views",
                        Expression = "ga:pageViews"
                    },
                    new Metric()
                    {
                        Alias = "Sessions",
                        Expression = "ga:sessions"
                    }
                };

                var dims = new List<Dimension>
                {
                    new Dimension { Name = "ga:date" },
                    new Dimension { Name = "ga:hour" },
                    new Dimension { Name = "ga:browser" },
                    new Dimension { Name = "ga:pagePath" },
                    new Dimension { Name = "ga:fullReferrer"}
                };

                var dateRange = new DateRange
                {
                    StartDate = startDate.ToFormattedString(),
                    EndDate = endDate.ToFormattedString()
                };

                var reportRequest = new ReportRequest
                {
                    DateRanges = new List<DateRange> { dateRange },
                    Dimensions = dims,
                    Metrics = mets,
                    ViewId = viewId,
                    PageToken = nextPageToken
                };
                var getReportsRequest = new GetReportsRequest
                {
                    ReportRequests = new List<ReportRequest> { reportRequest },
                };
                var batchRequest = svc.Reports.BatchGet(getReportsRequest);
                var response = batchRequest.Execute();

                return response;
            }
        }
        catch (Exception e)
        {
            return null;
        }
    }

And my code for filtering the results is here:

public static List<AnalyticEntry> Filter(Google.Apis.AnalyticsReporting.v4.Data.GetReportsResponse response)
    {
        if (response == null) return new List<AnalyticEntry>();

        List<GoogleDataDto> gData = new List<GoogleDataDto>();
        foreach (var report in response.Reports)
        {
            foreach (var row in report.Data.Rows)
            {
                GoogleDataDto dto = new GoogleDataDto();

                foreach (var metric in row.Metrics)
                {
                    foreach (var value in metric.Values)
                    {
                        int index = metric.Values.IndexOf(value);
                        var metricHeader = report.ColumnHeader.MetricHeader.MetricHeaderEntries[index];

                        switch (metricHeader.Name)
                        {
                            case "Sessions":
                                dto.Sessions = Convert.ToInt32(value);
                                break;
                            case "Bounce Rate":
                                dto.BounceRate = Convert.ToDecimal(value);
                                break;
                            case "Page Views":
                                dto.PageViews = Convert.ToInt32(value);
                                break;
                            case "Users":
                                dto.Users = Convert.ToInt32(value);
                                break;
                        }
                    }
                }

                foreach (var dimension in row.Dimensions)
                {
                    int index = row.Dimensions.IndexOf(dimension);
                    var dimensionName = report.ColumnHeader.Dimensions[index];
                    switch (dimensionName)
                    {
                        case "ga:date":
                            dto.Date = dimension;
                            break;
                        case "ga:hour":
                            dto.Hour = dimension;
                            break;
                        case "ga:browser":
                            dto.Browser = dimension;
                            break;
                        case "ga:pagePath":
                            dto.PagePath = dimension;
                            break;
                        case "ga:source":
                            dto.Source = dimension;
                            break;
                        case "ga:fullRefferer":
                            dto.Referrer = dimension;
                            break;
                    }
                }

                gData.Add(dto);
            }
        }

        return Combine(gData);
    }

    private static List<AnalyticEntry> Combine(IReadOnlyCollection<GoogleDataDto> gData)
    {
        List<AnalyticEntry> outputDtos = new List<AnalyticEntry>();

        var dates = gData.GroupBy(d => d.Date.Substring(0,6)).Select(d => d.First()).Select(d => d.Date.Substring(0,6)).ToList();

        foreach (var date in dates)
        {
            var entities = gData.Where(d => d.Date.Contains(date)).ToList();

            AnalyticEntry dto = new AnalyticEntry
            {
                Date = date.ToDate(),
                PageViews = 0,
                Sessions = 0,
                Users = 0,
                BounceRate = 0
            };


            foreach (var entity in entities)
            {
                dto.BounceRate += entity.BounceRate;
                dto.PageViews += entity.PageViews;
                dto.Users += entity.Users;
                dto.Sessions += entity.Sessions;
            }

            dto.BounceRate = dto.BounceRate / entities.Count();

            var dictionaries = entities.GetDictionaries();

            var commonBrowsers = dictionaries[0].GetMostCommon();
            var commonTimes = dictionaries[1].GetMostCommon();
            var commonPages = dictionaries[2].GetMostCommon();
            var commonSources = dictionaries[3].GetMostCommon();
            var commonReferrers = dictionaries[4].GetMostCommon();

            dto.CommonBrowser = commonBrowsers.Key;
            dto.CommonBrowserViews = commonBrowsers.Value;
            dto.CommonTimeOfDay = commonTimes.Key.ToInt();
            dto.CommonTimeOfDayViews = commonTimes.Value;
            dto.CommonPage = commonPages.Key;
            dto.CommonPageViews = commonPages.Value;
            dto.CommonSource = commonSources.Key;
            dto.CommonSourceViews = commonSources.Value;
            dto.CommonReferrer = commonReferrers.Key;
            dto.CommonReferrerViews = commonReferrers.Value;

            outputDtos.Add(dto);
        }

        return outputDtos;
    }

I'm not sure what else to put, please comment for more info :)


Solution

  • Solved!

    Originally I was trying to find a 'metric name' based on the location of a value in an array. So using the location I would get the name and set the value.

    The problem was the array could have multiple values which were the same.

    For example:

    var arr = [1,0,3,1,1];
    

    If a value was 1, I was trying to use the location of 1 in the array to get a name. So if the index of 1 in the array was 0, I would find its name by using that index and finding the name in another array.

    For example:

    var names = ['a','b','c'];
    var values = [1,2,1];
    
    var value = 1;
    var index = values.indexOf(value); // which would be 0  
    
    SetProperty(
        propertyName:names[index], // being a
        value: value);
    

    Although its hard to explain I was setting the same value multiple times due to the fact that there were more than one value equal to the same thing in the array.

    Here is the answer. Tested and works

    public List<GoogleDataDto> Filter(GetReportsResponse response)
        {
            if (response == null) return null;
    
            List<GoogleDataDto> gData = new List<GoogleDataDto>();
            foreach (var report in response.Reports)
            {
                foreach (var row in report.Data.Rows)
                {
                    GoogleDataDto dto = new GoogleDataDto();
    
                    foreach (var metric in row.Metrics)
                    {
                        int index = 0; // Index counter, used to get the metric name
                        foreach (var value in metric.Values)
                        {
                            var metricHeader = report.ColumnHeader.MetricHeader.MetricHeaderEntries[index];
    
                            //Sets property value based on the metric name
                            dto.SetMetricValue(metricHeader.Name, value);
                            index++;
                        }
                    }
    
                    int dIndex = 0; // Used to get dimension name
                    foreach (var dimension in row.Dimensions)
                    {
                        var dimensionName = report.ColumnHeader.Dimensions[dIndex];
    
                        //Sets property value based on dimension name
                        dto.SetDimensionValue(dimensionName, dimension);
    
                        dIndex++;
                    }
    
                    // Will only add the dto to the list if its not a duplicate
                    if (!gData.IsDuplicate(dto))
                        gData.Add(dto);
                }
            }
    
            return gData;
        }