/core/externals/update-engine/externals/gdata-objectivec-client/Examples/AnalyticsSample/AnalyticsSampleWindowController.m

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