presume 0.0.2

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.
@@ -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