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 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
- Gets the URL that will ask the user to authenticate your application
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.2.0
1
+ 0.3.0
@@ -5,12 +5,15 @@ require 'net/https'
5
5
 
6
6
  module RubyDesk
7
7
  class Connector
8
- ODESK_API_URL = "www.odesk.com/api/"
9
- ODESK_AUTH_URL = "www.odesk.com/services/api/auth/"
10
- DEFAULT_OPTIONS = {:secure=>true, :sign=>true, :format=>'xml'}
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
- md5 = Digest::MD5.hexdigest(concatenated)
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
- def prepare_api_call(path, params = {}, options = {})
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[:api_sig] = sign(params) if options[:sign]
47
- {:url=>"http#{options[:secure]? 's' : ''}://" + ODESK_API_URL + path + "." + options[:format],
48
- :params=> params, :method=>options[:method]}
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, params = {}, options = {})
76
- api_call = prepare_api_call(path, params, options)
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
- params = {:api_key=>@api_key}
83
- params[:api_sig] = sign(params)
84
- "https://"+ODESK_AUTH_URL+"?"+params.to_a.map{|pair| pair.join '='}.join('&')
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
- "https://"+ODESK_AUTH_URL
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', {:frob=>@frob, :api_key=>@api_key}, :method=>:post, :format=>'json'
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', {:api_key=>@api_key}, :method=>:post, :format=>'json'
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', {:api_key=>@api_key, :api_token=>@api_token}, :method=>:get, :format=>'json'
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', {:api_key=>@api_key, :api_token=>@api_token}, :method=>:delete, :format=>'json'
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
+
@@ -1,91 +1,7 @@
1
1
  class RubyDesk::Provider < RubyDesk::OdeskEntity
2
2
  class << self
3
3
  def all_categories
4
- text = <<-CATEGORIES
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 'profiles/v1/search/providers', {:api_token=>@api_token, :api_key=>@api_key}, :method=>:get, :format=>'json'
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("profiles/v1/providers/#{id}" + (brief ? "/brief" : ""),
174
- {:api_token=>@api_token, :api_key=>@api_key}, :method=>:get, :format=>'json')
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
+
@@ -8,4 +8,38 @@ class RubyDesk::Snapshot < RubyDesk::OdeskEntity
8
8
 
9
9
  attribute :user, :class=>RubyDesk::User
10
10
 
11
- end
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
+
@@ -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', {:api_token=>@api_token, :api_key=>@api_key}, :method=>:get, :format=>'json'
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}", {:api_token=>@api_token, :api_key=>@api_key, :online=>online}, :method=>:get, :format=>'json'
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
- end
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
- :cmp_percentile, :cmp_name, :cmp_recno, :cmp_score, :cmp_id)
20
- Skill = define_simple_class(:skl_level_num, :skl_ref, :skl_name, :skl_last_used,
21
- :skl_recno, :skl_level, :skl_comment, :skl_year_exp)
22
- Agency = define_simple_class(:ag_adj_score, :ag_logo, :ag_name, :ag_recent_hours, :ag_tot_feedback,
23
- :ag_recno, :ciphertext, :ag_adj_score_recent, :ag_total_hours)
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
- :ts_provider, :ts_score, :ts_percentile, :ts_curl_name, :ts_ref,
26
- :ts_is_certification, :ts_id, :ts_pass, :ts_seo_link)
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
- :exp_title, :exp_company, :exp_to)
30
+ :exp_title, :exp_company, :exp_to)
30
31
  Candidacy = define_simple_class(:permalink, :dev_recno, :opening_ciphertext,
31
- :rel_requested_assignment, :created_type, :developer_ciphertext,
32
- :ciphertext, :create_date, :profile_seo_link, :Agencyref, :agency_ciphertext,
33
- :op_title, :record_id, :job_profile_access)
34
- Assignment = define_simple_class(:as_type, :as_blended_cost_rate, :as_total_hours,
35
- :as_to, :as_from, :as_client, :as_opening_access, :as_agency_name, :as_rate,
36
- :as_blended_charge_rate, :as_amount, :as_total_charge, :as_opening_recno,
37
- :as_description, :as_opening_title, :as_status, :as_job_type,
38
- :as_ciphertext_opening_recno, :as_total_cost)
39
- Certificate = define_simple_class(:cmp_url, :cmp_when, :cmp_provider, :cmp_ref,
40
- :cmp_percentile, :cmp_name, :cmp_recno, :cmp_score, :cmp_id)
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
- :ed_recno, :ed_comment, :ed_from)
43
- OtherExperience = define_simple_class(:exp_recno, :exp_description, :exp_subject)
44
- Trend = define_simple_class(:trend_word, :trend_url, :trend_recno, :trend_skill)
45
- PortfolioItem = define_simple_class(:pi_recno, :pi_description, :pi_attachment,
46
- :pi_completed, :pi_thumbnail, :pi_title, :pi_category, :pi_url, :pi_image)
47
- User = define_simple_class(:messenger_id, :timezone, :uid, :timezone_offset, :last_name,
48
- :mail, :creation_time, :first_name)
49
- # = define_simple_class()
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
+
@@ -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
+
@@ -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
+
@@ -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"}]}]}}
@@ -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.2.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: []
@@ -1,3 +0,0 @@
1
- class RubyDesk::DeveloperSkill < RubyDesk::OdeskEntity
2
- attributes :order, :description, :avg_category_score_recent, :avg_category_score, :label
3
- end