hawatel_search_jobs 0.1.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/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
4
+ before_install: gem install bundler -v 1.10.6
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,70 @@
1
+ # Contributing
2
+
3
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Hawatel/hawatel_search_jobs. This project is intended to be a safe, welcoming space for collaboration, and contributors.
4
+
5
+ 1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone your fork,
6
+ and configure the remotes:
7
+
8
+ ```sh
9
+ # Clone your fork of the repo into the current directory
10
+ git clone https://github.com/<your-username>/hawatel_ps
11
+ # Navigate to the newly cloned directory
12
+ cd hawatel_search_jobs
13
+ # Assign the original repo to a remote called "upstream"
14
+ git remote add upstream https://github.com/Hawatel/hawatel_search_jobs
15
+ ```
16
+
17
+ 2. If you cloned a while ago, get the latest changes from upstream:
18
+
19
+ ```bash
20
+ git checkout master
21
+ git pull upstream master
22
+ ```
23
+
24
+ 3. Create a new topic branch (off of `master`) to contain your feature, change,
25
+ or fix.
26
+
27
+ **IMPORTANT**: Making changes in `master` is discouraged. You should always
28
+ keep your local `master` in sync with upstream `master` and make your
29
+ changes in topic branches.
30
+
31
+ ```sh
32
+ git checkout -b <topic-branch-name>
33
+ ```
34
+
35
+ 4. Commit your changes in logical chunks. Keep your commit messages organized,
36
+ with a short description in the first line and more detailed information on
37
+ the following lines. Feel free to use Git's
38
+ [interactive rebase](https://help.github.com/articles/about-git-rebase/)
39
+ feature to tidy up your commits before making them public.
40
+
41
+ 5. Make sure all the tests are still passing.
42
+
43
+ ```sh
44
+ rake
45
+ ```
46
+
47
+ 6. Push your topic branch up to your fork:
48
+
49
+ ```sh
50
+ git push origin <topic-branch-name>
51
+ ```
52
+
53
+ 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
54
+ with a clear title and description.
55
+
56
+ 8. If you haven't updated your pull request for a while, you should consider
57
+ rebasing on master and resolving any conflicts.
58
+
59
+ **IMPORTANT**: _Never ever_ merge upstream `master` into your branches. You
60
+ should always `git rebase` on `master` to bring your changes up to date when
61
+ necessary.
62
+
63
+ ```sh
64
+ git checkout master
65
+ git pull upstream master
66
+ git checkout <your-topic-branch>
67
+ git rebase master
68
+ ```
69
+
70
+ Thank you for your contributions!
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hawatel_search_jobs.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Daniel Iwaniuk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # Hawatel Search Jobs
2
+
3
+ Hawatel_job_search, it is gem which provides ease access to API from popular job websites to get current job offers. At this moment, supported backends are indeed.com, careerjet.com, xing.com, careerbuilder.com and reed.co.uk.
4
+
5
+ Before you can start using the gem, you need have an accounts/tokens for each portal where is required by API.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'hawatel_search_jobs'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install hawatel_search_jobs
22
+
23
+ ## Usage
24
+
25
+ #### How to configure access to search engine APIs
26
+ ```ruby
27
+ HawatelSearchJobs.configure do |config|
28
+ config.indeed[:activated] = true
29
+ config.indeed[:api] = 'api.indeed.com'
30
+ config.indeed[:version] = '2'
31
+ config.indeed[:publisher] = 'secret-key'
32
+
33
+ config.xing[:activated] = true
34
+ config.xing[:consumer_key] = 'secret-key'
35
+ config.xing[:consumer_secret] = 'secret-key'
36
+ config.xing[:oauth_token] = 'secret-key'
37
+ config.xing[:oauth_token_secret] = 'secret-key'
38
+
39
+ config.reed[:activated] = true
40
+ config.reed[:api] = 'reed.co.uk/api'
41
+ config.reed[:clientid] = 'secret-key'
42
+ config.reed[:version] = '1.0'
43
+
44
+ config.careerbuilder[:activated] = true
45
+ config.careerbuilder[:api] = 'api.careerbuilder.com'
46
+ config.careerbuilder[:clientid] = 'secret-key'
47
+ config.careerbuilder[:version] = 'v2'
48
+
49
+ config.careerjet[:activated] = true
50
+ config.careerjet[:api] = 'public.api.careerjet.net'
51
+ end
52
+ ```
53
+
54
+ #### Where to get a secret-key
55
+ 1. Indeed: http://www.indeed.com/jsp/apiinfo.jsp
56
+ 2. Xing: https://dev.xing.com
57
+ 3. Reed: https://www.reed.co.uk/developers/jobseeker
58
+ 4. Careerbuilder: http://developer.careerbuilder.com
59
+ 5. Careerjet: secret-key is no needed (http://www.careerjet.com/partners/api/)
60
+
61
+ #### Get first page of job offers
62
+ There are two ways to read the returned job offers.
63
+
64
+ 1. Returned job offers by search_jobs method:
65
+ ```ruby
66
+ client = HawatelSearchJobs::Client.new
67
+ result = client.search_jobs({:keywords => 'ruby'})
68
+ p result[:indeed]
69
+ p result[:xing]
70
+ p result[:reed]
71
+ p result[:careerbuilder]
72
+ p result[:careerjet]
73
+ ```
74
+
75
+ 2. Access to the instance variable jobs_table.
76
+ Instance variable *jobs_table* always has last returned job offers.
77
+ ```ruby
78
+ client = HawatelSearchJobs::Client.new
79
+ client.search_jobs({:keywords => 'ruby'})
80
+ p client.jobs_table
81
+ ```
82
+
83
+ Keep in mind that each API has a limit of returned records. For consistency, each API returns maximum `25` records.
84
+
85
+ #### Get next page of job offers
86
+ ```ruby
87
+ client = HawatelSearchJobs::Client.new
88
+ client.search_jobs({:keywords => 'ruby'})
89
+ p client.next
90
+ ```
91
+
92
+ #### Get all pages of job offers
93
+ ```ruby
94
+ client = HawatelSearchJobs::Client.new
95
+ client.search_jobs({:keywords => 'ruby'})
96
+
97
+ job_offers = Array.new
98
+
99
+ while(client.next) do
100
+ job_offers << client.jobs_table
101
+ end
102
+ ```
103
+ Keep in mind if keywords will be too general probably each API will return loads of data and then a daily limit for each API provider can be exceeded.
104
+ Reed about your daily limit for each API on the provider side.
105
+
106
+ #### How many job offers were found
107
+ ```ruby
108
+ client = HawatelSearchJobs::Client.new
109
+ client.search_jobs({:keywords => 'ruby'})
110
+ p client.count # for all APIs
111
+ p client.count('indeed') # for a particular API
112
+ ```
113
+ Keep in mind the `client.count` returns count of total results for each APIs which can be returned to you if you will use `client.next` method.
114
+
115
+ ## Structure of job offers table
116
+ Below is an example for indeed but each of APIs has the same result structure.
117
+ ```ruby
118
+ client = HawatelSearchJobs::Client.new
119
+ client.search_jobs({:keywords => 'ruby'})
120
+ result = client.jobs_table
121
+
122
+ result[:indeed][:code] # HTTP status code (see https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)
123
+ result[:indeed][:message] # HTTP message (seee https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)
124
+
125
+ result[:indeed][:totalResults] # Total results of job offers which matches to your search criteria on API provider
126
+ result[:indeed][:page] # Current results page number counted from index 0
127
+ result[:indeed][:last] # Last results page number
128
+ result[:indeed][:key] # Internal key which usuely keep last URL sent to API or last used keywords
129
+
130
+ result[:indeed][:jobs] # OpenStruct array which store returned job offers from API provider
131
+ result[:indeed][:jobs].jobtitle # Job title
132
+ result[:indeed][:jobs].location # Job location
133
+ result[:indeed][:jobs].company # Company name which posted the job
134
+ result[:indeed][:jobs].date # Date of the posted job (format %d/%m/%y)
135
+ result[:indeed][:jobs].url # Source URL to the job offer
136
+ ```
137
+
138
+ ## Contributing
139
+
140
+ See [CONTRIBUTING](CONTRIBUTING.md)
141
+
142
+
143
+ ## License
144
+
145
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
146
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hawatel_search_jobs/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hawatel_search_jobs"
8
+ spec.version = HawatelSearchJobs::VERSION
9
+ spec.authors = ['Przemyslaw Mantaj','Daniel Iwaniuk']
10
+ spec.email = ['przemyslaw.mantaj@hawatel.com', 'daniel.iwaniuk@hawatel.com']
11
+
12
+ spec.summary = %q{Hawatel_job_search, it is gem which provides ease access to API from popular job websites to get current job offers.}
13
+ spec.description = %q{Hawatel_job_search, it is gem which provides ease access to API from popular job websites
14
+ to get current job offers. At this moment, supported backends are indeed.com, careerjet.com,
15
+ xing.com, careerbuilder.com and reed.com.}
16
+ spec.homepage = "http://github.com/Hawatel/hawatel_search_jobs"
17
+ spec.license = "MIT"
18
+
19
+ spec.files = `git ls-files`.split($/)
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_runtime_dependency 'xing_api'
25
+ spec.add_runtime_dependency 'activesupport'
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.10"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "rspec"
30
+ end
@@ -0,0 +1,64 @@
1
+ ##
2
+ # = Hawatel Search Jobs
3
+ # More details how to use it was described here {HawatelSearchJobs::Client}
4
+ module HawatelSearchJobs
5
+ require 'hawatel_search_jobs/helpers'
6
+ require 'hawatel_search_jobs/api'
7
+ require 'hawatel_search_jobs/client'
8
+
9
+ class << self
10
+ attr_accessor :indeed, :xing, :reed, :careerbuilder, :careerjet
11
+
12
+ ##
13
+ # How to configure APIs go to example {HawatelSearchJobs::Client#search_jobs}
14
+ def configure
15
+ @indeed = default_indeed(Hash.new)
16
+ @xing = default_xing(Hash.new)
17
+ @reed = default_reed(Hash.new)
18
+ @careerbuilder = default_careerbuilder(Hash.new)
19
+ @careerjet = default_careerjet(Hash.new)
20
+ yield self
21
+ true
22
+ end
23
+
24
+ private
25
+
26
+ def default_xing(settings)
27
+ settings[:activated] = false
28
+ settings[:consumer_key] = ''
29
+ settings[:consumer_secret] = ''
30
+ settings[:oauth_token] = ''
31
+ settings[:oauth_token_secret] = ''
32
+ return settings
33
+ end
34
+
35
+ def default_indeed(settings)
36
+ settings[:activated] = false
37
+ settings[:api] = 'api.indeed.com'
38
+ settings[:version] = '2'
39
+ settings[:publisher] = ''
40
+ return settings
41
+ end
42
+
43
+ def default_careerjet(settings)
44
+ settings[:activated] = false
45
+ settings[:api] = 'public.api.careerjet.net'
46
+ return settings
47
+ end
48
+
49
+ def default_reed(settings)
50
+ settings[:activated] = false
51
+ settings[:api] = 'reed.co.uk/api'
52
+ settings[:version] = '1.0'
53
+ return settings
54
+ end
55
+
56
+ def default_careerbuilder(settings)
57
+ settings[:activated] = false
58
+ settings[:api] = 'api.careerbuilder.com'
59
+ settings[:version] = 'v2'
60
+ return settings
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,9 @@
1
+ module HawatelSearchJobs
2
+ module Api
3
+ require 'hawatel_search_jobs/api/indeed'
4
+ require 'hawatel_search_jobs/api/reed'
5
+ require 'hawatel_search_jobs/api/xing'
6
+ require 'hawatel_search_jobs/api/careerbuilder'
7
+ require 'hawatel_search_jobs/api/careerjet'
8
+ end
9
+ end
@@ -0,0 +1,170 @@
1
+ require 'ostruct'
2
+ require 'active_support/core_ext/hash'
3
+
4
+ module HawatelSearchJobs
5
+ module Api
6
+ ##
7
+ # = Carerrbuilder API
8
+ #
9
+ # @see http://api.careerbuilder.com/Search/jobsearch/jobsearchinfo.aspx
10
+ module Careerbuilder
11
+ class << self
12
+ include HawatelSearchJobs::Helpers::Base
13
+
14
+ DEFAULT = {
15
+ :keywords => '',
16
+ :location => '',
17
+ :company => ''
18
+ }
19
+
20
+ RESULT_LIMIT = 25
21
+
22
+ # Search jobs by specific criteria
23
+ # @param [Hash] args
24
+ # @option args :settings [Hash] search criteria
25
+ # - *:api* (String) - URL API (default: api.careerbuilder.com)
26
+ # - *:version* (String) - a version of API (default: v2)
27
+ # - *:clientid* (String) - Private Developer Key (see http://developer.careerbuilder.com)
28
+ # @option args :query [Hash] settings of API
29
+ # - *:keywors* (String)
30
+ # - *:location* (String)
31
+ # - *:company* (String)
32
+ # @example
33
+ # jobs = Careerbuilder.search({:settings => {
34
+ # :api => 'api.careerbuilder.com',
35
+ # :version => 'v2',
36
+ # :clientid => 'secret-code'},
37
+ # :query => {
38
+ # :keywords => 'ruby',
39
+ # :location => 'London',
40
+ # :company => ''}})
41
+ # @return [Hash] First page of the result (see constant RESULT_LIMIT)
42
+ def search(args)
43
+ args[:query] = DEFAULT.merge(args[:query]) if args[:query]
44
+
45
+ if args[:query_key].nil?
46
+ url_request = prepare_conn_string(args) + prepare_query(args)
47
+ else
48
+ url_request = args[:query_key]
49
+ end
50
+
51
+ response = send_request(url_request)
52
+ attributes = build_jobs_table(response, url_request)
53
+ OpenStruct.new(attributes)
54
+ end
55
+
56
+
57
+ # Get a specific page result
58
+ # At the beging you have to run {search} method and get :key from result and pass it to the argument :query_key
59
+ # @param [Hash] args
60
+ # @option args :settings [Hash] see {search}
61
+ # @option args [Integer] :page page number counted from 0
62
+ # @option args [String] :query_key
63
+ # @example
64
+ # jobs = Careerbuilder.page({:settings => {
65
+ # :api => 'api.careerbuilder.com',
66
+ # :version => 'v2',
67
+ # :clientid => 'secret-code'},
68
+ # :page => 5,
69
+ # :query_key => 'value from :key returned by search method'})
70
+ # @return [Hash] Job offers from specific page
71
+ def page(args)
72
+ page = args[:page].to_i || 0
73
+ if args[:query_key]
74
+ url_request = args[:query_key].gsub(/&PageNumber=\d+/, '') << "&PageNumber=#{page+1}"
75
+ args[:query_key] = url_request
76
+ search(args)
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def build_jobs_table(response, url_request)
83
+ attributes = Hash.new
84
+ attributes[:code] = response.code.to_i
85
+ attributes[:msg] = response.message
86
+
87
+ attributes[:totalResults] = 0
88
+ attributes[:page] = nil
89
+ attributes[:last] = nil
90
+ attributes[:key] = url_request
91
+ attributes[:jobs] = nil
92
+
93
+ if response.code.to_i == 200
94
+ xml_response = Hash.from_xml(response.body.gsub('\n', ''))
95
+ begin
96
+ if xml_response['ResponseJobSearch'] && xml_response['ResponseJobSearch']['Results']
97
+ attributes[:totalResults] = xml_response['ResponseJobSearch']['TotalCount'].to_i || 0
98
+ attributes[:page] = get_page_number(url_request) - 1
99
+ attributes[:last] = xml_response['ResponseJobSearch']['TotalPages'].to_i - 1
100
+ attributes[:key] = url_request
101
+ attributes[:jobs] = parse_raw_data(xml_response)
102
+ end
103
+ rescue
104
+ raise "Something wrong with returned data: #{xml_response}"
105
+ end
106
+ end
107
+ attributes
108
+ end
109
+
110
+ def prepare_query(args)
111
+ "Keywords=#{args[:query][:keywords]}&" \
112
+ "Location=#{args[:query][:location]}&" \
113
+ "CompanyName=#{args[:query][:company]}"
114
+ end
115
+
116
+ def prepare_conn_string(args)
117
+ conn_string = "https://#{args[:settings][:api]}/#{args[:settings][:version]}/jobsearch?" \
118
+ "PerPage=#{RESULT_LIMIT}&DeveloperKey=#{args[:settings][:clientid]}&"
119
+
120
+ conn_string
121
+ end
122
+
123
+ def parse_raw_data(data)
124
+ jobs = Array.new
125
+ data['ResponseJobSearch']['Results']['JobSearchResult'].each do |offer|
126
+ job = Hash.new
127
+ job[:jobtitle] = offer['JobTitle'] if offer['JobTitle']
128
+
129
+ country = get_country_name(offer['State'], data['ResponseJobSearch']['SearchMetaData']['SearchLocations'])
130
+ location = country
131
+ location += ", #{offer['City']}" if offer['City'] && country != offer['City']
132
+ job[:location] = location
133
+ job[:company] = offer['Company']
134
+
135
+ newdate = offer['PostedDate'].split('/')
136
+ job[:date] = "#{newdate[1]}/#{newdate[0]}/#{newdate[2]}"
137
+
138
+ job[:date] = convert_date_to_format(job[:date],'%d/%m/%y')
139
+ job[:url] = offer['JobDetailsURL']
140
+
141
+ job = convert_empty_to_nil(job)
142
+
143
+ jobs << OpenStruct.new(job)
144
+ end
145
+ jobs
146
+ rescue
147
+ raise "Cannot parse raw data - #{data.inspect}"
148
+ end
149
+
150
+ def get_country_name(code, locations)
151
+ locations.each do |loc|
152
+ if loc[1]['StateCode'] && code && code.to_s == loc[1]['StateCode'].to_s
153
+ return loc[1]['City'] if loc[1]['City']
154
+ end
155
+ end
156
+
157
+ 'United States'
158
+ rescue
159
+ raise "Cannot get country name (code=#{code}, locations=#{locations}"
160
+ end
161
+
162
+ def get_page_number(url)
163
+ result = url.match(/\&PageNumber=(\d+)/)
164
+ result ? result[1].to_i : 1
165
+ end
166
+
167
+ end
168
+ end
169
+ end
170
+ end