job_dollars 1.0.5

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 93c5f216180ff72d2df380c54fe76c74c39514cb
4
+ data.tar.gz: 2f2ca2f47c34ba02c58eba37959285ef8b8e92c0
5
+ SHA512:
6
+ metadata.gz: 9945ef0043844f89ec810a371e4e746723259656746487d37bf78ce9dcd0d0aacf67630f6c5d30b5bd3e9f653c2297cb897ff6d5fb67085aacb41da4143bcf8e
7
+ data.tar.gz: 40662a9fa558a8aae8907f9986e55cc46c37d6a522e32985a751d70480792ff56aef7283f17265cc0dfb605b7193d042a8848b173b5e77ec7ed7ae8d0437654c
@@ -0,0 +1,30 @@
1
+ require File.join(File.dirname(__FILE__), './job_dollars/core_ext/hash.rb')
2
+ require File.join(File.dirname(__FILE__), './job_dollars/core_ext/hash_with_indifferent_access.rb')
3
+ require File.join(File.dirname(__FILE__), './job_dollars/estimator.rb')
4
+
5
+ require 'yaml'
6
+
7
+ # JobDollars is the main module to include to start using JobDollars. It automatically
8
+ # includes the built-in Estimator module, along with some constants.
9
+ #
10
+ # Example (IRB):
11
+ #
12
+ # irb(main):001:0> require 'job_dollars'
13
+ # => true
14
+ # irb(main):002:0> include JobDollars
15
+ # => Object
16
+ # irb(main):006:0> beginner_javagrad_rating(JOB_DATA['java-android'])
17
+ # => 17.541885876163494
18
+ module JobDollars
19
+ DATA_YML_FILE = File.join(File.dirname(__FILE__), './job_dollars.yml')
20
+
21
+ extend Estimator
22
+
23
+ def self.included(base)
24
+ base.include Estimator
25
+ end
26
+
27
+ JOB_DATA = YAML.load(File.read(DATA_YML_FILE))
28
+ JAVAGRAD_IN_JOB_DOLLARS = market_work_in_job_dollars(0.0, JOB_DATA["java-swing"])
29
+ end
30
+
@@ -0,0 +1,329 @@
1
+ --- !map:HashWithIndifferentAccess
2
+
3
+ csharp-dotnet:
4
+ salaries:
5
+ entry: >
6
+ $65,000+ (2686)
7
+ $75,000+ (2243)
8
+ $80,000+ (1913)
9
+ $90,000+ (1218)
10
+ $100,000+ (637)
11
+
12
+ mid: >
13
+ $85,000+ (1823)
14
+ $95,000+ (1544)
15
+ $105,000+ (1122)
16
+ $110,000+ (906)
17
+ $120,000+ (477)
18
+
19
+ senior: >
20
+ $100,000+ (610)
21
+ $110,000+ (482)
22
+ $115,000+ (363)
23
+ $120,000+ (280)
24
+ $130,000+ (142)
25
+
26
+ jobs:
27
+ entry: 3137
28
+ mid: 11974
29
+ senior: 3422
30
+
31
+ resumes: 128_839
32
+
33
+ ruby-rails:
34
+ salaries:
35
+ entry: >
36
+ $75,000+ (777)
37
+ $90,000+ (634)
38
+ $100,000+ (500)
39
+ $105,000+ (383)
40
+ $115,000+ (198)
41
+
42
+ mid: >
43
+ $85,000+ (1823)
44
+ $95,000+ (1544)
45
+ $105,000+ (1122)
46
+ $110,000+ (906)
47
+ $120,000+ (477)
48
+
49
+ senior: >
50
+ $100,000+ (610)
51
+ $110,000+ (482)
52
+ $115,000+ (363)
53
+ $120,000+ (280)
54
+ $130,000+ (142)
55
+
56
+ jobs:
57
+ entry: 908
58
+ mid: 2088
59
+ senior: 718
60
+
61
+ resumes: 20_269
62
+
63
+ javascript-redux:
64
+ salaries:
65
+ entry: >
66
+ $80,000+ (64)
67
+ $95,000+ (48)
68
+ $100,000+ (38)
69
+ $105,000+ (27)
70
+ $110,000+ (21)
71
+
72
+ mid: >
73
+ $90,000+ (127)
74
+ $100,000+ (106)
75
+ $105,000+ (83)
76
+ $110,000+ (64)
77
+ $120,000+ (29)
78
+
79
+ senior: >
80
+ $100,000+ (43)
81
+ $105,000+ (38)
82
+ $110,000+ (28)
83
+ $115,000+ (22)
84
+ $130,000+ (8)
85
+
86
+ jobs:
87
+ entry: 71
88
+ mid: 144
89
+ senior: 47
90
+
91
+ resumes: 448
92
+
93
+ java-swing:
94
+ salaries:
95
+ entry: >
96
+ $70,000+ (61)
97
+ $75,000+ (56)
98
+ $80,000+ (43)
99
+ $90,000+ (29)
100
+ $105,000+ (12)
101
+
102
+ mid: >
103
+ $85,000+ (249)
104
+ $90,000+ (223)
105
+ $100,000+ (148)
106
+ $105,000+ (117)
107
+ $115,000+ (50)
108
+
109
+ senior: >
110
+ $85,000+ (126)
111
+ $95,000+ (99)
112
+ $105,000+ (73)
113
+ $110,000+ (57)
114
+ $120,000+ (27)
115
+
116
+ jobs:
117
+ entry: 71
118
+ mid: 292
119
+ senior: 136
120
+
121
+ resumes: 23_648
122
+
123
+ java-android:
124
+ salaries:
125
+ entry: >
126
+ $70,000+ (977)
127
+ $85,000+ (785)
128
+ $95,000+ (613)
129
+ $105,000+ (398)
130
+ $115,000+ (235)
131
+
132
+ mid: >
133
+ $85,000+ (2461)
134
+ $95,000+ (2032)
135
+ $105,000+ (1451)
136
+ $110,000+ (1176)
137
+ $120,000+ (646)
138
+
139
+ senior: >
140
+ $90,000+ (896)
141
+ $100,000+ (758)
142
+ $110,000+ (563)
143
+ $120,000+ (343)
144
+ $130,000+ (181)
145
+
146
+ jobs:
147
+ entry: 1_142
148
+ mid: 2_896
149
+ senior: 1_002
150
+
151
+ resumes: 65_373
152
+
153
+ java-j2ee:
154
+ salaries:
155
+ entry: >
156
+ $70,000+ (764)
157
+ $75,000+ (688)
158
+ $85,000+ (487)
159
+ $95,000+ (322)
160
+ $105,000+ (176)
161
+
162
+ mid: >
163
+ $80,000+ (3173)
164
+ $90,000+ (2557)
165
+ $95,000+ (2142)
166
+ $100,000+ (1697)
167
+ $110,000+ (845)
168
+
169
+ senior: >
170
+ $90,000+ (1805)
171
+ $100,000+ (1412)
172
+ $105,000+ (1166)
173
+ $110,000+ (935)
174
+ $120,000+ (510)
175
+
176
+ jobs:
177
+ entry: 910
178
+ mid: 3_692
179
+ senior: 2_087
180
+
181
+ resumes: 92_525
182
+
183
+ swift-ios:
184
+ salaries:
185
+ entry: >
186
+ $65,000+ (403)
187
+ $85,000+ (340)
188
+ $95,000+ (282)
189
+ $105,000+ (196)
190
+ $115,000+ (102)
191
+
192
+ mid: >
193
+ $85,000+ (837)
194
+ $95,000+ (699)
195
+ $105,000+ (502)
196
+ $110,000+ (417)
197
+ $120,000+ (218)
198
+
199
+ senior: >
200
+ $95,000+ (235)
201
+ $105,000+ (189)
202
+ $110,000+ (159)
203
+ $120,000+ (98)
204
+ $130,000+ (47)
205
+
206
+ jobs:
207
+ entry: 471
208
+ mid: 984
209
+ senior: 267
210
+
211
+ resumes: 6_169
212
+
213
+ javascript-jquery:
214
+ salaries:
215
+ entry: >
216
+ $60,000+ (2249)
217
+ $75,000+ (1821)
218
+ $85,000+ (1404)
219
+ $95,000+ (903)
220
+ $105,000+ (453)
221
+
222
+ mid: >
223
+ $75,000+ (8387)
224
+ $85,000+ (6890)
225
+ $90,000+ (5850)
226
+ $100,000+ (3551)
227
+ $110,000+ (1685)
228
+
229
+ senior: >
230
+ $90,000+ (2352)
231
+ $95,000+ (2049)
232
+ $100,000+ (1701)
233
+ $105,000+ (1257)
234
+ $115,000+ (604)
235
+
236
+ jobs:
237
+ entry: 2_571
238
+ mid: 9_641
239
+ senior: 2_803
240
+
241
+ resumes: 123_148
242
+
243
+ php:
244
+ salaries:
245
+ entry: >
246
+ $45,000+ (4548)
247
+ $60,000+ (3761)
248
+ $75,000+ (2779)
249
+ $85,000+ (2058)
250
+ $100,000+ (1100)
251
+
252
+ mid: >
253
+ $65,000+ (9836)
254
+ $80,000+ (7749)
255
+ $90,000+ (5693)
256
+ $95,000+ (4595)
257
+ $105,000+ (2631)
258
+
259
+ senior: >
260
+ $85,000+ (2511)
261
+ $95,000+ (2116)
262
+ $105,000+ (1478)
263
+ $110,000+ (1171)
264
+ $120,000+ (629)
265
+
266
+ jobs:
267
+ entry: 5_458
268
+ mid: 11_397
269
+ senior: 2_892
270
+
271
+ resumes: 184_970
272
+
273
+ javascript-react:
274
+ salaries:
275
+ entry:
276
+ $80,000+ (622)
277
+ $90,000+ (531)
278
+ $100,000+ (380)
279
+ $110,000+ (249)
280
+ $115,000+ (174)
281
+
282
+ mid:
283
+ $90,000+ (1832)
284
+ $95,000+ (1601)
285
+ $100,000+ (1370)
286
+ $110,000+ (825)
287
+ $120,000+ (426)
288
+
289
+ senior:
290
+ $95,000+ (658)
291
+ $105,000+ (505)
292
+ $110,000+ (426)
293
+ $115,000+ (323)
294
+ $125,000+ (158)
295
+ jobs:
296
+ entry: 723
297
+ mid: 2_192
298
+ senior: 727
299
+ resumes: 4_409
300
+
301
+ javascript-angular:
302
+ salaries:
303
+ entry:
304
+ $80,000+ (1233)
305
+ $90,000+ (1029)
306
+ $95,000+ (848)
307
+ $105,000+ (502)
308
+ $115,000+ (275)
309
+
310
+ mid:
311
+ $85,000+ (4146)
312
+ $95,000+ (3285)
313
+ $100,000+ (2704)
314
+ $105,000+ (2049)
315
+ $115,000+ (1074)
316
+
317
+ senior:
318
+ $95,000+ (1435)
319
+ $100,000+ (1269)
320
+ $105,000+ (1010)
321
+ $115,000+ (568)
322
+ $125,000+ (289)
323
+
324
+ jobs:
325
+ entry: 1_453
326
+ mid: 4_725
327
+ senior: 1_641
328
+
329
+ resumes: 27_324
@@ -0,0 +1,22 @@
1
+ class Array
2
+ # File activesupport/lib/active_support/core_ext/array/grouping.rb, line 20
3
+ def in_groups_of(number, fill_with = nil)
4
+ if number.to_i <= 0
5
+ raise ArgumentError,
6
+ "Group size must be a positive integer, was #{number.inspect}"
7
+ end
8
+
9
+ if fill_with == false
10
+ collection = self
11
+ else
12
+ padding = (number - size % number) % number
13
+ collection = dup.concat(Array.new(padding, fill_with))
14
+ end
15
+
16
+ if block_given?
17
+ collection.each_slice(number) { |slice| yield(slice) }
18
+ else
19
+ collection.each_slice(number).to_a
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+
2
+ class Hash
3
+
4
+ def with_indifferent_access
5
+ ActiveSupport::HashWithIndifferentAccess.new(self)
6
+ end
7
+
8
+ alias nested_under_indifferent_access with_indifferent_access
9
+ end
@@ -0,0 +1,174 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+ require 'active_support/core_ext/hash/reverse_merge'
3
+
4
+ module ActiveSupport
5
+ class HashWithIndifferentAccess < Hash
6
+ def extractable_options?
7
+ true
8
+ end
9
+
10
+ def with_indifferent_access
11
+ dup
12
+ end
13
+
14
+ def nested_under_indifferent_access
15
+ self
16
+ end
17
+
18
+ def initialize(constructor = {})
19
+ if constructor.respond_to?(:to_hash)
20
+ super()
21
+ update(constructor)
22
+ else
23
+ super(constructor)
24
+ end
25
+ end
26
+
27
+ def default(key = nil)
28
+ if key.is_a?(Symbol) && include?(key = key.to_s)
29
+ self[key]
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ def self.new_from_hash_copying_default(hash)
36
+ hash = hash.to_hash
37
+ new(hash).tap do |new_hash|
38
+ new_hash.default = hash.default
39
+ new_hash.default_proc = hash.default_proc if hash.default_proc
40
+ end
41
+ end
42
+
43
+ def self.[](*args)
44
+ new.merge!(Hash[*args])
45
+ end
46
+
47
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
48
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
49
+
50
+ def []=(key, value)
51
+ regular_writer(convert_key(key), convert_value(value, for: :assignment))
52
+ end
53
+
54
+ alias_method :store, :[]=
55
+
56
+ def update(other_hash)
57
+ if other_hash.is_a? HashWithIndifferentAccess
58
+ super(other_hash)
59
+ else
60
+ other_hash.to_hash.each_pair do |key, value|
61
+ if block_given? && key?(key)
62
+ value = yield(convert_key(key), self[key], value)
63
+ end
64
+ regular_writer(convert_key(key), convert_value(value))
65
+ end
66
+ self
67
+ end
68
+ end
69
+
70
+ alias_method :merge!, :update
71
+
72
+ def key?(key)
73
+ super(convert_key(key))
74
+ end
75
+
76
+ alias_method :include?, :key?
77
+ alias_method :has_key?, :key?
78
+ alias_method :member?, :key?
79
+
80
+ def fetch(key, *extras)
81
+ super(convert_key(key), *extras)
82
+ end
83
+
84
+ def values_at(*indices)
85
+ indices.collect { |key| self[convert_key(key)] }
86
+ end
87
+
88
+ def dup
89
+ self.class.new(self).tap do |new_hash|
90
+ set_defaults(new_hash)
91
+ end
92
+ end
93
+
94
+ def merge(hash, &block)
95
+ self.dup.update(hash, &block)
96
+ end
97
+
98
+ def reverse_merge(other_hash)
99
+ super(self.class.new_from_hash_copying_default(other_hash))
100
+ end
101
+
102
+ def reverse_merge!(other_hash)
103
+ replace(reverse_merge( other_hash ))
104
+ end
105
+
106
+ def replace(other_hash)
107
+ super(self.class.new_from_hash_copying_default(other_hash))
108
+ end
109
+
110
+ def delete(key)
111
+ super(convert_key(key))
112
+ end
113
+
114
+ def stringify_keys!; self end
115
+ def deep_stringify_keys!; self end
116
+ def stringify_keys; dup end
117
+ def deep_stringify_keys; dup end
118
+ undef :symbolize_keys!
119
+ undef :deep_symbolize_keys!
120
+ def symbolize_keys; to_hash.symbolize_keys! end
121
+ def deep_symbolize_keys; to_hash.deep_symbolize_keys! end
122
+ def to_options!; self end
123
+
124
+ def select(*args, &block)
125
+ dup.tap { |hash| hash.select!(*args, &block) }
126
+ end
127
+
128
+ def reject(*args, &block)
129
+ dup.tap { |hash| hash.reject!(*args, &block) }
130
+ end
131
+
132
+ def to_hash
133
+ _new_hash = Hash.new
134
+ set_defaults(_new_hash)
135
+
136
+ each do |key, value|
137
+ _new_hash[key] = convert_value(value, for: :to_hash)
138
+ end
139
+ _new_hash
140
+ end
141
+
142
+ protected
143
+ def convert_key(key)
144
+ key.kind_of?(Symbol) ? key.to_s : key
145
+ end
146
+
147
+ def convert_value(value, options = {})
148
+ if value.is_a? Hash
149
+ if options[:for] == :to_hash
150
+ value.to_hash
151
+ else
152
+ value.nested_under_indifferent_access
153
+ end
154
+ elsif value.is_a?(Array)
155
+ if options[:for] != :assignment || value.frozen?
156
+ value = value.dup
157
+ end
158
+ value.map! { |e| convert_value(e, options) }
159
+ else
160
+ value
161
+ end
162
+ end
163
+
164
+ def set_defaults(target)
165
+ if default_proc
166
+ target.default_proc = default_proc.dup
167
+ else
168
+ target.default = default
169
+ end
170
+ end
171
+ end
172
+ end
173
+ HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
174
+
@@ -0,0 +1,184 @@
1
+ require File.join(File.dirname(__FILE__), './core_ext/array.rb')
2
+
3
+ module JobDollars
4
+ # This module contains the estimation code.
5
+ module Estimator
6
+ # Estimate value of a developer resume item.
7
+ #
8
+ # @param y (Fixnum) number of years
9
+ # @param hash (HashWithIndifferentAccess) job data for the chosen skill,
10
+ # shaped like the top-level values in langval.yml
11
+ # @return (Float) Value of resume item in dollars.
12
+ def value_of_resume_item_in_dollars(y, hash)
13
+ resu = hash[:resumes]
14
+ sala = crunch_salaries(hash)
15
+
16
+ chance = {
17
+ entry: chance_of_getting_job(y, resu, :entry),
18
+ mid: chance_of_getting_job(y, resu, :mid),
19
+ senior: chance_of_getting_job(y, resu, :senior)
20
+ }
21
+
22
+ ((chance[:entry] * sala[:entry]) +
23
+ (chance[:mid] * sala[:mid]) +
24
+ (chance[:senior] * sala[:senior])) / 3.0
25
+ end
26
+
27
+ # Estimate the work the job market does for a candidate with a certain
28
+ # number of years of experience in a given skill.
29
+ #
30
+ # @param y (Fixnum) number of years
31
+ # @param hash (HashWithIndifferentAccess) job data for the chosen skill,
32
+ # shaped like the top-level values in langval.yml
33
+ # @return (Float) Market work estimate in job-dollars.
34
+ def market_work_in_job_dollars(y, hash)
35
+ candidate_type = case
36
+ when y >= 5
37
+ :senior
38
+ when y >= 3 && y < 5
39
+ :mid
40
+ when y < 3
41
+ :entry
42
+ end
43
+
44
+ value_of_resume_item_in_dollars(y, hash) * hash[:jobs][candidate_type]
45
+ end
46
+
47
+ # Estimate the work the job market does for a candidate with a certain
48
+ # number of years of experience in a given skill, in Javagrads.
49
+ #
50
+ # @param y (Fixnum) number of years
51
+ # @param hash (HashWithIndifferentAccess) job data for the chosen skill,
52
+ # shaped like the top-level values in langval.yml
53
+ # @return (Float) Market work estimate in Javagrads.
54
+ def market_work_in_javagrads(y, hash)
55
+ market_work_in_job_dollars(y, hash) / JAVAGRAD_IN_JOB_DOLLARS
56
+ end
57
+
58
+ # Calculate the number of Javagrads of work the job market does for beginners
59
+ # (zero years of experience) with this skill.
60
+ #
61
+ # @param hash (HashWithIndifferentAccess) job data for the chosen skill,
62
+ # shaped like the top-level values in langval.yml
63
+ # @return (Float) Beginner rating in Javagrads.
64
+ def beginner_javagrad_rating(hash)
65
+ market_work_in_javagrads(0.0, hash)
66
+ end
67
+
68
+ # Get the average salary for jobs returned from an Indeed.com search,
69
+ # based on refinements sidebar text with facet totals.
70
+ #
71
+ # @param salatext (String) refinements text
72
+ # @param total (Fixnum) total number of results
73
+ # @return (Float) Average salary in dollars
74
+ def average_salary_from_indeed_facets(salatext, total)
75
+ salaries = salatext.split(/\s+/).in_groups_of(2).map { |r| r.map { |i| i.gsub(/\D/, '').to_f }}
76
+
77
+ salaries = [[salaries[0][0], total.to_f]] + salaries
78
+
79
+ salaries = salaries.map.with_index do |salary, idx|
80
+ if idx == 0
81
+ amt, count = *salary
82
+ [amt, count - salaries[1][1]]
83
+ elsif idx == salaries.size-1
84
+ salary
85
+ else
86
+ amt, count = *salary
87
+ [(amt+salaries[idx+1][0])/2, count-salaries[idx+1][1]]
88
+ end
89
+ end
90
+
91
+ salaries = salaries.map do |amt, count|
92
+ amt * count
93
+ end
94
+
95
+ salaries.inject(&:+) / total
96
+ end
97
+
98
+ NORMAL_DISTRIBUTION = {
99
+ 1 => [1.0],
100
+ 2 => [0.5,0.5],
101
+ 3 => [0.0918,0.82,0.0918],
102
+ 4 => [0.0228,0.48,0.48,0.0228],
103
+ 5 => [0.0082,0.21,0.58,0.21,0.0082],
104
+ 6 => [0.0038,0.09,0.41,0.41,0.09,0.0038],
105
+ 7 => [0.0021,0.04,0.24,0.43,0.24,0.04,0.0021],
106
+ 8 => [0.0013,0.02,0.14,0.34,0.34,0.14,0.02,0.0013],
107
+ 9 => [0.0009,0.01,0.09,0.24,0.34,0.24,0.09,0.01,0.0009],
108
+ 10 => [0.0007,0.01,0.04,0.16,0.29,0.29,0.16,0.04,0.01,0.0007],
109
+ 11 => [0.0005,0.01,0.03,0.1,0.22,0.28,0.22,0.1,0.03,0.01,0.0005]
110
+ }
111
+
112
+ # Given the total number of resumes, model the probability
113
+ # distribution that various numbers of resumes will be selected.
114
+ #
115
+ # @param total_resumes (Fixnum) Total number of resumes.
116
+ # @return (Hash<Fixnum, Float>) Probabilities keyed to the number
117
+ # of resumes selected.
118
+ def applicants_per_job_distribution(total_resumes)
119
+ max = (Math.log(total_resumes) / 1.5).to_i + 1
120
+
121
+ dist = (1..max).to_a.map do |num|
122
+ [num, NORMAL_DISTRIBUTION[max][num-1]]
123
+ end
124
+
125
+ Hash[dist]
126
+ end
127
+
128
+ # Compute the chance a candidate will get a job, given the years
129
+ # of experience, total number of resumes, and the candidate type
130
+ #
131
+ # @param years (Float) candidate years of experience
132
+ # @param total_resumes (Fixnum) total resumes
133
+ # @param type (Symbol) in [:entry, :mid, :senior]
134
+ # @return (Float) Chance the candidate will get a job
135
+ def chance_of_getting_job(years, total_resumes, type = :senior)
136
+ year_range = case
137
+ when type == :senior
138
+ [5.0, 10.0]
139
+ when type == :mid
140
+ [3.0, 5.0]
141
+ when type == :entry
142
+ [0.0, 3.0]
143
+ end
144
+
145
+ strength = [((years.to_f - year_range[0]) / (year_range[1] - year_range[0])), 1.0].min
146
+
147
+ return 0.0 if strength < 0
148
+
149
+ dist = applicants_per_job_distribution(total_resumes)
150
+ dist = dist.map do |resumes, c|
151
+ other_resumes = resumes - 1
152
+
153
+ # probability of getting the job
154
+ c * (strength ** other_resumes)
155
+ end
156
+
157
+ dist.inject(&:+)
158
+ end
159
+
160
+ # Get the salary distribution for a skill.
161
+ #
162
+ # @param hash (Hash) A hash of job data shaped like the top-level values
163
+ # in langval.yml
164
+ # @return (Hash<Symbol, Float>) {entry: Float, mid: Float, senior: Float}
165
+ def crunch_salaries(hash)
166
+ {
167
+ entry: average_salary_from_indeed_facets(hash["salaries"]["entry"], hash["jobs"]["entry"]),
168
+ mid: average_salary_from_indeed_facets(hash["salaries"]["mid"], hash["jobs"]["mid"]),
169
+ senior: average_salary_from_indeed_facets(hash["salaries"]["senior"], hash["jobs"]["senior"])
170
+ }
171
+ end
172
+
173
+ # Convert job-dollars to Javagrads.
174
+ #
175
+ # @param job_dollars (Float) Job-dollars.
176
+ # @return (Float) Javagrads, precision-1.
177
+ def to_javagrads(job_dollars)
178
+ (job_dollars / JAVAGRAD).to_f(1)
179
+ end
180
+
181
+ end
182
+ end
183
+
184
+
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: job_dollars
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Tyler Boyd
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-07 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A job dollar is an objective measure of the strength of the job market
14
+ for entry-level workers using a given programming language and framework. It is
15
+ a composite measure of work, like kilowatt-hours or Joules, and it measures how
16
+ much work the market has done for you.
17
+ email: tboyd47@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - lib/job_dollars.rb
23
+ - lib/job_dollars.yml
24
+ - lib/job_dollars/core_ext/array.rb
25
+ - lib/job_dollars/core_ext/hash.rb
26
+ - lib/job_dollars/core_ext/hash_with_indifferent_access.rb
27
+ - lib/job_dollars/estimator.rb
28
+ homepage: https://www.github.com/tb0yd/job_dollars
29
+ licenses:
30
+ - MIT
31
+ metadata: {}
32
+ post_install_message:
33
+ rdoc_options:
34
+ - "-x"
35
+ - lib/job_dollars/core_ext/*
36
+ - "-m"
37
+ - README.md
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 2.4.5.1
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: An economic measure of the value of a programming language and framework.
56
+ test_files: []