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