mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
Merge pull request #582 from Ameobea/google-analytics-realtime
Add support for pulling realtime Google Analytics data
This commit is contained in:
commit
2bd7c43006
@ -1,93 +1,132 @@
|
|||||||
package googleanalytics
|
package googleanalytics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"fmt"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/wtfutil/wtf/utils"
|
"github.com/wtfutil/wtf/utils"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/oauth2/google"
|
"golang.org/x/oauth2/google"
|
||||||
ga "google.golang.org/api/analyticsreporting/v4"
|
gaV3 "google.golang.org/api/analytics/v3"
|
||||||
|
gaV4 "google.golang.org/api/analyticsreporting/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type websiteReport struct {
|
type websiteReport struct {
|
||||||
Name string
|
Name string
|
||||||
Report *ga.GetReportsResponse
|
Report *gaV4.GetReportsResponse
|
||||||
|
RealtimeReport *gaV3.RealtimeData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) Fetch() ([]websiteReport) {
|
func (widget *Widget) Fetch() []websiteReport {
|
||||||
secretPath, err := utils.ExpandHomeDir(widget.settings.secretFile)
|
secretPath, err := utils.ExpandHomeDir(widget.settings.secretFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Unable to parse secretFile path")
|
log.Fatalf("Unable to parse secretFile path")
|
||||||
}
|
}
|
||||||
|
|
||||||
service, err := makeReportService(secretPath)
|
serviceV4, err := makeReportServiceV4(secretPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Unable to create Google Analytics Reporting Service")
|
log.Fatalf("Unable to create v3 Google Analytics Reporting Service")
|
||||||
}
|
}
|
||||||
|
|
||||||
visitorsDataArray := getReports(service, widget.settings.viewIds, widget.settings.months)
|
var serviceV3 *gaV3.Service
|
||||||
|
if widget.settings.enableRealtime {
|
||||||
|
serviceV3, err = makeReportServiceV3(secretPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to create v3 Google Analytics Reporting Service")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitorsDataArray := getReports(
|
||||||
|
serviceV4, widget.settings.viewIds, widget.settings.months, serviceV3,
|
||||||
|
)
|
||||||
return visitorsDataArray
|
return visitorsDataArray
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeReportService(secretPath string) (*ga.Service, error) {
|
func buildNetClient(secretPath string) *http.Client {
|
||||||
clientSecret, err := ioutil.ReadFile(secretPath)
|
clientSecret, err := ioutil.ReadFile(secretPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Unable to read secretPath. %v", err)
|
log.Fatalf("Unable to read secretPath. %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
jwtConfig, err := google.JWTConfigFromJSON(clientSecret, ga.AnalyticsReadonlyScope)
|
jwtConfig, err := google.JWTConfigFromJSON(clientSecret, gaV4.AnalyticsReadonlyScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Unable to get config from JSON. %v", err)
|
log.Fatalf("Unable to get config from JSON. %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var netClient *http.Client
|
return jwtConfig.Client(oauth2.NoContext)
|
||||||
netClient = jwtConfig.Client(oauth2.NoContext)
|
}
|
||||||
svc, err := ga.New(netClient)
|
|
||||||
|
func makeReportServiceV3(secretPath string) (*gaV3.Service, error) {
|
||||||
|
var netClient = buildNetClient(secretPath)
|
||||||
|
svc, err := gaV3.New(netClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to create Google Analytics Reporting Service")
|
log.Fatalf("Failed to create v3 Google Analytics Reporting Service")
|
||||||
}
|
}
|
||||||
|
|
||||||
return svc, err
|
return svc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getReports(service *ga.Service, viewIds map[string]interface{}, displayedMonths int) ([]websiteReport) {
|
func makeReportServiceV4(secretPath string) (*gaV4.Service, error) {
|
||||||
startDate := fmt.Sprintf("%s-01", time.Now().AddDate(0, -displayedMonths+1, 0).Format("2006-01"))
|
var netClient = buildNetClient(secretPath)
|
||||||
var websiteReports []websiteReport = nil
|
svc, err := gaV4.New(netClient)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create v4 Google Analytics Reporting Service")
|
||||||
|
}
|
||||||
|
|
||||||
for website, viewId := range viewIds {
|
return svc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getReports(
|
||||||
|
serviceV4 *gaV4.Service, viewIds map[string]interface{}, displayedMonths int, serviceV3 *gaV3.Service,
|
||||||
|
) []websiteReport {
|
||||||
|
startDate := fmt.Sprintf("%s-01", time.Now().AddDate(0, -displayedMonths+1, 0).Format("2006-01"))
|
||||||
|
var websiteReports []websiteReport
|
||||||
|
|
||||||
|
for website, viewID := range viewIds {
|
||||||
// For custom queries: https://ga-dev-tools.appspot.com/dimensions-metrics-explorer/
|
// For custom queries: https://ga-dev-tools.appspot.com/dimensions-metrics-explorer/
|
||||||
|
|
||||||
req := &ga.GetReportsRequest{
|
req := &gaV4.GetReportsRequest{
|
||||||
ReportRequests: []*ga.ReportRequest{
|
ReportRequests: []*gaV4.ReportRequest{
|
||||||
{
|
{
|
||||||
ViewId: viewId.(string),
|
ViewId: viewID.(string),
|
||||||
DateRanges: []*ga.DateRange{
|
DateRanges: []*gaV4.DateRange{
|
||||||
{StartDate: startDate, EndDate: "today"},
|
{StartDate: startDate, EndDate: "today"},
|
||||||
},
|
},
|
||||||
Metrics: []*ga.Metric{
|
Metrics: []*gaV4.Metric{
|
||||||
{Expression: "ga:sessions"},
|
{Expression: "ga:sessions"},
|
||||||
},
|
},
|
||||||
Dimensions: []*ga.Dimension{
|
Dimensions: []*gaV4.Dimension{
|
||||||
{Name: "ga:month"},
|
{Name: "ga:month"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
response, err := service.Reports.BatchGet(req).Do()
|
response, err := serviceV4.Reports.BatchGet(req).Do()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("GET request to analyticsreporting/v4 returned error with viewID: %s", viewId)
|
log.Fatalf("GET request to analyticsreporting/v4 returned error with viewID: %s", viewID)
|
||||||
}
|
}
|
||||||
if response.HTTPStatusCode != 200 {
|
if response.HTTPStatusCode != 200 {
|
||||||
log.Fatalf("Did not get expected HTTP response code")
|
log.Fatalf("Did not get expected HTTP response code")
|
||||||
}
|
}
|
||||||
|
|
||||||
report := websiteReport{Name: website, Report: response,}
|
report := websiteReport{Name: website, Report: response}
|
||||||
|
if serviceV3 != nil {
|
||||||
|
report.RealtimeReport = getLiveCount(serviceV3, viewID.(string))
|
||||||
|
}
|
||||||
websiteReports = append(websiteReports, report)
|
websiteReports = append(websiteReports, report)
|
||||||
}
|
}
|
||||||
return websiteReports
|
return websiteReports
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getLiveCount(service *gaV3.Service, viewID string) *gaV3.RealtimeData {
|
||||||
|
res, err := service.Data.Realtime.Get("ga:"+viewID, "rt:activeUsers").Do()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to fetch real time data for view ID %s: %v. Have you enrolled in the real time beta? If not, do so here: https://docs.google.com/forms/d/1qfRFysCikpgCMGqgF3yXdUyQW4xAlLyjKuOoOEFN2Uw/viewform", viewID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
@ -2,12 +2,40 @@ package googleanalytics
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (widget *Widget) createTable(websiteReports []websiteReport) (string) {
|
func (widget *Widget) createTable(websiteReports []websiteReport) string {
|
||||||
content := widget.createHeader()
|
content := ""
|
||||||
|
|
||||||
|
if len(websiteReports) == 0 {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
if websiteReports[0].RealtimeReport != nil {
|
||||||
|
content += "Realtime Visitor Counts\n"
|
||||||
|
for _, websiteReport := range websiteReports {
|
||||||
|
websiteRow := fmt.Sprintf(" %-20s", websiteReport.Name)
|
||||||
|
|
||||||
|
if websiteReport.RealtimeReport == nil {
|
||||||
|
websiteRow += fmt.Sprintf("No data found for given ViewId.")
|
||||||
|
} else {
|
||||||
|
if len(websiteReport.RealtimeReport.Rows) == 0 {
|
||||||
|
websiteRow += "-"
|
||||||
|
} else {
|
||||||
|
websiteRow += fmt.Sprintf("%-10s", websiteReport.RealtimeReport.Rows[0][0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content += websiteRow + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
content += "\n"
|
||||||
|
content += "Historical Visitor Counts\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
content += widget.createHeader()
|
||||||
|
|
||||||
for _, websiteReport := range websiteReports {
|
for _, websiteReport := range websiteReports {
|
||||||
websiteRow := ""
|
websiteRow := ""
|
||||||
@ -33,13 +61,15 @@ func (widget *Widget) createTable(websiteReports []websiteReport) (string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content += websiteRow + "\n"
|
content += websiteRow + "\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Widget) createHeader() (string) {
|
func (widget *Widget) createHeader() string {
|
||||||
// Creates the table header of consisting of Months
|
// Creates the table header of consisting of Months
|
||||||
currentMonth := int(time.Now().Month())
|
currentMonth := int(time.Now().Month())
|
||||||
widgetStartMonth := currentMonth - widget.settings.months + 1
|
widgetStartMonth := currentMonth - widget.settings.months + 1
|
||||||
|
@ -13,6 +13,7 @@ type Settings struct {
|
|||||||
months int
|
months int
|
||||||
secretFile string `help:"Your Google client secret JSON file." values:"A string representing a file path to the JSON secret file."`
|
secretFile string `help:"Your Google client secret JSON file." values:"A string representing a file path to the JSON secret file."`
|
||||||
viewIds map[string]interface{}
|
viewIds map[string]interface{}
|
||||||
|
enableRealtime bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
|
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
|
||||||
@ -23,6 +24,7 @@ func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *co
|
|||||||
months: ymlConfig.UInt("months"),
|
months: ymlConfig.UInt("months"),
|
||||||
secretFile: ymlConfig.UString("secretFile"),
|
secretFile: ymlConfig.UString("secretFile"),
|
||||||
viewIds: ymlConfig.UMap("viewIds"),
|
viewIds: ymlConfig.UMap("viewIds"),
|
||||||
|
enableRealtime: ymlConfig.UBool("enableRealtime", false),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &settings
|
return &settings
|
||||||
|
Loading…
x
Reference in New Issue
Block a user