ruby_desk 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +2 -1
- data/VERSION +1 -1
- data/lib/ruby_desk/connector.rb +61 -46
- data/lib/ruby_desk/provider.rb +10 -92
- data/lib/ruby_desk/snapshot.rb +35 -1
- data/lib/ruby_desk/team_room.rb +16 -9
- data/lib/ruby_desk/time_report.rb +119 -0
- data/lib/ruby_desk.rb +34 -28
- data/test/snapshot.json +2 -0
- data/test/teamrooms.json +2 -0
- data/test/test_ruby_desk.rb +105 -4
- data/test/timereport.json +1 -0
- data/test/workdiary.json +2 -0
- metadata +6 -2
- data/lib/ruby_desk/developer_skill.rb +0 -3
data/README.rdoc
CHANGED
@@ -6,7 +6,7 @@ A gem built by BadrIT (www.badrit.com) that works as an interface for oDesk APIs
|
|
6
6
|
Initialize with your api key
|
7
7
|
rd = RubyDesk::Connector.new(api_key, api_secret)
|
8
8
|
|
9
|
-
|
9
|
+
Get the URL that will ask the user to authenticate your application
|
10
10
|
rd.auth_url
|
11
11
|
|
12
12
|
You should then redirect the user to the returned URL to get the frob.
|
@@ -23,3 +23,4 @@ Now you are ready to use all the APIs you need
|
|
23
23
|
== Copyright
|
24
24
|
|
25
25
|
Copyright (c) 2010 Ahmed ElDawy. See LICENSE for details.
|
26
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/ruby_desk/connector.rb
CHANGED
@@ -5,12 +5,15 @@ require 'net/https'
|
|
5
5
|
|
6
6
|
module RubyDesk
|
7
7
|
class Connector
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
ODESK_URL = "www.odesk.com/"
|
9
|
+
ODESK_API_URL = "#{ODESK_URL}api/"
|
10
|
+
ODESK_GDS_URL = "#{ODESK_URL}gds/"
|
11
|
+
ODESK_AUTH_URL = "#{ODESK_URL}services/api/auth/"
|
12
|
+
DEFAULT_OPTIONS = {:secure=>true, :sign=>true, :format=>'json',
|
13
|
+
:base_url=>ODESK_API_URL, :auth=>true, :params=>{} }
|
14
|
+
|
12
15
|
attr_writer :frob
|
13
|
-
|
16
|
+
|
14
17
|
def initialize(api_key=nil, api_secret=nil, frob=nil, api_token=nil)
|
15
18
|
@api_key = api_key
|
16
19
|
@api_secret = api_secret
|
@@ -19,46 +22,52 @@ module RubyDesk
|
|
19
22
|
@api_key = "991ac77f4c202873b0ab88f11762370c"
|
20
23
|
@api_secret = "c3382d5902e5a7b0"
|
21
24
|
end
|
22
|
-
|
23
|
-
# Sign the given parameters and returns the signature
|
25
|
+
|
26
|
+
# Sign the given parameters and returns the signature
|
24
27
|
def sign(params)
|
25
28
|
# sort parameters by its names (keys)
|
26
29
|
sorted_params = params.sort { |a, b| a.to_s <=> b.to_s}
|
27
|
-
|
30
|
+
|
28
31
|
# concatenate secret with names, values
|
29
32
|
concatenated = @api_secret + sorted_params.to_s
|
30
|
-
|
31
|
-
# Calculate md5 of concatenated string
|
32
|
-
|
33
|
-
|
34
|
-
# Return the calculated value as the signature
|
35
|
-
md5
|
33
|
+
|
34
|
+
# Calculate and return md5 of concatenated string
|
35
|
+
Digest::MD5.hexdigest(concatenated)
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
# Returns the correct URL to go to to invoke the given api
|
39
39
|
# path: the path of the API to call. e.g. 'auth'
|
40
|
-
# params: a hash of parameters that needs to be appended
|
41
40
|
# options:
|
42
41
|
# * :secure=>false: Whether a secure connection is required or not.
|
43
42
|
# * :sign=>true: Whether you need to sign the parameters or not
|
44
|
-
|
43
|
+
# * :params=>{}: a hash of parameters that needs to be appended
|
44
|
+
# * :auth=>true: when true indicates that this call need authentication.
|
45
|
+
# This forces adding :api_token, :api_key and :api_sig to parameters.
|
46
|
+
def prepare_api_call(path, options = {})
|
45
47
|
options = DEFAULT_OPTIONS.merge(options)
|
46
|
-
params
|
47
|
-
|
48
|
-
|
48
|
+
params = options[:params]
|
49
|
+
params[:api_sig] = sign(params) if options[:sign] || options[:auth]
|
50
|
+
if options[:auth]
|
51
|
+
params[:api_token] ||= @api_token
|
52
|
+
params[:api_key] ||= @api_key
|
53
|
+
end
|
54
|
+
url = (options[:secure] ? "https" : "http") + "://"
|
55
|
+
url << options[:base_url] << path
|
56
|
+
url << ".#{options[:format]}" if options[:format]
|
57
|
+
return {:url=>url, :params=> params, :method=>options[:method]}
|
49
58
|
end
|
50
|
-
|
59
|
+
|
51
60
|
# invokes the given API call and returns body of the response as text
|
52
61
|
def invoke_api_call(api_call)
|
53
62
|
url = URI.parse(api_call[:url])
|
54
63
|
http = Net::HTTP.new(url.host, url.port)
|
55
64
|
http.use_ssl = true
|
56
|
-
|
65
|
+
|
57
66
|
data = api_call[:params].to_a.map{|pair| pair.join '='}.join('&')
|
58
67
|
headers = {
|
59
68
|
'Content-Type' => 'application/x-www-form-urlencoded'
|
60
69
|
}
|
61
|
-
|
70
|
+
|
62
71
|
case api_call[:method]
|
63
72
|
when :get, 'get' then
|
64
73
|
resp, data = http.request(Net::HTTP::Get.new(url.path+"?"+data, headers))
|
@@ -67,28 +76,30 @@ module RubyDesk
|
|
67
76
|
when :delete, 'delete' then
|
68
77
|
resp, data = http.request(Net::HTTP::Delete.new(url.path, headers), data)
|
69
78
|
end
|
70
|
-
|
79
|
+
|
71
80
|
return data
|
72
81
|
end
|
73
|
-
|
82
|
+
|
74
83
|
# Prepares an API call with the given arguments then invokes it and returns its body
|
75
|
-
def prepare_and_invoke_api_call(path,
|
76
|
-
api_call = prepare_api_call(path,
|
84
|
+
def prepare_and_invoke_api_call(path, options = {})
|
85
|
+
api_call = prepare_api_call(path, options)
|
77
86
|
invoke_api_call(api_call)
|
78
87
|
end
|
79
|
-
|
88
|
+
|
80
89
|
# Returns the URL that authenticates the application for the current user
|
81
90
|
def auth_url
|
82
|
-
|
83
|
-
|
84
|
-
|
91
|
+
auth_call = prepare_api_call("", :params=>{:api_key=>@api_key},
|
92
|
+
:base_url=>ODESK_AUTH_URL)
|
93
|
+
return auth_call[:url]
|
85
94
|
end
|
86
|
-
|
95
|
+
|
87
96
|
# return the URL that logs user out of odesk applications
|
88
97
|
def logout_url
|
89
|
-
"
|
98
|
+
logout_call = prepare_api_call("", :base_url=>ODESK_AUTH_URL,
|
99
|
+
:secure=>false)
|
100
|
+
return logout_call[:url]
|
90
101
|
end
|
91
|
-
|
102
|
+
|
92
103
|
# Returns an authentication frob.
|
93
104
|
# Parameters
|
94
105
|
# * frob
|
@@ -98,11 +109,13 @@ module RubyDesk
|
|
98
109
|
# Return Data
|
99
110
|
# * token
|
100
111
|
def get_token
|
101
|
-
response = prepare_and_invoke_api_call 'auth/v1/keys/tokens',
|
112
|
+
response = prepare_and_invoke_api_call 'auth/v1/keys/tokens',
|
113
|
+
:params=>{:frob=>@frob, :api_key=>@api_key}, :method=>:post,
|
114
|
+
:auth=>false
|
102
115
|
json = JSON.parse(response)
|
103
116
|
@api_token = json['token']
|
104
117
|
end
|
105
|
-
|
118
|
+
|
106
119
|
# Returns an authentication frob.
|
107
120
|
#Parameters
|
108
121
|
# * api_key
|
@@ -110,25 +123,26 @@ module RubyDesk
|
|
110
123
|
#
|
111
124
|
#Return Data
|
112
125
|
# * frob
|
113
|
-
|
126
|
+
|
114
127
|
def get_frob
|
115
|
-
response = prepare_and_invoke_api_call 'auth/v1/keys/frobs',
|
128
|
+
response = prepare_and_invoke_api_call 'auth/v1/keys/frobs',
|
129
|
+
:params=>{:api_key=>@api_key}, :method=>:post, :auth=>false
|
116
130
|
json = JSON.parse(response)
|
117
131
|
@frob = json['frob']
|
118
132
|
end
|
119
|
-
|
133
|
+
|
120
134
|
# Returns the authenticated user associated with the given authorization token.
|
121
135
|
# Parameters
|
122
|
-
#
|
136
|
+
#
|
123
137
|
# * api_key
|
124
138
|
# * api_sig
|
125
139
|
# * api_token
|
126
|
-
#
|
140
|
+
#
|
127
141
|
# Return Data
|
128
|
-
#
|
129
|
-
# * token
|
142
|
+
#
|
143
|
+
# * token
|
130
144
|
def check_token
|
131
|
-
prepare_and_invoke_api_call 'auth/v1/keys/token',
|
145
|
+
prepare_and_invoke_api_call 'auth/v1/keys/token', :method=>:get
|
132
146
|
json = JSON.parse(response)
|
133
147
|
# TODO what to make with results?
|
134
148
|
return json
|
@@ -141,9 +155,10 @@ module RubyDesk
|
|
141
155
|
# * api_token
|
142
156
|
|
143
157
|
def revoke_token
|
144
|
-
prepare_and_invoke_api_call 'auth/v1/keys/token',
|
158
|
+
prepare_and_invoke_api_call 'auth/v1/keys/token', :method=>:delete
|
145
159
|
@api_token = nil
|
146
160
|
end
|
147
|
-
|
161
|
+
|
148
162
|
end
|
149
163
|
end
|
164
|
+
|
data/lib/ruby_desk/provider.rb
CHANGED
@@ -1,91 +1,7 @@
|
|
1
1
|
class RubyDesk::Provider < RubyDesk::OdeskEntity
|
2
2
|
class << self
|
3
3
|
def all_categories
|
4
|
-
text =
|
5
|
-
Web Development
|
6
|
-
* Web Design
|
7
|
-
* Web Programming
|
8
|
-
* Ecommerce
|
9
|
-
* UI Design
|
10
|
-
* Website QA
|
11
|
-
* Website Project Management
|
12
|
-
* Other - Web Development
|
13
|
-
Software Development
|
14
|
-
* Desktop Applications
|
15
|
-
* Game Development
|
16
|
-
* Scripts & Utilities
|
17
|
-
* Software Plug-ins
|
18
|
-
* Mobile Apps
|
19
|
-
* Application Interface Design
|
20
|
-
* Software Project Management
|
21
|
-
* Software QA
|
22
|
-
* VOIP
|
23
|
-
* Other - Software Development
|
24
|
-
Networking & Information Systems
|
25
|
-
* Network Administration
|
26
|
-
* DBA - Database Administration
|
27
|
-
* Server Administration
|
28
|
-
* ERP / CRM Implementation
|
29
|
-
* Other - Networking & Information Systems
|
30
|
-
Writing & Translation
|
31
|
-
* Technical Writing
|
32
|
-
* Website Content
|
33
|
-
* Blog & Article Writing
|
34
|
-
* Copywriting
|
35
|
-
* Translation
|
36
|
-
* Creative Writing
|
37
|
-
* Other - Writing & Translation
|
38
|
-
Administrative Support
|
39
|
-
* Data Entry
|
40
|
-
* Personal Assistant
|
41
|
-
* Web Research
|
42
|
-
* Email Response Handling
|
43
|
-
* Transcription
|
44
|
-
* Other - Administrative Support
|
45
|
-
Design & Multimedia
|
46
|
-
* Graphic Design
|
47
|
-
* Logo Design
|
48
|
-
* Illustration
|
49
|
-
* Print Design
|
50
|
-
* 3D Modeling & CAD
|
51
|
-
* Audio Production
|
52
|
-
* Video Production
|
53
|
-
* Voice Talent
|
54
|
-
* Animation
|
55
|
-
* Presentations
|
56
|
-
* Engineering & Technical Design
|
57
|
-
* Other - Design & Multimedia
|
58
|
-
Customer Service
|
59
|
-
* Customer Service & Support
|
60
|
-
* Technical support
|
61
|
-
* Phone Support
|
62
|
-
* Order Processing
|
63
|
-
* Other - Customer Service
|
64
|
-
Sales & Marketing
|
65
|
-
* Advertising
|
66
|
-
* Email Marketing
|
67
|
-
* SEO - Search Engine Optimization
|
68
|
-
* SEM - Search Engine Marketing
|
69
|
-
* SMM - Social Media Marketing
|
70
|
-
* PR - Public Relations
|
71
|
-
* Telemarketing & Telesales
|
72
|
-
* Business Plans & Marketing Strategy
|
73
|
-
* Market Research & Surveys
|
74
|
-
* Sales & Lead Generation
|
75
|
-
* Other - Sales & Marketing
|
76
|
-
Business Services
|
77
|
-
* Accounting
|
78
|
-
* Bookkeeping
|
79
|
-
* HR / Payroll
|
80
|
-
* Financial Services & Planning
|
81
|
-
* Payment Processing
|
82
|
-
* Legal
|
83
|
-
* Project Management
|
84
|
-
* Business Consulting
|
85
|
-
* Recruiting
|
86
|
-
* Statistical Analysis
|
87
|
-
* Other - Business Services
|
88
|
-
CATEGORIES
|
4
|
+
text = File.read(File.join(File.dirname(__FILE__), "..", "categories"))
|
89
5
|
lines = text.split(/[\r\n]+/).map{|line| line.strip}
|
90
6
|
categories = {}
|
91
7
|
subcategories = nil
|
@@ -98,7 +14,7 @@ class RubyDesk::Provider < RubyDesk::OdeskEntity
|
|
98
14
|
end
|
99
15
|
return categories
|
100
16
|
end
|
101
|
-
|
17
|
+
|
102
18
|
# Implements search with the given criteria
|
103
19
|
#* q - ProfileData (ProfileData)
|
104
20
|
# * Profile data is any text that appears in a provider's profile. For example if q = 'odesk' the search would return any user's with the word odesk in their profile.
|
@@ -158,7 +74,8 @@ class RubyDesk::Provider < RubyDesk::OdeskEntity
|
|
158
74
|
if options.respond_to? :to_str
|
159
75
|
return search(connector, :q=>options.to_str)
|
160
76
|
end
|
161
|
-
response = connector.prepare_and_invoke_api_call
|
77
|
+
response = connector.prepare_and_invoke_api_call(
|
78
|
+
'profiles/v1/search/providers', :method=>:get)
|
162
79
|
# parses a JSON result returned from oDesk and extracts an array of Providers out of it
|
163
80
|
json = JSON.parse(response)
|
164
81
|
providers = []
|
@@ -167,11 +84,11 @@ class RubyDesk::Provider < RubyDesk::OdeskEntity
|
|
167
84
|
end
|
168
85
|
return providers
|
169
86
|
end
|
170
|
-
|
87
|
+
|
171
88
|
def get_profile(connector, id, options={})
|
172
89
|
brief = options.delete :brief || false
|
173
|
-
response = connector.prepare_and_invoke_api_call(
|
174
|
-
|
90
|
+
response = connector.prepare_and_invoke_api_call(
|
91
|
+
"profiles/v1/providers/#{id}" + (brief ? "/brief" : ""), :method=>:get)
|
175
92
|
json = JSON.parse(response)
|
176
93
|
return self.new(json['profile'])
|
177
94
|
end
|
@@ -203,7 +120,7 @@ class RubyDesk::Provider < RubyDesk::OdeskEntity
|
|
203
120
|
:ag_adj_score, :dev_recent_hours, :dev_timezone, :ag_country_tz, :ag_city,
|
204
121
|
:dev_test_passed_count, :dev_tot_feedback, :ag_summary, :ag_manager_name,
|
205
122
|
:ag_active_assignments, :portfolio_items
|
206
|
-
|
123
|
+
|
207
124
|
attribute :skills, :class=>RubyDesk::Skill, :sub_element=>'skill'
|
208
125
|
attribute :dev_scores, :class=>RubyDesk::DeveloperSkill, :sub_element=>'dev_score'
|
209
126
|
attribute :dev_ac_agencies, :class=>RubyDesk::Agency, :sub_element=>'dev_ac_agency'
|
@@ -228,4 +145,5 @@ class RubyDesk::Provider < RubyDesk::OdeskEntity
|
|
228
145
|
end
|
229
146
|
end
|
230
147
|
end
|
231
|
-
end
|
148
|
+
end
|
149
|
+
|
data/lib/ruby_desk/snapshot.rb
CHANGED
@@ -8,4 +8,38 @@ class RubyDesk::Snapshot < RubyDesk::OdeskEntity
|
|
8
8
|
|
9
9
|
attribute :user, :class=>RubyDesk::User
|
10
10
|
|
11
|
-
|
11
|
+
def self.work_diary(connector, company_id, user_id, date = nil, timezone = "mine")
|
12
|
+
response = connector.prepare_and_invoke_api_call(
|
13
|
+
"team/v1/workdiaries/#{company_id}/#{user_id}" + (date ? "/"+date : ""),
|
14
|
+
:params=>{:timezone=>timezone}, :method=>:get)
|
15
|
+
json = JSON.parse(response)
|
16
|
+
|
17
|
+
return nil unless json['snapshots']['snapshot']
|
18
|
+
[json['snapshots']['snapshot']].flatten.map do |snapshot|
|
19
|
+
self.new(snapshot)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.snapshot_details(connector, company_id, user_id, timestamp = nil)
|
24
|
+
timestamp_param = case timestamp
|
25
|
+
when String then timestamp
|
26
|
+
when Date, Time then timestamp.strftime("%Y%m%d")
|
27
|
+
when Range then [timestamp.first, timestamp.last].map{|t|t.strftime("%Y%m%d")}.join(",")
|
28
|
+
when Array then timestamp.map{|t| t.strftime("%Y%m%d")}.join(";")
|
29
|
+
end
|
30
|
+
# Invoke API call
|
31
|
+
response = connector.prepare_and_invoke_api_call(
|
32
|
+
"team/v1/workdiaries/#{company_id}/#{user_id}" +
|
33
|
+
(timestamp_param ? "/#{timestamp_param}" : ""), :method=>:get)
|
34
|
+
|
35
|
+
# Parse result in json format
|
36
|
+
json = JSON.parse(response)
|
37
|
+
|
38
|
+
# Generate ruby objects for each snapshot
|
39
|
+
[json['snapshot']].flatten.map do |snapshot|
|
40
|
+
self.new(snapshot)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
data/lib/ruby_desk/team_room.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
class RubyDesk::TeamRoom
|
2
|
+
# Attribute readers for all attributes
|
3
|
+
attr_reader :company_recno, :company_name, :name, :id, :recno, :teamroom_api
|
4
|
+
|
2
5
|
class << self
|
3
6
|
def get_teamrooms(connector)
|
4
|
-
response = connector.prepare_and_invoke_api_call 'team/v1/teamrooms',
|
7
|
+
response = connector.prepare_and_invoke_api_call 'team/v1/teamrooms',
|
8
|
+
:method=>:get
|
5
9
|
# parses a JSON result returned from oDesk and extracts an array of TeamRooms out of it
|
6
10
|
json = JSON.parse(response)
|
7
11
|
team_rooms = []
|
@@ -12,12 +16,9 @@ class RubyDesk::TeamRoom
|
|
12
16
|
# return the resulting array
|
13
17
|
team_rooms
|
14
18
|
end
|
15
|
-
|
19
|
+
|
16
20
|
end
|
17
|
-
|
18
|
-
# Attribute readers for all attributes
|
19
|
-
attr_reader :company_recno, :company_name, :name, :id, :recno, :teamroom_api
|
20
|
-
|
21
|
+
|
21
22
|
# Create a new TeamRoom from a hash similar to the one in ActiveRecord::Base.
|
22
23
|
# The given hash maps each attribute name to its value
|
23
24
|
def initialize(params={})
|
@@ -25,10 +26,16 @@ class RubyDesk::TeamRoom
|
|
25
26
|
self.instance_variable_set("@#{k}", v)
|
26
27
|
end
|
27
28
|
end
|
28
|
-
|
29
|
+
|
29
30
|
def snapshot(connector, online='now')
|
30
|
-
response = connector.prepare_and_invoke_api_call "team/v1/teamrooms/#{self.id}",
|
31
|
+
response = connector.prepare_and_invoke_api_call "team/v1/teamrooms/#{self.id}",
|
32
|
+
:params=>{:online=>online}, :method=>:get
|
31
33
|
json = JSON.parse(response)
|
32
34
|
RubyDesk::Snapshot.new(json['teamroom']['snapshot'])
|
33
35
|
end
|
34
|
-
|
36
|
+
|
37
|
+
def work_diary(connector, user_id, date = nil, timezone = "mine")
|
38
|
+
RubyDesk::Snapshot.workdiary(connector, self.id, user_id, date, timezone)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
class RubyDesk::TimeReport
|
5
|
+
DEFAULT_OPTIONS = {:select => "worked_on, provider_id, sum(hours)",
|
6
|
+
:conditions=>{} }
|
7
|
+
|
8
|
+
# The time reports API uses the Google Visualization API's query language to
|
9
|
+
# run detailed reports on a user's team, company, as well as provider
|
10
|
+
# specific detail.
|
11
|
+
# In order to use the reports API you need to understand the data model
|
12
|
+
# accessible as well as the supported Google query language elements.
|
13
|
+
# Please see the
|
14
|
+
# <a href="http://developers.odesk.com/oDesk-Google-Data-Source-Language">
|
15
|
+
# oDesk Google Data Source Language</a> page for a detailed description of
|
16
|
+
# what's available.
|
17
|
+
# For more details around how to construct data parameters see the
|
18
|
+
# <a href="http://code.google.com/apis/visualization/documentation/querylanguage.html">
|
19
|
+
# google documentation</a>.
|
20
|
+
#
|
21
|
+
# Allowed values for options are
|
22
|
+
# :select
|
23
|
+
# :condition
|
24
|
+
# Here is the limited support for the where clause.
|
25
|
+
# Fields
|
26
|
+
# * Name
|
27
|
+
# * company_id
|
28
|
+
# * agency_id
|
29
|
+
# * provider_id
|
30
|
+
# * worked_on
|
31
|
+
# * assignment_team_id
|
32
|
+
# * provider_id
|
33
|
+
# * task
|
34
|
+
# :order
|
35
|
+
def self.find(connector, options={})
|
36
|
+
options = DEFAULT_OPTIONS.merge(options)
|
37
|
+
call_url = "timereports/v1"
|
38
|
+
# Adjust a URL that has as much information as we can
|
39
|
+
if String === options[:conditions][:company_id]
|
40
|
+
# Limit to one company in url (better looking)
|
41
|
+
call_url << "/companies/#{options[:conditions].delete :company_id}"
|
42
|
+
|
43
|
+
if String === options[:conditions][:agency_id]
|
44
|
+
# Limit to one agency in url (better looking)
|
45
|
+
call_url << "/agencies/#{options[:conditions].delete :agency_id}"
|
46
|
+
elsif String === options[:conditions][:team_id]
|
47
|
+
# Limit to one team in url (better looking)
|
48
|
+
call_url << "/teams/#{options[:conditions].delete :team_id}"
|
49
|
+
end
|
50
|
+
elsif String === options[:conditions][:provider_id]
|
51
|
+
call_url << "/providers/#{options[:conditions].delete :provider_id}"
|
52
|
+
end
|
53
|
+
|
54
|
+
call_url << "/hours" if options[:hours]
|
55
|
+
|
56
|
+
gds_query = build_query(options)
|
57
|
+
response = connector.prepare_and_invoke_api_call(call_url, :method=>:get,
|
58
|
+
:base_url=>RubyDesk::Connector::ODESK_GDS_URL, :format=>nil,
|
59
|
+
:params=>{:tq=>URI.escape(gds_query," ,%&=./"), :tqx=>"out:json"})
|
60
|
+
|
61
|
+
json = JSON.parse(response)
|
62
|
+
columns = [json['table']['cols']].flatten
|
63
|
+
rows = [json['table']['rows']].flatten.map do |row_data|
|
64
|
+
row = {}
|
65
|
+
[row_data['c']].flatten.each_with_index do |row_element, element_index|
|
66
|
+
column = columns[element_index]
|
67
|
+
row[column['label']] = str_to_value(row_element['v'], column['type'])
|
68
|
+
end
|
69
|
+
row
|
70
|
+
end
|
71
|
+
return rows
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.build_query(options)
|
75
|
+
gds_query = ""
|
76
|
+
gds_query << "SELECT " << options[:select]
|
77
|
+
if options[:conditions]
|
78
|
+
gds_query << " WHERE "
|
79
|
+
combined = options[:conditions].map do |field, condition|
|
80
|
+
"(" +
|
81
|
+
case condition
|
82
|
+
when String, Numeric, Date, Time then
|
83
|
+
"#{field}=#{value_to_str(condition)}"
|
84
|
+
when Array then
|
85
|
+
condition.map{|v| "#{field}=#{value_to_str(v)}"}.join(" OR ")
|
86
|
+
when Range
|
87
|
+
"#{field}>=#{value_to_str(condition.first)} AND #{field}" +
|
88
|
+
(condition.exclude_end? ? "<" : "<=") +
|
89
|
+
value_to_str(condition.last)
|
90
|
+
end +
|
91
|
+
")"
|
92
|
+
end
|
93
|
+
gds_query << combined.join(" AND ")
|
94
|
+
end
|
95
|
+
if options[:order]
|
96
|
+
gds_query << " ORDER BY " << options[:order]
|
97
|
+
end
|
98
|
+
gds_query
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.value_to_str(value)
|
102
|
+
case value
|
103
|
+
when String then value
|
104
|
+
when Date, Time then "'"+value.strftime("%Y-%m-%d")+"'"
|
105
|
+
when Numeric then value.to_s
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.str_to_value(string, type)
|
110
|
+
case type
|
111
|
+
when 'number' then string.to_f
|
112
|
+
when 'date' then Date.strptime(string, "%Y%m%d")
|
113
|
+
when 'string' then string
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
end
|
119
|
+
|
data/lib/ruby_desk.rb
CHANGED
@@ -14,43 +14,49 @@ module RubyDesk
|
|
14
14
|
attributes *attribute_names
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
Competency = define_simple_class(:cmp_url, :cmp_when, :cmp_provider, :cmp_ref,
|
19
|
-
|
20
|
-
Skill = define_simple_class(:skl_level_num, :skl_ref, :skl_name,
|
21
|
-
|
22
|
-
Agency = define_simple_class(:ag_adj_score, :ag_logo, :ag_name,
|
23
|
-
|
19
|
+
:cmp_percentile, :cmp_name, :cmp_recno, :cmp_score, :cmp_id)
|
20
|
+
Skill = define_simple_class(:skl_level_num, :skl_ref, :skl_name,
|
21
|
+
:skl_last_used, :skl_recno, :skl_level, :skl_comment, :skl_year_exp)
|
22
|
+
Agency = define_simple_class(:ag_adj_score, :ag_logo, :ag_name,
|
23
|
+
:ag_recent_hours, :ag_tot_feedback, :ag_recno, :ciphertext,
|
24
|
+
:ag_adj_score_recent, :ag_total_hours)
|
24
25
|
Exam = define_simple_class(:ts_name, :ts_rank, :ts_duration, :ts_when,
|
25
|
-
|
26
|
-
|
26
|
+
:ts_provider, :ts_score, :ts_percentile, :ts_curl_name, :ts_ref,
|
27
|
+
:ts_is_certification, :ts_id, :ts_pass, :ts_seo_link)
|
27
28
|
JobCategory = define_simple_class(:seo_link, :second_level, :first_level)
|
28
29
|
Experience = define_simple_class(:exp_comment, :exp_from, :exp_recno,
|
29
|
-
|
30
|
+
:exp_title, :exp_company, :exp_to)
|
30
31
|
Candidacy = define_simple_class(:permalink, :dev_recno, :opening_ciphertext,
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
Assignment = define_simple_class(:as_type, :as_blended_cost_rate,
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
Certificate = define_simple_class(:cmp_url, :cmp_when, :cmp_provider,
|
40
|
-
|
32
|
+
:rel_requested_assignment, :created_type, :developer_ciphertext,
|
33
|
+
:ciphertext, :create_date, :profile_seo_link, :Agencyref,
|
34
|
+
:agency_ciphertext, :op_title, :record_id, :job_profile_access)
|
35
|
+
Assignment = define_simple_class(:as_type, :as_blended_cost_rate,
|
36
|
+
:as_total_hours, :as_to, :as_from, :as_client, :as_opening_access,
|
37
|
+
:as_agency_name, :as_rate, :as_blended_charge_rate, :as_amount,
|
38
|
+
:as_total_charge, :as_opening_recno, :as_description, :as_opening_title,
|
39
|
+
:as_status, :as_job_type, :as_ciphertext_opening_recno, :as_total_cost)
|
40
|
+
Certificate = define_simple_class(:cmp_url, :cmp_when, :cmp_provider,
|
41
|
+
:cmp_ref, :cmp_percentile, :cmp_name, :cmp_recno, :cmp_score, :cmp_id)
|
41
42
|
Institution = define_simple_class(:ed_area, :ed_school, :ed_degree, :ed_to,
|
42
|
-
|
43
|
-
OtherExperience = define_simple_class(:exp_recno, :exp_description,
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
43
|
+
:ed_recno, :ed_comment, :ed_from)
|
44
|
+
OtherExperience = define_simple_class(:exp_recno, :exp_description,
|
45
|
+
:exp_subject)
|
46
|
+
Trend = define_simple_class(:trend_word, :trend_url, :trend_recno,
|
47
|
+
:trend_skill)
|
48
|
+
PortfolioItem = define_simple_class(:pi_recno, :pi_description,
|
49
|
+
:pi_attachment, :pi_completed, :pi_thumbnail, :pi_title, :pi_category,
|
50
|
+
:pi_url, :pi_image)
|
51
|
+
User = define_simple_class(:messenger_id, :timezone, :uid, :timezone_offset,
|
52
|
+
:last_name, :mail, :creation_time, :first_name)
|
53
|
+
DeveloperSkill = define_simple_class(:order, :description,
|
54
|
+
:avg_category_score_recent, :avg_category_score, :label)
|
50
55
|
# = define_simple_class()
|
51
56
|
# = define_simple_class()
|
52
57
|
end
|
53
58
|
|
54
59
|
Dir.glob(File.join(File.dirname(__FILE__), 'ruby_desk', '*.rb')).each do |file|
|
55
60
|
require file
|
56
|
-
end
|
61
|
+
end
|
62
|
+
|
data/test/snapshot.json
ADDED
@@ -0,0 +1,2 @@
|
|
1
|
+
{"server_time":1263130514,"auth_user":{"first_name":"Ahmed El-Dawy","last_name":".","uid":"aseldawy","mail":"aseldawy@odesk.com","messenger_id":"aseldawy","messenger_type":"yahoo","timezone":"Africa\/Cairo","timezone_offset":"7200"},"snapshot":{"status":"PRIVATE","time":"1249344000","billing_status":"non-billed.disconnected","activity":"0","online_presence":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0","user":{"messenger_id":"aseldawy","timezone":"Africa\/Cairo","uid":"aseldawy","timezone_offset":"7200","last_name":".","messenger_type":"yahoo","mail":"aseldawy@odesk.com","creation_time":"1207503924","archiving_time":"1262563200","first_name":"Ahmed El-Dawy"},"mouse_events_count":"","company_id":"techunlimited","timezone":"Africa\/Cairo","uid":"","keyboard_events_count":"","last_worked":"1258037998","memo":"","active_window_title":"","report24_img":"http:\/\/chart.apis.google.com\/chart?chs=96x17&cht=ls&chco=cccccc&chf=bg,s,f8f8f8&chm=B,cccccc,0,0,0&chd=t:0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0","computer_name":"","online_presence_img":"http:\/\/chart.apis.google.com\/chart?chs=96x17&cht=ls&chco=cccccc&chf=bg,s,f8f8f8&chm=B,cccccc,0,0,0&chd=t:0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0","user_id":"aseldawy","client_version":"","teamroom_api":"\/api\/team\/v1\/teamrooms\/techunlimited.json","workdiary_api":"\/api\/team\/v1\/workdiaries\/techunlimited\/aseldawy\/20090804.json"}}
|
2
|
+
|
data/test/teamrooms.json
ADDED
@@ -0,0 +1,2 @@
|
|
1
|
+
{"server_time":"1263125223","auth_user":{"first_name":"Ahmed El-Dawy","last_name":".","uid":"aseldawy","mail":"aseldawy@odesk.com","messenger_id":"aseldawy","messenger_type":"yahoo","timezone":"Africa\/Cairo","timezone_offset":"7200"},"teamrooms":{"teamroom":[{"company_recno":"28705","company_name":"Tech Unlimited, Inc.","name":"Tech Unlimited, Inc.","id":"techunlimited","recno":"28705","teamroom_api":"\/api\/team\/v1\/teamrooms\/techunlimited.json"},{"company_recno":"81112","company_name":"Arabic Development Int.","name":"Phase One","id":"tejahcom","recno":"81112","teamroom_api":"\/api\/team\/v1\/teamrooms\/tejahcom.json"},{"company_recno":"16273","company_name":"Techniprise Inc.","name":"Techniprise Inc.","id":"techniprise","recno":"16273","teamroom_api":"\/api\/team\/v1\/teamrooms\/techniprise.json"},{"company_recno":"33618","company_name":"BadrIT","name":"BadrIT","id":"badrit","recno":"33618","teamroom_api":"\/api\/team\/v1\/teamrooms\/badrit.json"},{"company_recno":"51737","company_name":"Lian AlKhaleej","name":"Lian AlKhaleej","id":"lianalkhaleej:lianalkhaleej","recno":"51737","teamroom_api":"\/api\/team\/v1\/teamrooms\/lianalkhaleej:lianalkhaleej.json"}]},"teamroom":""}
|
2
|
+
|
data/test/test_ruby_desk.rb
CHANGED
@@ -1,14 +1,44 @@
|
|
1
1
|
require 'helper'
|
2
|
+
require 'date'
|
2
3
|
|
3
4
|
class TestRubyDesk < Test::Unit::TestCase
|
5
|
+
def test_sign
|
6
|
+
connector = RubyDesk::Connector.new('824d225a889aca186c55ac49a6b23957',
|
7
|
+
'984aa36db13fff5c')
|
8
|
+
assert_equal 'dc30357f7c6b62aadf99e5313ac93365',
|
9
|
+
connector.sign(:online=>'all', :tz=>'mine',
|
10
|
+
:api_key=>'824d225a889aca186c55ac49a6b23957',
|
11
|
+
:api_token=>'ffffffffffffffffffffffffffffffff')
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_api_call
|
15
|
+
connector = RubyDesk::Connector.new('824d225a889aca186c55ac49a6b23957',
|
16
|
+
'984aa36db13fff5c')
|
17
|
+
api_call = connector.prepare_api_call('auth/v1/keys/tokens',
|
18
|
+
:params=>{:frob=>@frob, :api_key=>@api_key},
|
19
|
+
:method=>:post, :format=>'json')
|
20
|
+
assert api_call[:url].index('.json')
|
21
|
+
assert api_call[:url].index('/api/auth/v1/keys/tokens')
|
22
|
+
assert api_call[:params][:api_sig]
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_gds_api_call
|
26
|
+
connector = RubyDesk::Connector.new('824d225a889aca186c55ac49a6b23957',
|
27
|
+
'984aa36db13fff5c')
|
28
|
+
api_call = connector.prepare_api_call('timereports/v1/companies/1',
|
29
|
+
:method=>:post, :base_url=>RubyDesk::Connector::ODESK_GDS_URL)
|
30
|
+
assert api_call[:url].index('/gds/timereports/v1/companies/1')
|
31
|
+
assert api_call[:params][:api_sig]
|
32
|
+
end
|
33
|
+
|
4
34
|
def test_provider_search
|
5
35
|
connector = Object.new
|
6
36
|
def connector.prepare_and_invoke_api_call(*args)
|
7
37
|
File.read(File.join(File.dirname(__FILE__), 'providers.json'))
|
8
38
|
end
|
9
|
-
|
39
|
+
|
10
40
|
providers = RubyDesk::Provider.search(connector, 'rails')
|
11
|
-
|
41
|
+
|
12
42
|
assert providers.is_a?(Array)
|
13
43
|
assert_equal 20, providers.size
|
14
44
|
assert_equal RubyDesk::Provider, providers.first.class
|
@@ -20,11 +50,82 @@ class TestRubyDesk < Test::Unit::TestCase
|
|
20
50
|
def connector.prepare_and_invoke_api_call(*args)
|
21
51
|
File.read(File.join(File.dirname(__FILE__), 'profile.json'))
|
22
52
|
end
|
23
|
-
|
53
|
+
|
24
54
|
profile = RubyDesk::Provider.get_profile(connector, 'aseldawy')
|
25
|
-
|
55
|
+
|
26
56
|
assert_equal RubyDesk::Provider, profile.class
|
27
57
|
assert_equal RubyDesk::Skill, profile.skills.first.class
|
28
58
|
assert_equal RubyDesk::Competency, profile.competencies.first.class
|
29
59
|
end
|
60
|
+
|
61
|
+
def test_team_rooms
|
62
|
+
connector = Object.new
|
63
|
+
def connector.prepare_and_invoke_api_call(*args)
|
64
|
+
File.read(File.join(File.dirname(__FILE__), 'teamrooms.json'))
|
65
|
+
end
|
66
|
+
|
67
|
+
teamrooms = RubyDesk::TeamRoom.get_teamrooms(connector)
|
68
|
+
|
69
|
+
assert_equal 5, teamrooms.size
|
70
|
+
assert_equal RubyDesk::TeamRoom, teamrooms.first.class
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_workdiary
|
74
|
+
connector = Object.new
|
75
|
+
def connector.prepare_and_invoke_api_call(*args)
|
76
|
+
File.read(File.join(File.dirname(__FILE__), 'workdiary.json'))
|
77
|
+
end
|
78
|
+
|
79
|
+
snapshots = RubyDesk::Snapshot.work_diary(connector, 'techunlimited',
|
80
|
+
'aseldawy')
|
81
|
+
assert_equal 14, snapshots.size
|
82
|
+
assert_equal RubyDesk::Snapshot, snapshots.first.class
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_snapshot
|
86
|
+
connector = Object.new
|
87
|
+
def connector.prepare_and_invoke_api_call(*args)
|
88
|
+
File.read(File.join(File.dirname(__FILE__), 'snapshot.json'))
|
89
|
+
end
|
90
|
+
|
91
|
+
snapshots = RubyDesk::Snapshot.snapshot_details(connector, 'techunlimited',
|
92
|
+
'aseldawy', Time.now)
|
93
|
+
assert_equal 1, snapshots.size
|
94
|
+
assert_equal RubyDesk::Snapshot, snapshots.first.class
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_query_builder
|
98
|
+
test_data = [
|
99
|
+
[{:select=>"worked_on", :conditions=>{:worked_on=>Time.mktime(2009, 10, 10)}},
|
100
|
+
"SELECT worked_on WHERE (worked_on='2009-10-10')"],
|
101
|
+
[{:select=>"worked_on, provider_id", :conditions=>{:provider_id=>[1,2,3]}},
|
102
|
+
"SELECT worked_on, provider_id WHERE (provider_id=1 OR provider_id=2 OR provider_id=3)"],
|
103
|
+
[{:select=>"worked_on", :conditions=>{:provider_id=>1, :agency_id=>3}},
|
104
|
+
["SELECT worked_on WHERE (agency_id=3) AND (provider_id=1)",
|
105
|
+
"SELECT worked_on WHERE (provider_id=1) AND (agency_id=3)"]],
|
106
|
+
[{:select=>"worked_on", :conditions=>{:provider_id=>1..3}},
|
107
|
+
"SELECT worked_on WHERE (provider_id>=1 AND provider_id<=3)"],
|
108
|
+
[{:select=>"worked_on", :conditions=>{:provider_id=>1...3}},
|
109
|
+
"SELECT worked_on WHERE (provider_id>=1 AND provider_id<3)"],
|
110
|
+
]
|
111
|
+
test_data.each do |options, query|
|
112
|
+
assert query.include?(RubyDesk::TimeReport.build_query(options))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_timreport
|
117
|
+
connector = Object.new
|
118
|
+
def connector.prepare_and_invoke_api_call(*args)
|
119
|
+
File.read(File.join(File.dirname(__FILE__), 'timereport.json'))
|
120
|
+
end
|
121
|
+
|
122
|
+
timereport = RubyDesk::TimeReport.find(connector,
|
123
|
+
:select=>"worked_on, sum(hours), provider_id")
|
124
|
+
assert_equal 15, timereport.size
|
125
|
+
assert_equal Hash, timereport.first.class
|
126
|
+
assert_equal Date, timereport.first['worked_on'].class
|
127
|
+
assert timereport.first['hours'].is_a?(Numeric)
|
128
|
+
end
|
129
|
+
|
30
130
|
end
|
131
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
{"server_time":"1263311702","auth_user":{"first_name":"Ahmed El-Dawy","last_name":".","uid":"aseldawy","mail":"aseldawy@odesk.com","messenger_id":"aseldawy","messenger_type":"yahoo","timezone":"Africa\/Cairo","timezone_offset":"7200"},"table":{"cols":[{"type":"date","label":"worked_on"},{"type":"number","label":"hours"},{"type":"string","label":"provider_id"}],"rows":[{"c":[{"v":"20080528"},{"v":"0.83"},{"v":"aseldawy"}]},{"c":[{"v":"20080526"},{"v":"4.50"},{"v":"mostafaali"}]},{"c":[{"v":"20080526"},{"v":"3.5"},{"v":"melkharashy"}]},{"c":[{"v":"20080529"},{"v":"2.83"},{"v":"adewandaru"}]},{"c":[{"v":"20080531"},{"v":"1.5"},{"v":"adewandaru"}]},{"c":[{"v":"20080525"},{"v":"5.00"},{"v":"melkharashy"}]},{"c":[{"v":"20080528"},{"v":"3.00"},{"v":"adewandaru"}]},{"c":[{"v":"20080527"},{"v":"2.0"},{"v":"adewandaru"}]},{"c":[{"v":"20080525"},{"v":"2.67"},{"v":"adewandaru"}]},{"c":[{"v":"20080528"},{"v":"3.67"},{"v":"melkharashy"}]},{"c":[{"v":"20080530"},{"v":"5.5"},{"v":"adewandaru"}]},{"c":[{"v":"20080531"},{"v":"3.67"},{"v":"melkharashy"}]},{"c":[{"v":"20080527"},{"v":"2.17"},{"v":"melkharashy"}]},{"c":[{"v":"20080526"},{"v":"8.01"},{"v":"adewandaru"}]},{"c":[{"v":"20080529"},{"v":"0.67"},{"v":"melkharashy"}]}]}}
|
data/test/workdiary.json
ADDED
@@ -0,0 +1,2 @@
|
|
1
|
+
{"server_time":"1263126093","auth_user":{"first_name":"Ahmed El-Dawy","last_name":".","uid":"aseldawy","mail":"aseldawy@odesk.com","messenger_id":"aseldawy","messenger_type":"yahoo","timezone":"Africa\/Cairo","timezone_offset":"7200"},"snapshots":{"user":{"timezone":"Africa\/Cairo","uid":"aseldawy","timezone_offset":"7200","last_name":".","mail":"aseldawy@odesk.com","creation_time":"1207503924","archiving_time":"1262563200","first_name":"Ahmed El-Dawy"},"snapshot":[{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249372200","keyboard_events_count":"418","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249372678","billing_status":"billed.active","active_window_title":"sadfas","memo":"do nothing","activity":"5","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249372678.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249378200","keyboard_events_count":"431","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249378442","billing_status":"billed.active","active_window_title":"RadRails - Aptana Studio Community Edition ","memo":"do nothing","activity":"2","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249378442.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249378800","keyboard_events_count":"1018","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249379235","billing_status":"billed.active","active_window_title":"Google - Mozilla Firefox","memo":"do nothing","activity":"3","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249379235.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249379400","keyboard_events_count":"1015","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249379749","billing_status":"billed.active","active_window_title":"RadRails - 20090804093424_add_license_quota.rb - Aptana Studio Community Edition ","memo":"do nothing","activity":"7","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249379749.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249380000","keyboard_events_count":"665","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249380394","billing_status":"billed.active","active_window_title":"RadRails - page.rb - Aptana Studio Community Edition ","memo":"do nothing","activity":"7","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249380394.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249382400","keyboard_events_count":"691","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249382513","billing_status":"billed.active","active_window_title":"RadRails - page_test.rb - Aptana Studio Community Edition ","memo":"do nothing","activity":"1","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249382513.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249384200","keyboard_events_count":"536","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249384799","billing_status":"billed.active","active_window_title":"RadRails - page.rb - Aptana Studio Community Edition ","memo":"do nothing","activity":"5","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249384799.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249384800","keyboard_events_count":"1021","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249385243","billing_status":"billed.active","active_window_title":"aseldawy@aseldawy-laptop: ~\/Aptana_Studio\/planswift_backend","memo":"do nothing","activity":"10","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249385243.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249385400","keyboard_events_count":"380","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249385999","billing_status":"billed.active","active_window_title":"RadRails - fixtures.rb - Aptana Studio Community Edition ","memo":"do nothing","activity":"9","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249385999.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249386000","keyboard_events_count":"544","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249386477","billing_status":"billed.active","active_window_title":"Mohamed Amir","memo":"do nothing","activity":"8","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249386477.json"},{"mouse_events_count":"1","company_id":"bebebobo","cell_time":"1249386600","keyboard_events_count":"348","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249387084","billing_status":"billed.active","active_window_title":"RadRails - fixtures.rb - Aptana Studio Community Edition ","memo":"do nothing","activity":"7","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249387084.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249387200","keyboard_events_count":"598","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249387654","billing_status":"billed.active","active_window_title":"aseldawy@aseldawy-laptop: ~\/Aptana_Studio\/planswift_backend","memo":"do nothing","activity":"7","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249387654.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249387800","keyboard_events_count":"950","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249388254","billing_status":"billed.active","active_window_title":"Commit ","memo":"do nothing","activity":"10","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249388254.json"},{"mouse_events_count":"0","company_id":"bebebobo","cell_time":"1249388400","keyboard_events_count":"873","uid":"428268b0-45b1-4a54-83e0-4ec381b39cc0","timezone":"","status":"NORMAL","time":"1249388796","billing_status":"billed.active","active_window_title":"root@domU-12-31-38-00-3C-E3: \/var\/www\/planswift","memo":"do nothing","activity":"7","computer_name":"some computer","user_id":"aseldawy","client_version":"Linux\/1.3.4","snapshot_api":"\/api\/team\/v1\/snapshots\/bebebobo\/aseldawy\/1249388796.json"}],"teamroom_api":"\/api\/team\/v1\/teamrooms\/bebebobo.json"}}
|
2
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_desk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ahmed ElDawy
|
@@ -38,15 +38,19 @@ files:
|
|
38
38
|
- VERSION
|
39
39
|
- lib/ruby_desk.rb
|
40
40
|
- lib/ruby_desk/connector.rb
|
41
|
-
- lib/ruby_desk/developer_skill.rb
|
42
41
|
- lib/ruby_desk/odesk_entity.rb
|
43
42
|
- lib/ruby_desk/provider.rb
|
44
43
|
- lib/ruby_desk/snapshot.rb
|
45
44
|
- lib/ruby_desk/team_room.rb
|
45
|
+
- lib/ruby_desk/time_report.rb
|
46
46
|
- test/helper.rb
|
47
47
|
- test/profile.json
|
48
48
|
- test/providers.json
|
49
|
+
- test/snapshot.json
|
50
|
+
- test/teamrooms.json
|
49
51
|
- test/test_ruby_desk.rb
|
52
|
+
- test/timereport.json
|
53
|
+
- test/workdiary.json
|
50
54
|
has_rdoc: true
|
51
55
|
homepage: http://github.com/aseldawy/ruby_desk
|
52
56
|
licenses: []
|