presume 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b80339c1863dc69cf2e8be22e594a58bde43dbda
4
+ data.tar.gz: b7c250b6fb461ade98ee62d194aac7c9c8f5532a
5
+ SHA512:
6
+ metadata.gz: 3048dc8d33cca2e7c141a6dafb89c4ff769ca2a3ba029c5159abda211ac334f8cd7ad8f1030441743ed7e2232f9ca9b6788a8eff77f88bafb1ec0246731c225c
7
+ data.tar.gz: 3b50bf15404471cb27cfaded216de75cffcd053e4fdc822065a01c7e6650988655ccafb5b8b460f0c46b8f7ab8d35865c85307456bee3cecc0fbe2d55699b8ef
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in parse_resume.gemspec
4
+ gemspec
@@ -0,0 +1,104 @@
1
+ # Presume
2
+
3
+ A resume parser coupled with fundamental Applicant Tracking System Technology
4
+
5
+ ### Description
6
+
7
+ Presume is the first open-source Applicant Tracking System technology for Ruby-on-Rails developers. It works by parsing the resume into the CVSTOM.org resume format Section >> Header >> Bullets, and then taking an input of skills and their expected duration to check for in that resume.
8
+
9
+ ### Features
10
+
11
+ Parse resumes
12
+ Checks resumes for specific skills, positions, and their duration
13
+
14
+ ### Resume Parser:
15
+
16
+ require 'presume'
17
+
18
+ # Sample input
19
+ resume_text = "Leigh Silverstein\n123 Ave.\n\nWork Experience\nProject Coordinator"
20
+
21
+ # Parse resume with string input
22
+ presume = Presume.new(resume_text)
23
+
24
+ # Retrieve Sections
25
+ Presume.sections
26
+
27
+ #=> {0 => SectionObject1, 1 => SectionObject2}
28
+
29
+ # SectionObject Functions
30
+
31
+ SectionObject.text
32
+
33
+ #=> "Work Experience"
34
+
35
+ SectionObject.children
36
+
37
+ #=> [ HeaderObject1, HeaderObject2 ]
38
+
39
+ # HeaderObject Functions
40
+
41
+ SectionObject.text
42
+
43
+ #=> "Project Coordinator, Projects4Ever Inc., Toronto, Ontario, Jan 2011-Jul 2012
44
+
45
+ SectionObject.duration
46
+
47
+ #=> 1.5 (In Years)
48
+
49
+ SectionObject.start_time_text
50
+
51
+ #=> Jan 2011
52
+
53
+ SectionObject.end_time_text
54
+
55
+ #=> Jul 2012
56
+
57
+ HeaderObject.children
58
+
59
+ #=> [ BulletObject1, BulletObject2 ]
60
+
61
+ # BulletObject inherits all functions from the HeaderObject except children
62
+
63
+
64
+ ### ATS:
65
+
66
+ # After parsing a resume
67
+
68
+ # Checking for certain position or education ("name", expected_minimum_duration_in_years)
69
+ intake_hash = {"Project Coordinator|Project Assistant" => 1, "Bachelors Finance|BF|B.F." => 4}
70
+
71
+ # Check for positions
72
+ presume.positions?(intake_hash)
73
+
74
+ #=> {"Project Coordinator|Project Assistant" => [ MatchedHeaderObject1 ], "Bachelors Finance|BF|B.F." => [ MatchedHeaderObject2 ]}
75
+
76
+ # Checking for certain skills ("name", expected_minimum_duration_in_years)
77
+ intake_hash = {"database management" => 1, "clear communication" => 0}
78
+
79
+ # Check for positions
80
+ presume.skills?(intake_hash)
81
+
82
+ #=> {"database management" => [ MatchedBulletObject1 ], "clear communication" => [ MatchedBulletObject2 ]}
83
+
84
+ #Note that matched headers and bullets are the same classes as the headers and bullets discussed in the resume parsing section
85
+
86
+ ### Requirements
87
+
88
+ * EngTagger
89
+ * Ruby-Stemmer
90
+ * Docx (for testing)
91
+
92
+ ### Install
93
+
94
+ (sudo) gem install presume
95
+
96
+ ### Author
97
+
98
+ of this Ruby library
99
+
100
+ * Leigh Silverstein (lsilversteinto [at] gmail.com)
101
+
102
+ ### License
103
+
104
+ This library is distributed under the GPL. Please see the LICENSE file.
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
@@ -0,0 +1,18 @@
1
+ class Bullet < Header
2
+
3
+ attr_accessor :text, :id
4
+
5
+ def initialize(classifide, header)
6
+
7
+ both_classifications.each do |classification|
8
+ instance_variable_set(("@" + classification).to_sym, header.send(classification))
9
+ end
10
+
11
+ @header_text = @text
12
+ @header_id = @id
13
+
14
+ @text = classifide.text
15
+ @id = classifide.id
16
+ end
17
+
18
+ end
@@ -0,0 +1,81 @@
1
+
2
+
3
+ class Classifide
4
+
5
+ attr_accessor *both_classifications_symboled
6
+
7
+ def initialize(classifide)
8
+ both_classifications.each do |classification|
9
+ instance_variable_set(("@" + classification).to_sym, classifide[classification.to_sym])
10
+ end
11
+ end
12
+
13
+ def more_words_than?(number)
14
+ @number_of_words > number
15
+ end
16
+
17
+ def name?
18
+ @name.nil?
19
+ end
20
+
21
+ def many_words?
22
+ @many_words
23
+ end
24
+
25
+ def email?
26
+ !@email.nil?
27
+ end
28
+
29
+ def type?
30
+ @type.nil?
31
+ end
32
+
33
+ def address?
34
+ !@address.nil?
35
+ end
36
+
37
+ def phone?
38
+ !@phone.nil?
39
+ end
40
+
41
+ def verbs?
42
+ !@verbs.nil?
43
+ end
44
+
45
+ def date?
46
+ if @dates.nil? and @dates_2.nil?
47
+ false
48
+ else
49
+ true
50
+ end
51
+ end
52
+
53
+
54
+ def institution?
55
+ if @schools.nil? and @companies.nil?
56
+ false
57
+ else
58
+ true
59
+ end
60
+ end
61
+
62
+ def profession?
63
+ !@professions.nil?
64
+ end
65
+
66
+ def city?
67
+ !@cities.nil?
68
+ end
69
+
70
+ def section?
71
+ !@section.nil?
72
+ end
73
+
74
+ def set_new_value(attribute, new_value)
75
+ instance_variable_set(("@" + attribute).to_sym, new_value)
76
+ end
77
+
78
+ def children
79
+ @presume.all_types[@id].drop(1)
80
+ end
81
+ end
@@ -0,0 +1,170 @@
1
+ class Header < Classifide
2
+
3
+ attr_accessor :clean_profession
4
+
5
+ def initialize(classifide)
6
+ both_classifications.each do |classification|
7
+ instance_variable_set(("@" + classification).to_sym, classifide.send(classification))
8
+ end
9
+ end
10
+
11
+ def remove_date
12
+ @clean_profession = @text.gsub(regex_dates, "")
13
+ end
14
+
15
+ def remove_city
16
+ @clean_profession.gsub!(regex_cities, "")
17
+ end
18
+
19
+ def remove_institution
20
+ @clean_profession.gsub!(regex_companies, "")
21
+ @clean_profession.gsub!(regex_schools, "")
22
+ end
23
+
24
+ def get_professions
25
+ @clean_profession.gsub!(regex_companies, "")
26
+ end
27
+
28
+ def remove_all_but_profession
29
+ @clean_profession = @clean_profession[regex_professions]
30
+ end
31
+
32
+
33
+ def season?
34
+ !@text[regex_season].nil?
35
+ end
36
+
37
+ def split_date
38
+ @prepare_split_date = @dates.gsub(Regexp.new('( |)(to\\b|-|–)( |)', 'i'), "{+)")
39
+ @split_date = @prepare_split_date.split("{+)")
40
+ if @split_date.kind_of?(Array)
41
+ @split_date
42
+ else
43
+ @split_date = [@dates]
44
+ end
45
+ end
46
+
47
+ def start_time_text?
48
+ if date?
49
+ split_date[0]
50
+ end
51
+ end
52
+
53
+ def start_time_number
54
+ if date?
55
+ @start_time_number = start_year.to_i + convert_month_to_number(start_month)
56
+ else
57
+ nil
58
+ end
59
+ end
60
+
61
+ def end_time_text?
62
+ if @end_time_text.nil?
63
+ if date?
64
+ if end_date_exists?
65
+ @end_time_text = split_date[1]
66
+ end
67
+ end
68
+ else
69
+ @end_time_text
70
+ end
71
+ end
72
+
73
+ def current?
74
+
75
+ !end_time_text?[regex_current].nil?
76
+ end
77
+
78
+ def end_time_number
79
+ if date?
80
+ if end_date_exists?
81
+ if current?
82
+ @end_time_number = Date.today.strftime("%Y").to_i + (Date.today.strftime("%m")).to_i/12
83
+ else
84
+ @end_time_number = end_year.to_i + convert_month_to_number(end_month)
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def end_date_exists?
91
+ split_date.length == 2
92
+ end
93
+
94
+ def start_year
95
+ @start_year = split_date[0][regex_year]
96
+ end
97
+
98
+ def start_month
99
+ @start_month = split_date[0][regex_month]
100
+ if @start_month.nil?
101
+ @start_month = split_date[0][regex_season]
102
+ if @start_month.nil?
103
+ @start_month = "Jan"
104
+ end
105
+ end
106
+ @start_month
107
+ end
108
+
109
+ def end_year
110
+ @end_year = split_date[1][regex_year]
111
+ end
112
+
113
+ def end_month
114
+ if @end_month.nil?
115
+ @end_month = split_date[1][regex_month]
116
+ if @end_month.nil?
117
+ @end_month = "Jan"
118
+ end
119
+ @end_month
120
+ else
121
+ @end_month
122
+ end
123
+ end
124
+
125
+ def duration
126
+ if season?
127
+ @duration = 3/12
128
+ else
129
+ @duration = end_time_number - start_time_number
130
+ end
131
+ end
132
+
133
+ def convert_season_to_number(season)
134
+ case season.downcase
135
+ when "winter"
136
+ 0
137
+ when "spring"
138
+ 3
139
+ when "summer"
140
+ 6
141
+ when "fall"
142
+ 9
143
+ end
144
+ end
145
+
146
+
147
+ def convert_month_to_number(month)
148
+ if season?
149
+ @converted_month = convert_season_to_number(month)
150
+ else
151
+ @converted_month = Date::ABBR_MONTHNAMES.index(month[0..2])
152
+ end
153
+
154
+ @converted_month_of_12 = (@converted_month / 12).to_f
155
+ end
156
+
157
+ def split_cities
158
+ @location = @cities.split(", ")
159
+ end
160
+
161
+ def just_city
162
+
163
+ split_cities[0]
164
+ end
165
+
166
+ def just_state
167
+ split_cities[1]
168
+ end
169
+
170
+ end
@@ -0,0 +1,375 @@
1
+ class ResumeBuilder
2
+
3
+ attr_accessor :classifides, :resume
4
+
5
+ def initialize(classified_lines)
6
+ @classifides = classified_lines
7
+ @length = classified_lines.length
8
+ end
9
+
10
+ def set_classifide(line_number)
11
+ @classifide = @classifides[line_number]
12
+ @line_number = line_number
13
+ end
14
+
15
+ def header_line_number?
16
+ ((@line_number + 1)/@length).to_f <= 0.10
17
+ end
18
+
19
+ def check_for_name_in_header
20
+ if !@classifide.many_words? and !@classifide.name? and @classifide.type?
21
+ @classifide.type = "name"
22
+ end
23
+ end
24
+
25
+ def check_for_email_in_header
26
+ if !@classifide.many_words? and @classifide.email? and @classifide.type?
27
+ @classifide.type = "email"
28
+ end
29
+ end
30
+
31
+ def check_for_address_in_header
32
+ if @classifide.address? and @classifide.type?
33
+ @classifide.type = "address"
34
+ end
35
+ end
36
+
37
+ def check_for_phone_in_header
38
+ if !@classifide.many_words? and @classifide.phone? and @classifide.type?
39
+ @classifide.type = "phone"
40
+ end
41
+ end
42
+
43
+ def check_header
44
+ if header_line_number?
45
+ check_for_name_in_header
46
+ check_for_email_in_header
47
+ check_for_address_in_header
48
+ check_for_phone_in_header
49
+ end
50
+ end
51
+
52
+ def classifide_after
53
+ if @line_number == @length - 1
54
+ @classifide_after = @classifides[@line_number]
55
+ else
56
+ @classifide_after = @classifides[@line_number + 1]
57
+ end
58
+ end
59
+
60
+ def classifide_after_after
61
+ if @line_number >= @length - 2
62
+ @classifide_after = @classifides[@line_number]
63
+ else
64
+ @classifide_after = @classifides[@line_number + 2]
65
+ end
66
+ end
67
+
68
+ def classifide_before
69
+ @classifide_before = @classifides[@line_number - 1]
70
+ end
71
+
72
+ def check_obvious_types
73
+
74
+ if !@classifide.institution? and @classifide.type? and !@classifide.date? and !@classifide.city? and !@classifide.profession? and !@classifide.many_words? and @classifide.section?
75
+ @classifide.type = "section"
76
+ end
77
+
78
+ if @classifide.many_words? and @classifide.verbs? and @classifide.type? and !@classifide.date?
79
+ @classifide.type = "bullet"
80
+ end
81
+
82
+ if !@classifide.institution? and @classifide.type? and !@classifide.date? and !@classifide.city? and !@classifide.profession? and !@classifide.section?
83
+ @classifide.type = "bullet"
84
+ end
85
+
86
+ if @classifide.institution? and @classifide.type? and @classifide.date? and @classifide.city? and @classifide.profession?
87
+ @classifide.type = "header"
88
+ end
89
+
90
+ if @classifide.type? and ((@classifide.institution? and @classifide.date? and @classifide.city? and @classifide.profession?) or (@classifide.institution? and @classifide.date? and @classifide.city?) or (@classifide.institution? and @classifide.date? and !@classifide.many_words?) or (@classifide.date? and @classifide.city? and @classifide.profession? and !@classifide.many_words?))
91
+ @classifide.type = "header_x"
92
+ end
93
+
94
+ if @classifide.type? and @classifide.many_words? and !@classifide.verbs? and (@classifide.profession? or @classifide.city? or @classifide.institution?)
95
+ @classifide.type = "header_x"
96
+ end
97
+
98
+ end
99
+
100
+
101
+ def first_pass
102
+ @length.times do |n|
103
+ set_classifide(n)
104
+ check_header
105
+ check_obvious_types
106
+ end
107
+ end
108
+
109
+ def second_pass
110
+ @length.times do |n|
111
+ set_classifide(n)
112
+ if !@classifide.institution? and @classifide.type? and !@classifide.date? and !@classifide.city? and !@classifide.profession? and !@classifide.many_words?
113
+ if (@classifide_after.institution? and @classifide_after.profession?) or (!@classifide_after.institution? and !@classifide_after.profession?)
114
+ @classifide.type = "section"
115
+ end
116
+ end
117
+
118
+ if (@classifide.institution? or @classifide.profession?) and !@classifide.many_words? and @classifide.type?
119
+ @classifide.type = "header_x"
120
+ end
121
+
122
+ if !@classifide.institution? and @classifide.profession? and @classifide.type?
123
+ @classifide.type = "header_x"
124
+ end
125
+
126
+ if @classifide.type? and @classifide.more_words_than?(10)
127
+ @classifide.type = "bullet"
128
+ end
129
+
130
+
131
+ end
132
+ end
133
+
134
+ def reset_header_x_start
135
+ @header_x_start = true
136
+ end
137
+
138
+ def build_resume
139
+ reset_header_x_start
140
+ @length.times do |n|
141
+ set_classifide(n)
142
+ unless @classifide.type?
143
+ unless @classifide.type == "name" or @classifide.type == "email" or @classifide.type == "phone" or @classifide.type == "address"
144
+ send(@classifide.type + "_build")
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ def add_to_all_types(id)
151
+ if all_types[id].nil?
152
+ all_types[id] = [@all_type]
153
+ else
154
+ all_types[id] += [@all_type]
155
+ end
156
+ end
157
+
158
+ def section_build
159
+ @header_number = nil
160
+ @header_x_start = true
161
+ @section_number = @line_number
162
+ @all_type = @classifide
163
+ add_section_to_resume
164
+ end
165
+
166
+ def add_section_to_resume
167
+ sections.merge!({@line_number => @all_type})
168
+ add_to_all_types(@line_number)
169
+ end
170
+
171
+ def add_header_to_resume
172
+ headers.merge!({@line_number => @header})
173
+
174
+ add_to_all_types(@line_number)
175
+ add_to_all_types(@section_number)
176
+ end
177
+
178
+ def add_bullet_to_resume
179
+ bullets.merge!({@line_number => @bullet})
180
+ add_to_all_types(@line_number)
181
+ add_to_all_types(header_number?)
182
+ end
183
+
184
+ def section_number?
185
+ if @section_number.nil?
186
+ @section_number = -1
187
+
188
+ else
189
+ @section_number
190
+ end
191
+ end
192
+
193
+ def previous_section_headers?
194
+ !headers[@section_number].nil?
195
+ end
196
+
197
+ def header_build
198
+ @bullet = nil
199
+ @header_number = @line_number
200
+ @header = Header.new(@classifide)
201
+ @header_x_start = true
202
+ @all_type = @header
203
+ add_header_to_resume
204
+ end
205
+
206
+ def create_blank_classifide
207
+ @blank_classifide = Classifide.new(id: -1)
208
+ end
209
+ def bullet_parent
210
+ if all_types[header_number?].nil?
211
+ @all_type = create_blank_classifide
212
+ add_to_all_types(-1)
213
+ all_types[-1][0]
214
+ else
215
+ all_types[header_number?][0]
216
+ end
217
+ end
218
+
219
+ def bullet_build
220
+ @header_x_start = true
221
+ @bullet = Bullet.new(@classifide, bullet_parent)
222
+ @all_type = @bullet
223
+ add_bullet_to_resume
224
+ end
225
+
226
+ def header_number?
227
+ if @header_number.nil?
228
+ section_number?
229
+ else
230
+ @header_number
231
+ end
232
+ end
233
+
234
+ def check_for_gaps
235
+ header_classifications.each do |classification|
236
+ if @classifide.send(classification).nil?
237
+ if !classifide_after_after.send(classification).nil?
238
+ @classifide.set_new_value(classification, classifide_after_after.send(classification))
239
+ @header_x_start = "almost"
240
+ end
241
+ end
242
+ end
243
+ end
244
+
245
+ def combine_text()
246
+
247
+ end
248
+
249
+ def header_x_build
250
+
251
+ @header_number = @line_number
252
+ if @header_x_start == true
253
+ if classifide_after.type == "header_x"
254
+ header_classifications.each do |classification|
255
+ if @classifide.send(classification).nil?
256
+ @classifide.set_new_value(classification, classifide_after.send(classification))
257
+ end
258
+ end
259
+
260
+
261
+ @classifide.set_new_value("text", @classifide.text + ", " + classifide_after.text)
262
+
263
+ if classifide_after_after.type == "header_x"
264
+ check_for_gaps
265
+ if @header_x_start == 'almost'
266
+ @classifide.set_new_value("text", @classifide.text + ", " + classifide_after_after.text)
267
+ end
268
+
269
+ @header_x_start = false unless @header_x_start == "almost"
270
+ else
271
+
272
+
273
+
274
+ @header_x_start = false
275
+ end
276
+
277
+ end
278
+ @header = Header.new(@classifide)
279
+ @all_type = @header
280
+ add_header_to_resume
281
+
282
+ else
283
+ if @header_x_start == "almost"
284
+ @header_x_start == "almost_closer"
285
+ else
286
+ @header_x_start = true
287
+ end
288
+ end
289
+ end
290
+
291
+ def resume
292
+ resume = {sections: sections, headers: headers, bullets: bullets, all_types: all_types}
293
+ end
294
+
295
+ def sections
296
+ @sections ||= {}
297
+ end
298
+
299
+ def headers
300
+ @headers ||= {}
301
+ end
302
+
303
+ def bullets
304
+ @bullets ||= {}
305
+ end
306
+
307
+ def all_types
308
+ @all_types ||= {}
309
+ end
310
+ =begin
311
+ @length.times do |n|
312
+
313
+ set_classifide(n)
314
+
315
+ if type?
316
+
317
+ #check following types
318
+ if n < @n - 2 and n > 1
319
+
320
+ if !many_words? and @p_hash[n+1][:type] == "resume_post" and @p[:type].nil?
321
+ @p[:type] = "resume_group"
322
+ end
323
+
324
+ if @p[:word_length] == false and @p_hash[n+1][:type] == "resume_post_x" and @p_hash[n-1][:type] != "resume_post_x" and @p[:type].nil?
325
+ @p[:type] = "resume_group"
326
+ end
327
+ if @p[:word_length] == false and @p_hash[n+1][:type] == "resume_line" and /resume_post/.match(@p_hash[n-1][:type]).nil? and @p[:type].nil?
328
+ @p[:type] = "resume_group"
329
+ end
330
+ end
331
+ end
332
+ end
333
+
334
+ #pass 4
335
+
336
+ (@n -1).times do |n|
337
+
338
+ @p = @p_hash[n]
339
+
340
+ if @p[:type].nil?
341
+ #check following types
342
+ if n < @n - 2 and n > 1
343
+
344
+ if (@p_hash[n+1][:type] == "resume_post" or @p_hash[n+1][:type] == "resume_group") and @p_hash[n-1][:type] == "resume_post" and @p[:type].nil?
345
+ @p[:type] = "resume_line"
346
+ end
347
+
348
+ if @p_hash[n-1][:type] == "resume_post" and @p_hash[n+1][:type] == "resume_line" and @p[:type].nil?
349
+ @p[:type] = "resume_post_x"
350
+ end
351
+ end
352
+ end
353
+ end
354
+ =end
355
+
356
+ =begin
357
+ @current_user_2 = current_user
358
+ #@current_user_2 = User.find(2)
359
+ @resume_line_shoot = 0
360
+ @resume_line_build = 0
361
+ (@n -1).times do |n|
362
+
363
+ @p = @p_hash[n]
364
+
365
+ puts @p[:type]
366
+ unless /resume/.match(@p[:type]).nil?
367
+ puts @p[:type]
368
+ construct_resume_object(args = {n: n, p_hash: @p_hash, resume_group: @resume_group, resume_post: @resume_post, resume_line: @resume_line})
369
+ end
370
+ end
371
+
372
+ redirect_to current_user
373
+ =end
374
+
375
+ end