kuali_toolbox 0.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.gitignore +14 -0
- data/.travis.yml +10 -0
- data/Account_Provisioning_CSV_Template.xlsx +0 -0
- data/Gemfile +20 -0
- data/LICENSE.txt +663 -0
- data/README.md +57 -0
- data/Rakefile +26 -0
- data/bin/transform_CSV_to_HR_XML +332 -0
- data/bin/validate_HR_XML +32 -0
- data/kuali_toolbox.gemspec +48 -0
- data/lib/kuali_toolbox/etl/grm.rb +394 -0
- data/lib/kuali_toolbox/etl.rb +410 -0
- data/lib/kuali_toolbox/version.rb +20 -0
- data/lib/kuali_toolbox.rb +23 -0
- data/spec/kuali_toolbox/etl/grm_spec.rb +620 -0
- data/spec/kuali_toolbox/etl_spec.rb +521 -0
- data/spec/kuali_toolbox_spec.rb +27 -0
- data/spec/spec_helper.rb +42 -0
- data.tar.gz.sig +0 -0
- metadata +190 -0
- metadata.gz.sig +0 -0
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# kuali_toolbox
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/KualiCo/kuali_toolbox.svg?branch=master)](https://travis-ci.org/KualiCo/kuali_toolbox)
|
4
|
+
[![Test Coverage](https://codeclimate.com/github/KualiCo/kuali_toolbox/badges/coverage.svg)](https://codeclimate.com/github/KualiCo/kuali_toolbox)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/kuali_toolbox.svg)](http://badge.fury.io/rb/kuali_toolbox)
|
6
|
+
|
7
|
+
Client library and command-line tools to help interact with KualiCo's cloud APIs.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
To simply install the gem and provide access to the command line tools:
|
12
|
+
|
13
|
+
$ gem install kuali_toolbox
|
14
|
+
|
15
|
+
However, if you would like to reuse the our ruby modules in your own ruby program,
|
16
|
+
add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'kuali_toolbox'
|
20
|
+
```
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
$ bundle install
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
### transform_CSV_to_HR_XML
|
29
|
+
|
30
|
+
```
|
31
|
+
Usage: transform_CSV_to_HR_XML [options] csv_file
|
32
|
+
-o, --output [xml_file_output] The file in which the the XML data will be writen (defaults to <csv_file>.xml)
|
33
|
+
-s [separator_character], The character that separates each column of the CSV file.
|
34
|
+
--separator
|
35
|
+
-q, --quote [quote_character] The character used to quote fields.
|
36
|
+
-e, --email [email_recipients] Email recipient list that will receive job report status.
|
37
|
+
-u, --username [username] The username used to authenticate to the HR REST API.
|
38
|
+
-p, --password [password] The password used to authenticate to the HR REST API.
|
39
|
+
-l, --url [url] The full URL of the HR REST API; e.g. https://localhost/kc-dev/hr-import/hrimport/import
|
40
|
+
-h, --help Display this screen
|
41
|
+
```
|
42
|
+
> Note: Please be sure to use the [Account_Provisioning_CSV_Template.xlsx](https://github.com/KualiCo/kuali_toolbox/raw/master/Account_Provisioning_CSV_Template.xlsx) template with this tool.
|
43
|
+
|
44
|
+
### validate_HR_XML
|
45
|
+
|
46
|
+
```
|
47
|
+
Usage: validate_HR_XML xml_file
|
48
|
+
-h, --help Display this screen
|
49
|
+
```
|
50
|
+
|
51
|
+
## Contributing
|
52
|
+
|
53
|
+
1. Fork it: https://github.com/KualiCo/kuali_toolbox/fork
|
54
|
+
2. Create your feature branch: `git checkout -b my-new-feature`
|
55
|
+
3. Commit your changes: `git commit -am 'Add some feature'`
|
56
|
+
4. Push to the branch: `git push origin my-new-feature`
|
57
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# KualiCo's client library and command-line tool to help interact with KualiCo's cloud APIs.
|
2
|
+
# Copyright (C) 2014-2015 KualiCo, Inc.
|
3
|
+
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU Affero General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU Affero General Public License for more details.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU Affero General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require "bundler/gem_tasks"
|
18
|
+
require "rspec/core/rake_task"
|
19
|
+
|
20
|
+
task :default => [:spec]
|
21
|
+
task :test => [:spec]
|
22
|
+
|
23
|
+
desc "Run the specs."
|
24
|
+
RSpec::Core::RakeTask.new do |t|
|
25
|
+
t.pattern = "spec/**/*_spec.rb"
|
26
|
+
end
|
@@ -0,0 +1,332 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
require 'builder'
|
7
|
+
require 'csv'
|
8
|
+
require 'optparse'
|
9
|
+
require 'rest_client'
|
10
|
+
require 'time'
|
11
|
+
require 'kuali_toolbox/etl/grm'
|
12
|
+
|
13
|
+
ETL = KualiCo::ETL
|
14
|
+
GRM = KualiCo::ETL::GRM
|
15
|
+
TextParseError = KualiCo::ETL::TextParseError
|
16
|
+
|
17
|
+
def self.parse_command_line_options(
|
18
|
+
executable, args, opt={ csv_options: { headers: :first_row,
|
19
|
+
header_converters: :symbol,
|
20
|
+
skip_blanks: true,
|
21
|
+
col_sep: ",", # comma by default
|
22
|
+
quote_char: '"', # double quote by default
|
23
|
+
}
|
24
|
+
} )
|
25
|
+
optparse = OptionParser.new do |opts|
|
26
|
+
opts.banner = "Usage: #{executable} [options] csv_file"
|
27
|
+
opts.on( '-o [xml_file_output]' ,'--output [xml_file_output]', 'The file in which the the XML data will be writen (defaults to <csv_file>.xml)') do |f|
|
28
|
+
opt[:xml_filename] = f
|
29
|
+
end
|
30
|
+
opts.on( '-s [separator_character]' ,'--separator [separator_character]', 'The character that separates each column of the CSV file.' ) do |s|
|
31
|
+
opt[:col_sep] = s
|
32
|
+
end
|
33
|
+
opts.on( '-q [quote_character]' ,'--quote [quote_character]', 'The character used to quote fields.' ) do |q|
|
34
|
+
opt[:quote_char] = q
|
35
|
+
end
|
36
|
+
opts.on( '-e [email_recipients]', '--email [email_recipients]', 'Email recipient list that will receive job report status.' ) do |e|
|
37
|
+
opt[:email_recipients] = e
|
38
|
+
end
|
39
|
+
opts.on( '-u [username]', '--username [username]', 'The username used to authenticate to the HR REST API.' ) do |u|
|
40
|
+
opt[:username] = u
|
41
|
+
end
|
42
|
+
opts.on( '-p [password]', '--password [password]', 'The password used to authenticate to the HR REST API.' ) do |p|
|
43
|
+
opt[:password] = p
|
44
|
+
end
|
45
|
+
opts.on( '-l [url]', '--url [url]', 'The full URL of the HR REST API; e.g. https://localhost/kc-dev/hr-import/hrimport/import' ) do |l|
|
46
|
+
opt[:url] = l
|
47
|
+
end
|
48
|
+
opts.on( '-h', '--help', 'Display this screen' ) do
|
49
|
+
puts opts
|
50
|
+
exit 1
|
51
|
+
end
|
52
|
+
|
53
|
+
opt[:csv_filename] = args[0] unless opt[:csv_filename]
|
54
|
+
if opt[:csv_filename].nil? || opt[:csv_filename].empty?
|
55
|
+
puts opts
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
end
|
59
|
+
optparse.parse!
|
60
|
+
|
61
|
+
# construct a sensible default ouptput filename
|
62
|
+
unless opt[:xml_filename]
|
63
|
+
file_extension = File.extname opt[:csv_filename]
|
64
|
+
dir_name = File.dirname opt[:csv_filename]
|
65
|
+
base_name = File.basename opt[:csv_filename], file_extension
|
66
|
+
opt[:xml_filename] = "#{dir_name}/#{base_name}.xml"
|
67
|
+
end
|
68
|
+
|
69
|
+
unless opt[:email_recipients]
|
70
|
+
opt[:email_recipients] = "no-reply@kuali.co"
|
71
|
+
end
|
72
|
+
|
73
|
+
if opt[:url]
|
74
|
+
unless opt[:username] && opt[:password]
|
75
|
+
raise ArgumentError, "Username and password are required when POSTing to a URL!"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
return opt
|
80
|
+
end
|
81
|
+
|
82
|
+
opt = parse_command_line_options (File.basename $0), ARGF.argv
|
83
|
+
|
84
|
+
text_parse_errors = []
|
85
|
+
|
86
|
+
CSV.open(opt[:csv_filename], opt[:csv_options]) do |csv|
|
87
|
+
record_count = csv.readlines.count
|
88
|
+
csv.rewind # go back to first row
|
89
|
+
|
90
|
+
File.open(opt[:xml_filename], 'w') do |xml_file|
|
91
|
+
xml = Builder::XmlMarkup.new target: xml_file, indent: 2
|
92
|
+
xml.instruct! :xml, encoding: "UTF-8"
|
93
|
+
xml.hrmanifest "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
94
|
+
"xsi:schemaLocation" => "https://github.com/KualiCo/ce-tech-docs/tree/master/v1_0 https://raw.github.com/KualiCo/ce-tech-docs/master/v1_0/hrmanifest.xsd",
|
95
|
+
xmlns: "https://github.com/KualiCo/ce-tech-docs/tree/master/v1_0",
|
96
|
+
schemaVersion: "1.0",
|
97
|
+
statusEmailRecipient: opt[:email_recipients],
|
98
|
+
reportDate: Time.now.iso8601,
|
99
|
+
recordCount: record_count do |hrmanifest|
|
100
|
+
hrmanifest.records do |record|
|
101
|
+
csv.find_all do |row| # begin processing csv rows
|
102
|
+
begin
|
103
|
+
xml.record principalId: GRM.parse_principal_id( row[:prncpl_id] ),
|
104
|
+
principalName: GRM.parse_principal_name( row[:prncpl_nm] ) do |record|
|
105
|
+
record.affiliations do |affiliations|
|
106
|
+
aff = {}
|
107
|
+
afltn_typ_cd = ETL.parse_string row[:afltn_typ_cd], name: 'AFLTN_TYP_CD', length: 40, required: true
|
108
|
+
campus = ETL.parse_string row[:campus_cd], name: 'CAMPUS_CD', length: 2
|
109
|
+
aff[:affiliationType] = afltn_typ_cd unless afltn_typ_cd.empty?
|
110
|
+
aff[:campus] = campus unless campus.empty?
|
111
|
+
aff[:default] = true
|
112
|
+
aff[:active] = true
|
113
|
+
|
114
|
+
affiliations.affiliation aff do |affiliation|
|
115
|
+
emp = {}
|
116
|
+
emp_stat_cd = GRM.parse_emp_stat_cd row[:emp_stat_cd]
|
117
|
+
emp_typ_cd = GRM.parse_emp_typ_cd row[:emp_typ_cd]
|
118
|
+
base_slry_amt = ETL.parse_float row[:base_slry_amt], length: 15, name: 'BASE_SLRY_AMT'
|
119
|
+
prmry_dept_cd = ETL.parse_string row[:prmry_dept_cd], length: 40, name: 'PRMRY_DEPT_CD'
|
120
|
+
emp_id = ETL.parse_string row[:emp_id], length: 40, name: 'EMP_ID'
|
121
|
+
emp[:employeeStatus] = emp_stat_cd unless emp_stat_cd.empty?
|
122
|
+
emp[:employeeType] = emp_typ_cd unless emp_typ_cd.empty?
|
123
|
+
emp[:baseSalaryAmount] = base_slry_amt unless base_slry_amt.nil?
|
124
|
+
emp[:primaryDepartment] = prmry_dept_cd unless prmry_dept_cd.empty?
|
125
|
+
emp[:employeeId] = emp_id unless emp_id.empty?
|
126
|
+
emp[:primaryEmployment] = true
|
127
|
+
|
128
|
+
affiliation.employment emp
|
129
|
+
end
|
130
|
+
end # affiliations
|
131
|
+
|
132
|
+
address = {}
|
133
|
+
addr_typ_cd = GRM.parse_address_type_code( row[:addr_typ_cd] )
|
134
|
+
addr_line_1 = ETL.parse_string( row[:addr_line_1], name: 'ADDR_LINE_1', length: 45 )
|
135
|
+
addr_line_2 = ETL.parse_string( row[:addr_line_2], name: 'ADDR_LINE_2', length: 45 )
|
136
|
+
addr_line_3 = ETL.parse_string( row[:addr_line_3], name: 'ADDR_LINE_3', length: 45 )
|
137
|
+
city = ETL.parse_string( row[:city], name: 'CITY', length: 30 )
|
138
|
+
state_pvc_cd = ETL.parse_string( row[:state_pvc_cd], name: 'STATE_PVC_CD', length: 2 )
|
139
|
+
postal_cd = ETL.parse_string( row[:postal_cd], name: 'POSTAL_CD', length: 20 )
|
140
|
+
postal_cntry_cd = ETL.parse_string( row[:postal_cntry_cd], name: 'POSTAL_CNTRY_CD', length: 2 )
|
141
|
+
address[:addressTypeCode] = addr_typ_cd unless addr_typ_cd.empty?
|
142
|
+
address[:addressLine1] = addr_line_1 unless addr_line_1.empty?
|
143
|
+
address[:addressLine2] = addr_line_2 unless addr_line_2.empty?
|
144
|
+
address[:addressLine3] = addr_line_3 unless addr_line_3.empty?
|
145
|
+
address[:city] = city unless city.empty?
|
146
|
+
address[:stateOrProvince] = state_pvc_cd unless state_pvc_cd.empty?
|
147
|
+
address[:postalCode] = postal_cd unless postal_cd.empty?
|
148
|
+
address[:country] = postal_cntry_cd unless postal_cntry_cd.empty?
|
149
|
+
unless address.empty?
|
150
|
+
record.addresses do |addresses|
|
151
|
+
addresses.address address
|
152
|
+
end # addresses
|
153
|
+
end
|
154
|
+
|
155
|
+
record.names do |names|
|
156
|
+
nm = {}
|
157
|
+
nm_typ_cd = GRM.parse_name_code row[:nm_typ_cd]
|
158
|
+
prefix_nm = GRM.parse_prefix row[:prefix_nm]
|
159
|
+
first_nm = ETL.parse_string row[:first_nm], length: 40, name: 'FIRST_NM'
|
160
|
+
middle_nm = ETL.parse_string row[:middle_nm], length: 40, name: 'MIDDLE_NM'
|
161
|
+
last_nm = ETL.parse_string row[:last_nm], length: 80, name: 'LAST_NM'
|
162
|
+
suffix_nm = GRM.parse_suffix row[:suffix_nm]
|
163
|
+
title_nm = ETL.parse_string row[:title_nm], length: 20, name: 'TITLE_NM'
|
164
|
+
nm[:nameCode] = nm_typ_cd unless nm_typ_cd.empty?
|
165
|
+
nm[:prefix] = prefix_nm unless prefix_nm.empty?
|
166
|
+
nm[:firstName] = first_nm unless first_nm.empty?
|
167
|
+
nm[:middleName] = middle_nm unless middle_nm.empty?
|
168
|
+
nm[:lastName] = last_nm unless last_nm.empty?
|
169
|
+
nm[:suffix] = suffix_nm unless suffix_nm.empty?
|
170
|
+
nm[:title] = title_nm unless title_nm.empty?
|
171
|
+
nm[:default] = true
|
172
|
+
nm[:active] = true
|
173
|
+
|
174
|
+
names.name nm
|
175
|
+
end # names
|
176
|
+
|
177
|
+
ph = {}
|
178
|
+
phone_typ_cd = GRM.parse_phone_type row[:phone_typ_cd]
|
179
|
+
phone_nbr = GRM.parse_phone_number row[:phone_nbr]
|
180
|
+
phone_extn_nbr = ETL.parse_string row[:phone_extn_nbr], length: 8, name: 'PHONE_EXTN_NBR'
|
181
|
+
phone_cntry_cd = ETL.parse_string row[:phone_cntry_cd], length: 2, name: 'PHONE_CNTRY_CD'
|
182
|
+
ph[:phoneType] = phone_typ_cd unless phone_typ_cd.empty?
|
183
|
+
ph[:phoneNumber] = phone_nbr unless phone_nbr.empty?
|
184
|
+
ph[:extension] = phone_extn_nbr unless phone_extn_nbr.empty?
|
185
|
+
ph[:country] = phone_cntry_cd unless phone_cntry_cd.empty?
|
186
|
+
ph[:default] = true
|
187
|
+
ph[:active] = true
|
188
|
+
|
189
|
+
unless phone_typ_cd.empty? || phone_nbr.empty?
|
190
|
+
record.phones do |phones|
|
191
|
+
phones.phone ph
|
192
|
+
end # phones
|
193
|
+
end
|
194
|
+
|
195
|
+
em = {}
|
196
|
+
email_typ_cd = GRM.parse_email_type( row[:email_typ_cd] )
|
197
|
+
email_addr = GRM.parse_email_address( row[:email_addr] )
|
198
|
+
em[:emailType] = email_typ_cd unless email_typ_cd.empty?
|
199
|
+
em[:emailAddress] = email_addr unless email_addr.empty?
|
200
|
+
em[:default] = true
|
201
|
+
em[:active] = true
|
202
|
+
|
203
|
+
unless email_typ_cd.empty? || email_addr.empty?
|
204
|
+
record.emails do |emails|
|
205
|
+
emails.email em unless email_addr.empty?
|
206
|
+
end # emails
|
207
|
+
end
|
208
|
+
|
209
|
+
ea = {}
|
210
|
+
visa_type = ETL.parse_string( row[:visa_type], length: 30, name: 'VISA_TYPE' )
|
211
|
+
county = ETL.parse_string( row[:county], length: 30, name: 'COUNTY' )
|
212
|
+
age_by_fiscal_year = ETL.parse_integer( row[:age_by_fiscal_year], length: 3, name: 'AGE_BY_FISCAL_YEAR' )
|
213
|
+
race = ETL.parse_string( row[:race], length: 30, name: 'RACE' )
|
214
|
+
education_level = ETL.parse_string( row[:education_level], length: 30, name: 'EDUCATION_LEVEL' )
|
215
|
+
degree = GRM.parse_degree( row[:degree], name: 'DEGREE' )
|
216
|
+
major = ETL.parse_string( row[:major], length: 30, name: 'MAJOR' )
|
217
|
+
is_handicapped = ETL.parse_boolean( row[:is_handicapped], name: 'IS_HANDICAPPED' )
|
218
|
+
handicap_type = ETL.parse_string( row[:handicap_type], length: 30, name: 'HANDICAP_TYPE' )
|
219
|
+
is_veteran = ETL.parse_boolean( row[:is_veteran], name: 'IS_VETERAN' )
|
220
|
+
veteran_type = ETL.parse_string( row[:veteran_type], length: 30, name: 'VETERAN_TYPE' )
|
221
|
+
has_visa = ETL.parse_boolean( row[:has_visa], name: 'HAS_VISA' )
|
222
|
+
visa_code = ETL.parse_string( row[:visa_code], length: 20, name: 'VISA_CODE' )
|
223
|
+
visa_renewal_date = ETL.parse_string( row[:visa_renewal_date], length: 19, name: 'VISA_RENEWAL_DATE' )
|
224
|
+
office_location = ETL.parse_string( row[:office_location], length: 30, name: 'OFFICE_LOCATION' )
|
225
|
+
secondry_office_location = ETL.parse_string( row[:secondry_office_location], length: 30, name: 'SECONDRY_OFFICE_LOCATION' )
|
226
|
+
school = ETL.parse_string( row[:school], length: 50, name: 'SCHOOL' )
|
227
|
+
year_graduated = GRM.parse_year( row[:year_graduated], name: 'YEAR_GRADUATED' )
|
228
|
+
directory_department = ETL.parse_string( row[:directory_department], length: 30, name: 'DIRECTORY_DEPARTMENT' )
|
229
|
+
directory_title = ETL.parse_string( row[:directory_title], length: 50, name: 'DIRECTORY_TITLE' )
|
230
|
+
primary_title = ETL.parse_string( row[:primary_title], length: 51, name: 'PRIMARY_TITLE' )
|
231
|
+
vacation_accural = ETL.parse_boolean( row[:vacation_accural], name: 'VACATION_ACCURAL' )
|
232
|
+
is_on_sabbatical = ETL.parse_boolean( row[:is_on_sabbatical], name: 'IS_ON_SABBATICAL' )
|
233
|
+
id_provided = ETL.parse_string( row[:id_provided], length: 30, name: 'ID_PROVIDED' )
|
234
|
+
id_verified = ETL.parse_string( row[:id_verified], length: 30, name: 'ID_VERIFIED' )
|
235
|
+
citizenship_type_code = GRM.parse_citizenship_type( row[:citizenship_type_code] )
|
236
|
+
multi_campus_principal_id = ETL.parse_string( row[:multi_campus_principal_id], length: 40, name: 'MULTI_CAMPUS_PRINCIPAL_ID' )
|
237
|
+
multi_campus_principal_name = ETL.parse_string( row[:multi_campus_principal_name], length: 100, name: 'MULTI_CAMPUS_PRINCIPAL_NAME' )
|
238
|
+
salary_anniversary_date = ETL.parse_string( row[:salary_anniversary_date], length: 10, name: 'SALARY_ANNIVERSARY_DATE' )
|
239
|
+
ea[:visaType] = visa_type unless visa_type.empty?
|
240
|
+
ea[:county] = county unless county.empty?
|
241
|
+
ea[:ageByFiscalYear] = age_by_fiscal_year unless age_by_fiscal_year.nil?
|
242
|
+
ea[:race] = race unless race.empty?
|
243
|
+
ea[:educationLevel] = education_level unless education_level.empty?
|
244
|
+
ea[:degree] = degree unless degree.empty?
|
245
|
+
ea[:major] = major unless major.empty?
|
246
|
+
ea[:handicapped] = is_handicapped unless is_handicapped.nil?
|
247
|
+
ea[:handicapType] = handicap_type unless handicap_type.empty?
|
248
|
+
ea[:veteran] = is_veteran unless is_veteran.nil?
|
249
|
+
ea[:veteranType] = veteran_type unless veteran_type.empty?
|
250
|
+
ea[:visa] = has_visa unless has_visa.nil?
|
251
|
+
ea[:visaCode] = visa_code unless visa_code.empty?
|
252
|
+
ea[:visaRenewalDate] = visa_renewal_date unless visa_renewal_date.empty?
|
253
|
+
ea[:officeLocation] = office_location unless office_location.empty?
|
254
|
+
ea[:secondaryOfficeLocation] = secondry_office_location unless secondry_office_location.empty?
|
255
|
+
ea[:school] = school unless school.empty?
|
256
|
+
ea[:yearGraduated] = year_graduated unless year_graduated.empty?
|
257
|
+
ea[:directoryDepartment] = directory_department unless directory_department.empty?
|
258
|
+
ea[:directoryTitle] = directory_title unless directory_title.empty?
|
259
|
+
ea[:primaryTitle] = primary_title unless primary_title.empty?
|
260
|
+
ea[:vacationAccrual] = vacation_accural unless vacation_accural.nil?
|
261
|
+
ea[:onSabbatical] = is_on_sabbatical unless is_on_sabbatical.nil?
|
262
|
+
ea[:idProvided] = id_provided unless id_provided.empty?
|
263
|
+
ea[:idVerified] = id_verified unless id_verified.empty?
|
264
|
+
ea[:citizenshipType] = citizenship_type_code unless citizenship_type_code.empty?
|
265
|
+
ea[:multiCampusPrincipalId] = multi_campus_principal_id unless multi_campus_principal_id.empty?
|
266
|
+
ea[:multiCampusPrincipalName] = multi_campus_principal_name unless multi_campus_principal_name.empty?
|
267
|
+
ea[:salaryAnniversaryDate] = salary_anniversary_date unless salary_anniversary_date.empty?
|
268
|
+
|
269
|
+
record.kcExtendedAttributes ea
|
270
|
+
|
271
|
+
ap = {}
|
272
|
+
unit_number = ETL.parse_string( row[:unit_number], length: 8, name: 'UNIT_NUMBER' )
|
273
|
+
appointment_type_code = ETL.parse_string( row[:appointment_type_code], length: 3, name: 'APPOINTMENT_TYPE_CODE' )
|
274
|
+
job_code = ETL.parse_string( row[:job_code], length: 6, name: 'JOB_CODE' )
|
275
|
+
salary = ETL.parse_float( row[:salary], length: 15, name: 'SALARY' )
|
276
|
+
appointment_start_date = ETL.parse_string( row[:appointment_start_date], name: 'APPOINTMENT_START_DATE' )
|
277
|
+
appointment_end_date = ETL.parse_string( row[:appointment_end_date], name: 'APPOINTMENT_END_DATE' )
|
278
|
+
job_title = ETL.parse_string( row[:job_title], length: 50, name: 'JOB_TITLE' )
|
279
|
+
prefered_job_title = ETL.parse_string( row[:prefered_job_title], length: 51, name: 'PREFERED_JOB_TITLE' )
|
280
|
+
ap[:unitNumber] = unit_number unless unit_number.empty?
|
281
|
+
ap[:appointmentType] = appointment_type_code unless appointment_type_code.empty?
|
282
|
+
ap[:jobCode] = job_code unless job_code.empty?
|
283
|
+
ap[:salary] = salary unless salary.nil?
|
284
|
+
ap[:startDate] = appointment_start_date unless appointment_start_date.empty?
|
285
|
+
ap[:endDate] = appointment_end_date unless appointment_end_date.empty?
|
286
|
+
ap[:jobTitle] = job_title unless job_title.empty?
|
287
|
+
ap[:preferedJobTitle] = prefered_job_title unless prefered_job_title.empty?
|
288
|
+
|
289
|
+
unless unit_number.empty? || job_code.empty?
|
290
|
+
record.appointments do |appointments|
|
291
|
+
appointments.appointment ap
|
292
|
+
end # appointments
|
293
|
+
end
|
294
|
+
end # record
|
295
|
+
|
296
|
+
rescue TextParseError => e
|
297
|
+
puts e.message
|
298
|
+
text_parse_errors.push e
|
299
|
+
end
|
300
|
+
end # row
|
301
|
+
end # record
|
302
|
+
end # hrmanifest
|
303
|
+
end # xml_file
|
304
|
+
end # csv
|
305
|
+
|
306
|
+
def number_of_errors(parse_errors_array)
|
307
|
+
num_errors = 0
|
308
|
+
unless parse_errors_array.empty?
|
309
|
+
parse_errors_array.each do |err|
|
310
|
+
num_errors += 1 if err.message.start_with? "ERROR"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
return num_errors
|
314
|
+
end
|
315
|
+
|
316
|
+
num_errors = number_of_errors text_parse_errors
|
317
|
+
if num_errors > 0
|
318
|
+
puts "\n#{num_errors} errors found and must be corrected.\n\n"
|
319
|
+
File.unlink opt[:xml_filename]
|
320
|
+
exit 1
|
321
|
+
end
|
322
|
+
|
323
|
+
puts "\nXML file written to #{opt[:xml_filename]}\n\n"
|
324
|
+
|
325
|
+
exit 1 unless GRM.validate_hr_xml opt[:xml_filename]
|
326
|
+
|
327
|
+
# POST the XML file to the server if opt[:url]
|
328
|
+
if opt[:url]
|
329
|
+
resource = RestClient::Resource.new( opt[:url], opt[:username], opt[:password] )
|
330
|
+
resource.post file: File.new(opt[:xml_filename], 'rt'), content_type: 'multipart/form-data', multipart: true
|
331
|
+
puts "\n"
|
332
|
+
end
|
data/bin/validate_HR_XML
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require 'kuali_toolbox/etl/grm'
|
8
|
+
|
9
|
+
GRM = KualiCo::ETL::GRM
|
10
|
+
|
11
|
+
def self.parse_command_line_options( executable, args, opt={} )
|
12
|
+
optparse = OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: #{executable} xml_file"
|
14
|
+
opts.on( '-h', '--help', 'Display this screen' ) do
|
15
|
+
puts opts
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
|
19
|
+
opt[:xml_filename] = args[0] unless opt[:xml_filename]
|
20
|
+
if opt[:xml_filename].nil? || opt[:xml_filename].empty?
|
21
|
+
puts opts
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
optparse.parse!
|
26
|
+
|
27
|
+
return opt
|
28
|
+
end
|
29
|
+
|
30
|
+
opt = parse_command_line_options (File.basename $0), ARGF.argv
|
31
|
+
|
32
|
+
exit 1 unless GRM.validate_hr_xml opt[:xml_filename]
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# KualiCo's client library and command-line tool to help interact with KualiCo's cloud APIs.
|
2
|
+
# Copyright (C) 2014-2015 KualiCo, Inc.
|
3
|
+
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU Affero General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU Affero General Public License for more details.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU Affero General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
# coding: utf-8
|
18
|
+
lib = File.expand_path('../lib', __FILE__)
|
19
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
20
|
+
require 'kuali_toolbox/version'
|
21
|
+
|
22
|
+
Gem::Specification.new do |spec|
|
23
|
+
spec.name = "kuali_toolbox"
|
24
|
+
spec.version = KualiCo::VERSION
|
25
|
+
spec.authors = ["Lance Speelmon"]
|
26
|
+
spec.email = ["lspeelmon@kuali.co"]
|
27
|
+
spec.summary = %q{Client library and command-line tools to help interact with KualiCo's cloud APIs.}
|
28
|
+
# spec.description = %q{TODO: Write a longer description. Optional.}
|
29
|
+
spec.homepage = "http://kualico.github.io/kuali_toolbox/"
|
30
|
+
spec.metadata = { "issue_tracker" => "https://github.com/KualiCo/kuali_toolbox/issues" }
|
31
|
+
spec.license = "AGPL-3.0"
|
32
|
+
|
33
|
+
spec.files = `git ls-files -z`.split("\x0")
|
34
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
35
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
36
|
+
spec.require_paths = ["lib"]
|
37
|
+
|
38
|
+
spec.add_runtime_dependency 'builder', '~> 3.2.2'
|
39
|
+
spec.add_runtime_dependency 'nokogiri', '~> 1.6.3.1'
|
40
|
+
spec.add_runtime_dependency 'rest-client', '~> 1.7.2'
|
41
|
+
|
42
|
+
spec.required_ruby_version = '>= 1.9'
|
43
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
44
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
45
|
+
spec.add_development_dependency "rspec", "~> 3.0.0"
|
46
|
+
# spec.add_development_dependency "simplecov"
|
47
|
+
spec.add_development_dependency "codeclimate-test-reporter"
|
48
|
+
end
|