PageRenderTime 108ms CodeModel.GetById 2ms app.highlight 101ms RepoModel.GetById 1ms app.codeStats 0ms

/Source/externals/GData/Examples/AnalyticsSample/AnalyticsSampleWindowController.m

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