/core/externals/update-engine/externals/gdata-objectivec-client/Examples/AnalyticsSample/AnalyticsSampleWindowController.m
Objective C | 515 lines | 328 code | 116 blank | 71 comment | 30 complexity | 2267f219013fb13df81d8cc1ccd9a71f MD5 | raw file
1/* Copyright (c) 2009 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// 17// AnalyticsSampleWindowController.m 18// 19 20// 21// IMPORTANT: 22// 23// The XML-based API for Google Analytics has been replaced with a more efficient 24// and easier-to-use JSON API. The JSON API is documented at 25// 26// https://developers.google.com/analytics 27// 28// See the new Objective-C client library and sample code at 29// http://code.google.com/p/google-api-objectivec-client/ 30// 31// This sample application and library support for the XML-based Analytics 32// API will eventually be removed. 33// 34 35#import "AnalyticsSampleWindowController.h" 36 37@interface AnalyticsSampleWindowController (PrivateMethods) 38- (void)updateUI; 39 40- (void)fetchFeedOfAccounts; 41- (void)fetchSelectedAccount; 42- (void)fetchSelectedAnalyticsData; 43 44- (GDataServiceGoogleAnalytics *)analyticsService; 45- (GDataEntryAnalyticsAccount *)selectedAccount; 46- (GDataEntryAnalyticsData *)selectedAnalyticsData; 47 48- (NSString *)itemListForPopup:(NSPopUpButton *)popup; 49- (NSString *)analyticsDateStringForDatePicker:(NSDatePicker *)picker; 50 51- (GDataFeedAnalyticsAccount *)accountFeed; 52- (void)setAccountFeed:(GDataFeedAnalyticsAccount *)feed; 53- (NSError *)accountFetchError; 54- (void)setAccountFetchError:(NSError *)error; 55 56- (GDataFeedAnalyticsData *)analyticsDataFeed; 57- (void)setAnalyticsDataFeed:(GDataFeedAnalyticsData *)feed; 58- (NSError *)analyticsDataFetchError; 59- (void)setAnalyticsDataFetchError:(NSError *)error; 60@end 61 62@implementation AnalyticsSampleWindowController 63 64static AnalyticsSampleWindowController* gWindowController = nil; 65 66+ (AnalyticsSampleWindowController *)sharedAnalyticsSampleWindowController { 67 68 if (!gWindowController) { 69 gWindowController = [[AnalyticsSampleWindowController alloc] init]; 70 } 71 return gWindowController; 72} 73 74- (id)init { 75 return [self initWithWindowNibName:@"AnalyticsSampleWindow"]; 76} 77 78// dimensions and metrics, from 79// http://code.google.com/apis/analytics/docs/gdata/gdataReferenceDimensionsMetrics.html 80 81- (NSArray *)dimensionNames { 82 return [NSArray arrayWithObjects: 83 @"ga:browser", @"ga:browserVersion", @"ga:city", 84 @"ga:connectionSpeed", @"ga:continent", @"ga:visitCount", 85 @"ga:country", @"ga:date", @"ga:day", @"ga:daysSinceLastVisit", 86 @"ga:flashVersion", @"ga:hostname", @"ga:hour", @"ga:javaEnabled", 87 @"ga:language", @"ga:latitude", @"ga:longitude", @"ga:month", 88 @"ga:networkDomain", @"ga:networkLocation", @"ga:pageDepth", 89 @"ga:operatingSystem", @"ga:operatingSystemVersion", @"ga:region", 90 @"ga:screenColors", @"ga:screenResolution", @"ga:subContinent", 91 @"ga:userDefinedValue", @"ga:visitLength", @"ga:visitorType", 92 @"ga:week", @"ga:year", @"ga:adContent", @"ga:adGroup", @"ga:adSlot", 93 @"ga:adSlotPosition", @"ga:campaign", @"ga:keyword", @"ga:medium", 94 @"ga:referralPath", @"ga:source", @"ga:exitPagePath", 95 @"ga:landingPagePath", @"ga:secondPagePath", @"ga:pagePath", 96 @"ga:pageTitle", @"ga:affiliation", @"ga:daysToTransaction", 97 @"ga:productCategory", @"ga:productName", @"ga:productSku", 98 @"ga:transactionId", @"ga:searchCategory", 99 @"ga:searchDestinationPage", @"ga:searchKeyword", 100 @"ga:searchKeywordRefinement", @"ga:searchStartPage", 101 @"ga:searchUsed", @"ga:nextPagePath", @"ga:previousPagePath", 102 @"ga:eventCategory", @"ga:eventAction", @"ga:eventLabel", nil]; 103} 104 105- (NSArray *)metricNames { 106 return [NSArray arrayWithObjects: 107 @"ga:adClicks", @"ga:adCost", @"ga:bounces ", @"ga:CPC", @"ga:CPM", 108 @"ga:CTR", @"ga:entrances", @"ga:exits", @"ga:goal1Completions", 109 @"ga:goal1Starts", @"ga:goal1Value", @"ga:goal2Completions", 110 @"ga:goal2Starts", @"ga:goal2Value", @"ga:goal3Completions", 111 @"ga:goal3Starts", @"ga:goal3Value", @"ga:goal4Completions", 112 @"ga:goal4Starts", @"ga:goal4Value", @"ga:goalCompletionsAll", 113 @"ga:goalStartsAll", @"ga:goalValueAll", @"ga:impressions", 114 @"ga:itemQuantity", @"ga:itemRevenue", @"ga:newVisits", 115 @"ga:pageviews", @"ga:searchDepth", @"ga:searchDuration", 116 @"ga:searchExits", @"ga:searchRefinements", @"ga:searchUniques", 117 @"ga:searchVisits", @"ga:timeOnPage", @"ga:timeOnSite", 118 @"ga:transactionRevenue", @"ga:transactions", 119 @"ga:transactionShipping", @"ga:transactionTax", 120 @"ga:uniquePageviews", @"ga:uniquePurchases", 121 @"ga:visitors", @"ga:visits", 122 @"ga:totalEvents", @"ga:uniqueEvents", @"ga:eventValue", nil]; 123} 124 125- (void)awakeFromNib { 126 // Set the result text fields to have a distinctive color and mono-spaced font 127 // to aid in understanding of each query operation. 128 129 [mAccountsResultTextField setTextColor:[NSColor darkGrayColor]]; 130 [mAnalyticsDataResultTextField setTextColor:[NSColor darkGrayColor]]; 131 132 NSFont *resultTextFont = [NSFont fontWithName:@"Monaco" size:9]; 133 [mAccountsResultTextField setFont:resultTextFont]; 134 [mAnalyticsDataResultTextField setFont:resultTextFont]; 135 136 // set the date pickers to run from last week to today 137 NSTimeInterval aWeekAgo = -7 * 24 * 60 * 60; 138 [mStartDatePicker setDateValue:[NSDate dateWithTimeIntervalSinceNow:aWeekAgo]]; 139 [mEndDatePicker setDateValue:[NSDate date]]; 140 141 // load the pop-ups for dimensions and metrics, and check one item for each 142 [mDimensionsPopup addItemsWithTitles:[self dimensionNames]]; 143 [[mDimensionsPopup itemWithTitle:@"ga:country"] setState:NSOnState]; 144 145 [mMetricsPopup addItemsWithTitles:[self metricNames]]; 146 [[mMetricsPopup itemWithTitle:@"ga:pageviews"] setState:NSOnState]; 147 148 [self updateUI]; 149} 150 151- (void)dealloc { 152 [mAccountFeed release]; 153 [mAccountFetchError release]; 154 155 [mAnalyticsDataFeed release]; 156 [mAnalyticsDataFetchError release]; 157 158 [super dealloc]; 159} 160 161#pragma mark - 162 163- (void)updateUI { 164 165 // account list display 166 [mAccountsTable reloadData]; 167 168 if (mIsAccountFetchPending) { 169 [mAccountsProgressIndicator startAnimation:self]; 170 } else { 171 [mAccountsProgressIndicator stopAnimation:self]; 172 } 173 174 // account fetch result or selected item 175 NSString *accountResultStr = @""; 176 if (mAccountFetchError) { 177 accountResultStr = [mAccountFetchError description]; 178 } else { 179 GDataEntryAnalyticsAccount *account = [self selectedAccount]; 180 if (account) { 181 accountResultStr = [account description]; 182 } 183 } 184 [mAccountsResultTextField setString:accountResultStr]; 185 186 187 // analyticsData list display 188 [mAnalyticsDataTable reloadData]; 189 190 if (mIsAnalyticsDataFetchPending) { 191 [mAnalyticsDataProgressIndicator startAnimation:self]; 192 } else { 193 [mAnalyticsDataProgressIndicator stopAnimation:self]; 194 } 195 196 // analyticsData fetch result or selected item 197 NSString *analyticsDataResultStr = @""; 198 if (mAnalyticsDataFetchError) { 199 analyticsDataResultStr = [mAnalyticsDataFetchError description]; 200 } else { 201 GDataEntryAnalyticsData *analyticsData = [self selectedAnalyticsData]; 202 if (analyticsData) { 203 analyticsDataResultStr = [analyticsData description]; 204 } 205 } 206 [mAnalyticsDataResultTextField setString:analyticsDataResultStr]; 207 208 209 // the reload button is useful after the user changes dates or checkboxes 210 // for metrics and dimensions 211 GDataEntryAnalyticsAccount *selectedAccount = [self selectedAccount]; 212 [mReloadButton setEnabled:(selectedAccount != nil)]; 213 214 215 // update the comma-separated lists of dimensions and metrics to match the 216 // checked items in the pop-up menus 217 NSString *dimensions = [self itemListForPopup:mDimensionsPopup]; 218 [mDimensionsField setStringValue:dimensions]; 219 220 NSString *metrics = [self itemListForPopup:mMetricsPopup]; 221 [mMetricsField setStringValue:metrics]; 222} 223 224- (IBAction)loggingCheckboxClicked:(id)sender { 225 [GTMHTTPFetcher setLoggingEnabled:[sender state]]; 226} 227 228#pragma mark IBActions 229 230- (IBAction)getAccountsClicked:(id)sender { 231 232 NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet]; 233 234 NSString *username = [mUsernameField stringValue]; 235 username = [username stringByTrimmingCharactersInSet:whitespace]; 236 237 if ([username rangeOfString:@"@"].location == NSNotFound) { 238 // if no domain was supplied, add @gmail.com 239 username = [username stringByAppendingString:@"@gmail.com"]; 240 } 241 242 [mUsernameField setStringValue:username]; 243 244 [self fetchFeedOfAccounts]; 245} 246 247#pragma mark - 248 249// get a account service object with the current username/password 250// 251// A "service" object handles networking tasks. Service objects 252// contain user authentication information as well as networking 253// state information (such as cookies and the "last modified" date for 254// fetched data.) 255 256- (GDataServiceGoogleAnalytics *)analyticsService { 257 258 static GDataServiceGoogleAnalytics* service = nil; 259 260 if (!service) { 261 service = [[GDataServiceGoogleAnalytics alloc] init]; 262 263 [service setShouldCacheResponseData:YES]; 264 [service setServiceShouldFollowNextLinks:YES]; 265 } 266 267 // username/password may change 268 NSString *username = [mUsernameField stringValue]; 269 NSString *password = [mPasswordField stringValue]; 270 271 [service setUserCredentialsWithUsername:username 272 password:password]; 273 return service; 274} 275 276// get the account selected in the top list, or nil if none 277- (GDataEntryAnalyticsAccount *)selectedAccount { 278 279 NSArray *accounts = [mAccountFeed entries]; 280 int rowIndex = [mAccountsTable selectedRow]; 281 if ([accounts count] > 0 && rowIndex > -1) { 282 283 GDataEntryAnalyticsAccount *account = [accounts objectAtIndex:rowIndex]; 284 return account; 285 } 286 return nil; 287} 288 289// get the analyticsData selected in the second list, or nil if none 290- (GDataEntryAnalyticsData *)selectedAnalyticsData { 291 292 NSArray *entries = [mAnalyticsDataFeed entries]; 293 int rowIndex = [mAnalyticsDataTable selectedRow]; 294 if ([entries count] > 0 && rowIndex > -1) { 295 296 GDataEntryAnalyticsData *analyticsData = [entries objectAtIndex:rowIndex]; 297 return analyticsData; 298 } 299 return nil; 300} 301 302- (IBAction)refreshAccountData:(id)sender { 303 [self fetchSelectedAccount]; 304} 305 306#pragma mark Fetch feed of all of the user's accounts 307 308// begin retrieving the list of the user's accounts 309- (void)fetchFeedOfAccounts { 310 311 [self setAccountFeed:nil]; 312 [self setAccountFetchError:nil]; 313 314 [self setAnalyticsDataFeed:nil]; 315 [self setAnalyticsDataFetchError:nil]; 316 317 mIsAccountFetchPending = YES; 318 319 GDataServiceGoogleAnalytics *service = [self analyticsService]; 320 NSURL *feedURL = [NSURL URLWithString:kGDataGoogleAnalyticsDefaultAccountFeed]; 321 [service fetchFeedWithURL:feedURL 322 feedClass:[GDataFeedAnalyticsAccount class] 323 delegate:self 324 didFinishSelector:@selector(accountFeedTicket:finishedWithFeed:error:)]; 325 326 [self updateUI]; 327} 328 329// account list fetch callback 330- (void)accountFeedTicket:(GDataServiceTicket *)ticket 331 finishedWithFeed:(GDataFeedAnalyticsAccount *)feed 332 error:(NSError *)error { 333 334 [self setAccountFeed:feed]; 335 [self setAccountFetchError:error]; 336 337 mIsAccountFetchPending = NO; 338 [self updateUI]; 339} 340 341#pragma mark Fetch a account's analyticsData 342 343// for the account selected in the top list, begin retrieving the feed of 344// analytics data 345 346- (void)fetchSelectedAccount { 347 348 GDataEntryAnalyticsAccount *accountEntry = [self selectedAccount]; 349 if (accountEntry != nil) { 350 351 [self setAnalyticsDataFeed:nil]; 352 [self setAnalyticsDataFetchError:nil]; 353 mIsAnalyticsDataFetchPending = YES; 354 355 NSString *tableID = [accountEntry tableID]; 356 357 NSString *startDateStr, *endDateStr; 358 startDateStr = [self analyticsDateStringForDatePicker:mStartDatePicker]; 359 endDateStr = [self analyticsDateStringForDatePicker:mEndDatePicker]; 360 361 GDataQueryAnalytics *query; 362 query = [GDataQueryAnalytics analyticsDataQueryWithTableID:tableID 363 startDateString:startDateStr 364 endDateString:endDateStr]; 365 366 NSString *dimensions = [self itemListForPopup:mDimensionsPopup]; 367 [query setDimensions:dimensions]; 368 369 NSString *metrics = [self itemListForPopup:mMetricsPopup]; 370 [query setMetrics:metrics]; 371 372 GDataServiceGoogleAnalytics *service = [self analyticsService]; 373 [service fetchFeedWithQuery:query 374 feedClass:[GDataFeedAnalyticsData class] 375 delegate:self 376 didFinishSelector:@selector(analyticsDataFeedTicket:finishedWithFeed:error:)]; 377 [self updateUI]; 378 } 379} 380 381// analytics data fetch callback 382- (void)analyticsDataFeedTicket:(GDataServiceTicket *)ticket 383 finishedWithFeed:(GDataFeedAnalyticsData *)feed 384 error:(NSError *)error { 385 386 [self setAnalyticsDataFeed:feed]; 387 [self setAnalyticsDataFetchError:error]; 388 389 mIsAnalyticsDataFetchPending = NO; 390 391 [self updateUI]; 392} 393 394#pragma mark TableView delegate methods 395// 396// table view delegate methods 397// 398 399- (void)tableViewSelectionDidChange:(NSNotification *)notification { 400 id obj = [notification object]; 401 if (obj == mAccountsTable) { 402 // the user clicked on an account, so fetch its analytics data 403 [self fetchSelectedAccount]; 404 } else { 405 // the user clicked on an analytics data entry 406 [self updateUI]; 407 } 408} 409 410// table view data source methods 411- (int)numberOfRowsInTableView:(NSTableView *)tableView { 412 if (tableView == mAccountsTable) { 413 return [[mAccountFeed entries] count]; 414 } else { 415 return [[mAnalyticsDataFeed entries] count]; 416 } 417} 418 419- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row { 420 if (tableView == mAccountsTable) { 421 // get the account entry's title 422 GDataEntryAnalyticsAccount *entry; 423 entry = [[mAccountFeed entries] objectAtIndex:row]; 424 425 return [[entry title] stringValue]; 426 } else { 427 // get the analyticsData entry's title 428 GDataEntryAnalyticsData *entry; 429 entry = [[mAnalyticsDataFeed entries] objectAtIndex:row]; 430 431 return [[entry title] stringValue]; 432 } 433} 434 435#pragma mark Menu item actions 436 437- (IBAction)menuItemClicked:(id)sender { 438 // toggle the checkmark for the selected pop-up menu item 439 NSMenuItem *item = [sender selectedItem]; 440 int oldState = [item state]; 441 [item setState:(!oldState)]; 442 443 [self updateUI]; 444} 445 446#pragma mark UI-related utilities 447 448// return a date string like @"2001-03-15" for the date picker 449- (NSString *)analyticsDateStringForDatePicker:(NSDatePicker *)picker { 450 NSDate *date = [picker dateValue]; 451 452 unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit 453 | NSDayCalendarUnit; 454 NSCalendar *calendar = [NSCalendar currentCalendar]; 455 456 NSDateComponents *dateComponents = [calendar components:unitFlags 457 fromDate:date]; 458 NSString *dateString = [NSString stringWithFormat:@"%04u-%02u-%02u", 459 [dateComponents year], [dateComponents month], 460 [dateComponents day]]; 461 return dateString; 462} 463 464// return a comma-separated list string like @"ga:country,ga:networkDomain" 465- (NSString *)itemListForPopup:(NSPopUpButton *)popup { 466 467 NSArray *checkedItems = [GDataUtilities objectsFromArray:[popup itemArray] 468 withValue:[NSNumber numberWithInt:NSOnState] 469 forKeyPath:@"state"]; 470 471 NSArray *titles = [checkedItems valueForKey:@"title"]; 472 NSString *commaSeparatedList = [titles componentsJoinedByString:@","]; 473 return commaSeparatedList; 474} 475 476#pragma mark Setters and Getters 477 478- (GDataFeedAnalyticsAccount *)accountFeed { 479 return mAccountFeed; 480} 481 482- (void)setAccountFeed:(GDataFeedAnalyticsAccount *)feed { 483 [mAccountFeed autorelease]; 484 mAccountFeed = [feed retain]; 485} 486 487- (NSError *)accountFetchError { 488 return mAccountFetchError; 489} 490 491- (void)setAccountFetchError:(NSError *)error { 492 [mAccountFetchError release]; 493 mAccountFetchError = [error retain]; 494} 495 496 497- (GDataFeedAnalyticsData *)analyticsDataFeed { 498 return mAnalyticsDataFeed; 499} 500 501- (void)setAnalyticsDataFeed:(GDataFeedAnalyticsData *)feed { 502 [mAnalyticsDataFeed autorelease]; 503 mAnalyticsDataFeed = [feed retain]; 504} 505 506- (NSError *)analyticsDataFetchError { 507 return mAnalyticsDataFetchError; 508} 509 510- (void)setAnalyticsDataFetchError:(NSError *)error { 511 [mAnalyticsDataFetchError release]; 512 mAnalyticsDataFetchError = [error retain]; 513} 514 515@end