betapond-gattica 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +475 -0
- data/Rakefile +24 -0
- data/VERSION.yml +5 -0
- data/gattica.gemspec +72 -0
- data/lib/gattica.rb +35 -0
- data/lib/gattica/account.rb +34 -0
- data/lib/gattica/auth.rb +53 -0
- data/lib/gattica/convertible.rb +39 -0
- data/lib/gattica/data_point.rb +60 -0
- data/lib/gattica/data_set.rb +52 -0
- data/lib/gattica/engine.rb +283 -0
- data/lib/gattica/exceptions.rb +21 -0
- data/lib/gattica/hash_extensions.rb +20 -0
- data/lib/gattica/segment.rb +17 -0
- data/lib/gattica/settings.rb +51 -0
- data/lib/gattica/user.rb +31 -0
- data/test/helper.rb +14 -0
- data/test/settings.rb +28 -0
- data/test/suite.rb +6 -0
- data/test/test_engine.rb +48 -0
- data/test/test_results.rb +23 -0
- data/test/test_user.rb +24 -0
- metadata +124 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm ruby-1.9.2-p180
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2009 Rob Cameron
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,475 @@
|
|
1
|
+
Gattica
|
2
|
+
=======
|
3
|
+
Gattica is an easy to use Gem for getting data from the Google Analytics API.
|
4
|
+
|
5
|
+
Features
|
6
|
+
--------
|
7
|
+
* Supports: metrics, dimensions, sorting, filters, goals, and segments.
|
8
|
+
* Handles accounts with over 1000 profiles
|
9
|
+
* Returns data as: hash, json, CSV
|
10
|
+
|
11
|
+
[How to export Google Analytics data using Ruby](
|
12
|
+
http://www.seerinteractive.com/blog/google-analytics-data-export-api-with-rubygattica/2011/02/22/) (Links to my blog post on [Seer Interactive](http://www.seerinteractive.com))
|
13
|
+
|
14
|
+
<hr />
|
15
|
+
|
16
|
+
Quick Start
|
17
|
+
===========
|
18
|
+
Here are bare basics to get you up and running.
|
19
|
+
|
20
|
+
Installation
|
21
|
+
------------
|
22
|
+
Add Gattica to your Gemfile
|
23
|
+
|
24
|
+
gem 'gattica', :git => 'git://github.com/chrisle/gattica.git'
|
25
|
+
|
26
|
+
Don't forget to bundle install:
|
27
|
+
|
28
|
+
$ bundle install
|
29
|
+
|
30
|
+
Login, get a list of accounts, pick an account, and get data:
|
31
|
+
|
32
|
+
# Include the gem
|
33
|
+
require 'gattica'
|
34
|
+
|
35
|
+
# Login
|
36
|
+
ga = Gattica.new({
|
37
|
+
:email => 'email@gmail.com',
|
38
|
+
:password => 'password'
|
39
|
+
})
|
40
|
+
|
41
|
+
# Get a list of accounts
|
42
|
+
accounts = ga.accounts
|
43
|
+
|
44
|
+
# Choose the first account
|
45
|
+
ga.profile_id = accounts.first.profile_id
|
46
|
+
|
47
|
+
# Get the data
|
48
|
+
data = ga.get({
|
49
|
+
:start_date => '2011-01-01',
|
50
|
+
:end_date => '2011-04-01',
|
51
|
+
:dimensions => ['month', 'year'],
|
52
|
+
:metrics => ['visits', 'bounces'],
|
53
|
+
})
|
54
|
+
|
55
|
+
# Show the data
|
56
|
+
puts data.inspect
|
57
|
+
|
58
|
+
<hr />
|
59
|
+
|
60
|
+
General Usage
|
61
|
+
=============
|
62
|
+
|
63
|
+
### Create your Gattica object
|
64
|
+
|
65
|
+
ga = Gattica.new({ :email => 'email@gmail.com', :password => 'password' })
|
66
|
+
puts ga.token # => returns a big alpha-numeric string
|
67
|
+
|
68
|
+
### Query for accounts you have access to
|
69
|
+
|
70
|
+
# Retrieve a list of accounts
|
71
|
+
accounts = ga.accounts
|
72
|
+
|
73
|
+
# Show information about accounts
|
74
|
+
puts "---------------------------------"
|
75
|
+
puts "Available profiles: " + accounts.count.to_s
|
76
|
+
accounts.each do |account|
|
77
|
+
puts " --> " + account.title
|
78
|
+
puts " last updated: " + account.updated.inspect
|
79
|
+
puts " web property: " + account.web_property_id
|
80
|
+
puts " profile id: " + account.profile_id.inspect
|
81
|
+
puts " goals: " + account.goals.count.inspect
|
82
|
+
end
|
83
|
+
|
84
|
+
### Set which profile Gattica needs to use
|
85
|
+
|
86
|
+
# Tell Gattica to query profile ID 5555555
|
87
|
+
ga.profile_id = 5555555
|
88
|
+
|
89
|
+
### Get data from Google Analytics
|
90
|
+
|
91
|
+
The Get method will get data from Google Analytics and return Gattica::DataSet type.
|
92
|
+
|
93
|
+
* Dates must be in 'YYYY-MM-DD' format.
|
94
|
+
* Dimensions and metrics can be gotten from [Google Analytics Dimensions & Metrics Reference](http://code.google.com/apis/analytics/docs/gdata/gdataReferenceDimensionsMetrics.html)
|
95
|
+
* You do not need to use "ga:" at the beginning of the strings.
|
96
|
+
|
97
|
+
Here's an example:
|
98
|
+
|
99
|
+
# Get the number of visitors by month from Jan 1st to April 1st.
|
100
|
+
data = ga.get({
|
101
|
+
:start_date => '2011-01-01',
|
102
|
+
:end_date => '2011-04-01',
|
103
|
+
:dimensions => ['month', 'year'],
|
104
|
+
:metrics => ['visitors']
|
105
|
+
})
|
106
|
+
|
107
|
+
<hr />
|
108
|
+
|
109
|
+
Using Dimension & Metrics
|
110
|
+
=========================
|
111
|
+
|
112
|
+
Here are some additional examples that illustrate different things you can do with dimensions and metrics.
|
113
|
+
|
114
|
+
|
115
|
+
### Sorting
|
116
|
+
|
117
|
+
# Sorting by number of visits in descending order (most visits at the top)
|
118
|
+
data = ga.get({
|
119
|
+
:start_date => '2011-01-01',
|
120
|
+
:end_date => '2011-04-01',
|
121
|
+
:dimensions => ['month', 'year'],
|
122
|
+
:metrics => ['visits']
|
123
|
+
:sort => ['-visits'],
|
124
|
+
})
|
125
|
+
|
126
|
+
### Limiting results
|
127
|
+
|
128
|
+
# Limit the number of results to 25.
|
129
|
+
data = ga.get({
|
130
|
+
:start_date => '2011-01-01',
|
131
|
+
:end_date => '2011-04-01',
|
132
|
+
:dimensions => ['month', 'year'],
|
133
|
+
:metrics => ['visits']
|
134
|
+
:max_results => 25
|
135
|
+
})
|
136
|
+
|
137
|
+
### Results as a Hash
|
138
|
+
|
139
|
+
my_hash = data.to_h['points']
|
140
|
+
|
141
|
+
# =>
|
142
|
+
# [{
|
143
|
+
# "xml" => "<entry gd:etag=\"W/".... </entry>",
|
144
|
+
# "id" => "http://www.google.com/analytics/feeds/data?...",
|
145
|
+
# "updated" => Thu, 31 Mar 2011 17:00:00 -0700,
|
146
|
+
# "title" => "ga:month=01 | ga:year=2011",
|
147
|
+
# "dimensions" => [{:month=>"01"}, {:year=>"2011"}],
|
148
|
+
# "metrics" => [{:visitors=>6}]
|
149
|
+
# },
|
150
|
+
# {
|
151
|
+
# "xml" => ...
|
152
|
+
# "id" => ...
|
153
|
+
# "updated" => ...
|
154
|
+
# ...
|
155
|
+
# }]
|
156
|
+
|
157
|
+
|
158
|
+
### JSON formatted string
|
159
|
+
|
160
|
+
# Return data as a json string. (Useful for NoSQL databases)
|
161
|
+
my_json = data.to_h['points'].to_json
|
162
|
+
|
163
|
+
# =>
|
164
|
+
# "[{
|
165
|
+
# \"xml\":\"<entry> .... </entry>\",
|
166
|
+
# \"id\":\"http://www.google.com/analytics/feeds/data? ...",
|
167
|
+
# \"updated\":\"2011-03-31T17:00:00-07:00\",
|
168
|
+
# \"title\":\"ga:month=01 | ga:year=2011\",
|
169
|
+
# \"dimensions\":[{\"month\":\"01\"},{\"year\":\"2011\"}],
|
170
|
+
# \"metrics\":[{\"visitors\":6}]
|
171
|
+
# },
|
172
|
+
# {
|
173
|
+
# \"xml\":\"<entry> .... </entry>\",
|
174
|
+
# \"id\":\"http://www.google.com/analytics/feeds/data? ...",
|
175
|
+
# ...
|
176
|
+
# }]"
|
177
|
+
|
178
|
+
### CSV formatted string
|
179
|
+
|
180
|
+
# Return the data in CSV format. (Useful for using in Excel.)
|
181
|
+
|
182
|
+
# Short CSV will only return your dimensions and metrics:
|
183
|
+
short_csv = data.to_csv(:short)
|
184
|
+
|
185
|
+
# => "month,year,visitors\n\n01,2011, ...."
|
186
|
+
|
187
|
+
# Long CSV will get you a few additional columns:
|
188
|
+
long_csv = data.to_csv
|
189
|
+
|
190
|
+
# => "id,updated,title,month,year,visitors\n\nhttp:// ..."
|
191
|
+
|
192
|
+
|
193
|
+
### DIY formatting
|
194
|
+
|
195
|
+
# You can work directly with the 'point' method to return data.
|
196
|
+
data.points.each do |data_point|
|
197
|
+
month = data_point.dimensions.detect { |dim| dim.key == :month }.value
|
198
|
+
year = data_point.dimensions.detect { |dim| dim.key == :year }.value
|
199
|
+
visitors = data_point.metrics.detect { |metric| metric.key == :visitors }.value
|
200
|
+
puts "#{month}/#{year} got #{visitors} visitors"
|
201
|
+
end
|
202
|
+
|
203
|
+
# =>
|
204
|
+
# 01/2011 got 34552 visitors
|
205
|
+
# 02/2011 got 36732 visitors
|
206
|
+
# 03/2011 got 45642 visitors
|
207
|
+
# 04/2011 got 44456 visitors
|
208
|
+
|
209
|
+
<hr />
|
210
|
+
|
211
|
+
Using Filter, Goals, and Segments
|
212
|
+
=========================
|
213
|
+
|
214
|
+
Learn more about filters: [Google Data feed filtering reference](http://code.google.com/apis/analytics/docs/gdata/gdataReference.html#filtering)
|
215
|
+
|
216
|
+
|
217
|
+
### Get profiles with goals
|
218
|
+
|
219
|
+
# Get all the profiles that have goals
|
220
|
+
profiles_with_goals = accounts.select { |account| account.goals.count > 0 }
|
221
|
+
|
222
|
+
# =>
|
223
|
+
# [{
|
224
|
+
# "id" => "http://www.google.com/analytics/feeds/accounts/ga:...",
|
225
|
+
# "updated" => Mon, 16 May 2011 16:40:30 -0700,
|
226
|
+
# "title" => "Profile Title",
|
227
|
+
# "table_id" => "ga:123456",
|
228
|
+
# "account_id" => 123456,
|
229
|
+
# "account_name" => "Account name",
|
230
|
+
# "profile_id" => 123456,
|
231
|
+
# "web_property_id" => "UA-123456-3",
|
232
|
+
# "goals"=>[{
|
233
|
+
# :active => "true",
|
234
|
+
# :name => "Goal name",
|
235
|
+
# :number => 1,
|
236
|
+
# :value => 0.0
|
237
|
+
# }]
|
238
|
+
# },
|
239
|
+
# {
|
240
|
+
# "id" => "http://www.google.com/analytics/feeds/accounts/ga:...",
|
241
|
+
# "updated" => Mon, 16 May 2011 16:40:30 -0700,
|
242
|
+
# "title" => "Profile Title",
|
243
|
+
# ...
|
244
|
+
# }]
|
245
|
+
|
246
|
+
### List available segments
|
247
|
+
|
248
|
+
# Get all the segments that are available to you
|
249
|
+
segments = ga.segments
|
250
|
+
|
251
|
+
# Segments with negative gaid are default segments from Google. Segments
|
252
|
+
# with positive gaid numbers are custom segments that you created.
|
253
|
+
# =>
|
254
|
+
# [{
|
255
|
+
# "id" => "gaid::-1",
|
256
|
+
# "name" => "All Visits",
|
257
|
+
# "definition" => " "
|
258
|
+
# },
|
259
|
+
# {
|
260
|
+
# "id" => "gaid::-2",
|
261
|
+
# "name" => "New Visitors",
|
262
|
+
# "definition" => "ga:visitorType==New Visitor"
|
263
|
+
# },
|
264
|
+
# {
|
265
|
+
# "id" => ... # more default segments
|
266
|
+
# "name" => ...
|
267
|
+
# "definition" => ...
|
268
|
+
# },
|
269
|
+
# {
|
270
|
+
# "id" => "gaid::12345678",
|
271
|
+
# "name" => "Name of segment",
|
272
|
+
# "definition" => "ga:keyword=...."
|
273
|
+
# },
|
274
|
+
# {
|
275
|
+
# "id" => ... # more custom segments
|
276
|
+
# "name" => ...
|
277
|
+
# "definition" => ...
|
278
|
+
# }]
|
279
|
+
|
280
|
+
### Query by segment
|
281
|
+
|
282
|
+
# Return visits and bounces for mobile traffic
|
283
|
+
# (Google's default user segment gaid::-11)
|
284
|
+
|
285
|
+
mobile_traffic = ga.get({
|
286
|
+
:start_date => '2011-01-01',
|
287
|
+
:end_date => '2011-02-01',
|
288
|
+
:dimensions => ['month', 'year'],
|
289
|
+
:metrics => ['visits', 'bounces'],
|
290
|
+
:segment => ['gaid::-11']
|
291
|
+
})
|
292
|
+
|
293
|
+
### Filtering
|
294
|
+
|
295
|
+
Filters are boolean expressions in strings. Here's an example of an equality:
|
296
|
+
|
297
|
+
# Filter by Firefox users
|
298
|
+
firefox_users = ga.get({
|
299
|
+
:start_date => '2010-01-01',
|
300
|
+
:end_date => '2011-01-01',
|
301
|
+
:dimensions => ['month', 'year'],
|
302
|
+
:metrics => ['visits', 'bounces'],
|
303
|
+
:filters => ['browser == Firefox']
|
304
|
+
})
|
305
|
+
|
306
|
+
Here's an example of greater-than:
|
307
|
+
|
308
|
+
# Filter where visits is >= 10000
|
309
|
+
lots_of_visits = ga.get({
|
310
|
+
:start_date => '2010-01-01',
|
311
|
+
:end_date => '2011-02-01',
|
312
|
+
:dimensions => ['month', 'year'],
|
313
|
+
:metrics => ['visits', 'bounces'],
|
314
|
+
:filters => ['visits >= 10000']
|
315
|
+
})
|
316
|
+
|
317
|
+
Multiple filters is an array. Currently, they are only joined by 'AND'.
|
318
|
+
|
319
|
+
# Firefox users and visits >= 10000
|
320
|
+
firefox_users_with_many_pageviews = ga.get({
|
321
|
+
:start_date => '2010-01-01',
|
322
|
+
:end_date => '2011-02-01',
|
323
|
+
:dimensions => ['month', 'year'],
|
324
|
+
:metrics => ['visits', 'bounces'],
|
325
|
+
:filters => ['browser == Firefox', 'visits >= 10000']
|
326
|
+
})
|
327
|
+
|
328
|
+
|
329
|
+
<hr />
|
330
|
+
|
331
|
+
Even More Examples!
|
332
|
+
===============
|
333
|
+
|
334
|
+
### Top 25 keywords that drove traffic
|
335
|
+
|
336
|
+
Output the top 25 keywords that drove traffic to your website in the first quarter of 2011.
|
337
|
+
|
338
|
+
# Get the top 25 keywords that drove traffic
|
339
|
+
data = ga.get({
|
340
|
+
:start_date => '2011-01-01',
|
341
|
+
:end_date => '2011-04-01',
|
342
|
+
:dimensions => ['keyword'],
|
343
|
+
:metrics => ['visits'],
|
344
|
+
:sort => ['-visits'],
|
345
|
+
:max_results => 25
|
346
|
+
})
|
347
|
+
|
348
|
+
# Output our results
|
349
|
+
data.points.each do |data_point|
|
350
|
+
kw = data_point.dimensions.detect { |dim| dim.key == :keyword }.value
|
351
|
+
visits = data_point.metrics.detect { |metric| metric.key == :visits }.value
|
352
|
+
puts "#{visits} visits => '#{kw}'"
|
353
|
+
end
|
354
|
+
|
355
|
+
# =>
|
356
|
+
# 19667 visits => '(not set)'
|
357
|
+
# 1677 visits => 'keyword 1'
|
358
|
+
# 178 visits => 'keyword 2'
|
359
|
+
# 165 visits => 'keyword 3'
|
360
|
+
# 161 visits => 'keyword 4'
|
361
|
+
# 112 visits => 'keyword 5'
|
362
|
+
# 105 visits => 'seo company reviews'
|
363
|
+
# ...
|
364
|
+
|
365
|
+
<hr />
|
366
|
+
|
367
|
+
Additional Options & Settings
|
368
|
+
=============================
|
369
|
+
|
370
|
+
Setting HTTP timeout
|
371
|
+
--------------------
|
372
|
+
|
373
|
+
If you have a lot of profiles in your account (like 1000+ profiles) querying for accounts may take over a minute. Net::HTTP will timeout and an exception will be raised.
|
374
|
+
|
375
|
+
To avoid this, specify a timeout when you instantiate the Gattica object:
|
376
|
+
|
377
|
+
ga = Gattica.new({
|
378
|
+
:email => 'email@gmail.com',
|
379
|
+
:password => 'password',
|
380
|
+
:timeout => 600 # Set timeout for 10 minutes!
|
381
|
+
})
|
382
|
+
|
383
|
+
The default timeout is 300 seconds (5 minutes). Change the default in: lib/gattica/settings.rb
|
384
|
+
|
385
|
+
For reference 1000 profiles with 2-5 goals each takes around 90-120 seconds.
|
386
|
+
|
387
|
+
Reusing a session token
|
388
|
+
-----------------------
|
389
|
+
|
390
|
+
You can reuse an older session if you still have the token string. Google recommends doing this to avoid authenticating over and over.
|
391
|
+
|
392
|
+
|
393
|
+
my_token = ga.token # => 'DSasdf94...'
|
394
|
+
|
395
|
+
# Sometime later, you can initialize Gattica with the same token
|
396
|
+
ga = Gattica.new({ :token => my_token })
|
397
|
+
|
398
|
+
If your token times out, you will need to re-authenticate.
|
399
|
+
|
400
|
+
Specifying your own headers
|
401
|
+
---------------------------
|
402
|
+
|
403
|
+
Google expects a special header in all HTTP requests called 'Authorization'. Gattica handles this header automatically. If you want to specify your own you can do that when you instantiate Gattica:
|
404
|
+
|
405
|
+
ga = Gattica.new({
|
406
|
+
:token => 'DSasdf94...',
|
407
|
+
:headers => {'My-Special-Header':'my_custom_value'}
|
408
|
+
})
|
409
|
+
|
410
|
+
<hr />
|
411
|
+
|
412
|
+
History
|
413
|
+
=======
|
414
|
+
|
415
|
+
Version history
|
416
|
+
---------------
|
417
|
+
### 0.4.4
|
418
|
+
* Added a configuration file to unit tests
|
419
|
+
* Removed version.rb. Not needed. (thanks John McGrath see: github.com/john)
|
420
|
+
* Migrated examples and rewrote README file
|
421
|
+
|
422
|
+
### 0.4.3
|
423
|
+
* FIXED: Typo in start-index parameter
|
424
|
+
* Refactored Engine class into it's own file.
|
425
|
+
* Began to re-style code to wrap at 80 characters
|
426
|
+
* Added some unit tests
|
427
|
+
|
428
|
+
### 0.4.2
|
429
|
+
* Added Ruby 1.9 support (Thanks @mathieuravaux https://github.com/mathieuravaux)
|
430
|
+
* Uses hpricot 0.8.4 now. 0.8.3 segfaults.
|
431
|
+
* Added ability to change the timeout when requesting analytics from Google
|
432
|
+
* Added the ability to use max_results
|
433
|
+
|
434
|
+
### 0.3.2.scottp
|
435
|
+
* scottp Added Analytics API v2 header, and basic support for "segment" argument.
|
436
|
+
|
437
|
+
### 0.3.2
|
438
|
+
* er1c updated to use standard Ruby CSV library
|
439
|
+
|
440
|
+
### 0.3.0
|
441
|
+
* Support for filters (filters are all AND'ed together, no OR yet)
|
442
|
+
|
443
|
+
### 0.2.1
|
444
|
+
* More robust error checking on HTTP calls
|
445
|
+
* Added to_xml to get raw XML output from Google
|
446
|
+
|
447
|
+
### 0.2.0 / 2009-04-27
|
448
|
+
* Changed initialization format: pass a hash of options rather than individual email, password and profile_id
|
449
|
+
* Can initialize with a valid token and use that instead of requiring email/password each time
|
450
|
+
* Can initialize with your own logger object instead of having to use the default (useful if you're using with Rails, initialize with RAILS_DEFAULT_LOGGER)
|
451
|
+
* Show error if token is invalid or expired (Google returns a 401 on any HTTP call)
|
452
|
+
* Started tests
|
453
|
+
|
454
|
+
### 0.1.4 / 2009-04-22
|
455
|
+
* Another attempt at getting the gem to build on github
|
456
|
+
|
457
|
+
### 0.1.3 / 2009-04-22
|
458
|
+
* Getting gem to build on github
|
459
|
+
|
460
|
+
### 0.1.2 / 2009-04-22
|
461
|
+
* Updated readme and examples, better documentation throughout
|
462
|
+
|
463
|
+
### 0.1.1 / 2009-04-22
|
464
|
+
* When outputting as CSV, surround each piece of data with double quotes (appears pretty common for various properties (like Browser name) to contain commas
|
465
|
+
|
466
|
+
### 0.1.0 / 2009-03-26
|
467
|
+
* Basic functionality working good. Can't use filters yet.
|
468
|
+
|
469
|
+
|
470
|
+
Maintainer history
|
471
|
+
------------------
|
472
|
+
* [Rob Cameron](https://github.com/activenetwork/gattica) (2010)
|
473
|
+
* [Mike Rumble](https://github.com/rumble/gattica) (2010)
|
474
|
+
* [Chris Le](https://github.com/chrisle/gattica) (Current)
|
475
|
+
|