Skip to content

Instantly share code, notes, and snippets.

@laod
Last active December 14, 2015 06:29
Show Gist options
  • Save laod/5043194 to your computer and use it in GitHub Desktop.
Save laod/5043194 to your computer and use it in GitHub Desktop.
Code snippet from a ruby project that maps data from our directories and sis warehouse to csv
#!/usr/bin/ruby
require 'ldap'
require 'pg'
require 'csv'
# because empty isn't false, that's why
class String
def presence
self.empty? ? nil : self
end
end
# HACK: we want to pick from the available addresses in order. So make sure we get the requested
# field(s) for all of them PLUS line_1 which we will use to decide
def address_helper(fields)
(["line_1"] | [fields].flatten).map{|f| ['rma','local','home'].map{|a| "#{a}_address_#{f}"}}.flatten
end
# decide which address to use, expects the first three values from address_helper
# rma_address_line_1, local_address_line_1, home_address_line_1
def which(addrs)
addrs.index((addrs[0].presence || addrs[1].presence || addrs[2].presence)) || 0
end
# kill me
map = [
#general
{name: "Patient Control ID", value: nil},
{name: "SSN", value: nil},
{name: "Other ID", source: :ldap, source_name: "datatelid"},
{name: "Last Name", source: :ldap, source_name: "sn"},
{name: "First Name", source: :ldap, source_name: "givenName"},
{name: "Middle Initial", source: :ods, source_name: "middle", mapper: Proc.new {|v| v[0]}},
{name: "Sex", source_name: "gender", source: :ods},
{name: "Address", source: :ods, source_name: address_helper(["line_1","line_2"]), mapper: Proc.new{|v| t = which v[0..2]; [v[t], v[t+3]].reject(&:empty?).join " "}},
{name: "City", source_name: address_helper("city"), source: :ods, mapper: Proc.new{|v| v[which(v[0..2]) + 3]}},
{name: "State", source_name: address_helper("state"), source: :ods, mapper: Proc.new{|v| t = v[which(v[0..2]) + 3]; t.empty? ? 'NA' : t}},
{name: "Zip Code", source_name: address_helper("zip"), source: :ods, mapper: Proc.new{|v| t = v[which(v[0..2]) + 3]; t.delete '-'}},
{name: "Home Phone", value: nil},
{name: "Work Phone", source_name: "officePhoneNumber", source: :ldap, mapper: Proc.new{|v| v.gsub /[^0-9]/, ""}},
{name: "Cell Phone", source_name: "cell_phone", source: :ods, mapper: Proc.new{|v| v.gsub /[^0-9]/, ""}},
{name: "Date of Birth", source_name: "birthday", source: :ods, mapper: Proc.new {|v| [1,2,0].map{|i| v.split('-')[i]}.join('/')}},
{name: "Marital Status", source_name: "marital_status", source: :ods, mapper: Proc.new {|v| {"M" => 1, "S" => 2, "" => 7}[v] }},
{name: "Employment", source_name: "employment_status_code", source: :ods, mapper: Proc.new {|v| {'FT' => 1, 'PT' => 2, 'AF' => 7, 'VF' => 7, 'T' => 7, '7' => 7, '' => 7}[v]}},
{name: "Employer Code", value: "TU"},
{name: "Email Address", source: :ldap, source_name: "primaryMail"},
{name: "Eligibility", value: 2},
# students
{name: "Campus Address", source_name: "rma_address_line_1", source: :ods},
{name: "Permanent Address", source: :ods, source_name: "home_address_line_1"},
{name: "Permanent City", source_name: "home_address_city", source: :ods},
{name: "Permanent State", source_name: "home_address_state", source: :ods},
{name: "Permanent Zip Code", source_name: "home_address_zip", source: :ods, mapper: Proc.new{|v| v.delete '-'}},
{name: "Permanent Country", source_name: "home_address_country", source: :ods},
{name: "Permanent Phone", source_name: "home_phone", source: :ods, mapper: Proc.new{|v| v.gsub /[^0-9]/, ""}},
{name: "Foreign Student", source_name: "foreign_student", source: :ods, mapper: Proc.new{|v| ['','true'].index v}},
{name: "Visa Type", source_name: "visa_type", source: :ods},
{name: "Major", source: :ods, source_name: "major1"},
{name: "Standing", source_name: "academic_level", source: :ods},
{name: "Class", source_name: "class", source: :ods},
{name: "Enrollment Date", source_name: "start_term", source: :ods},
{name: "Student Status", source_name: "course_load", source: :ods},
{name: "School", value: "TU"},
{name: "Residency", source_name: "room_assignment", source: :ods, mapper: Proc.new {|v| v.empty? ? 2 : 1}},
# misc
{name: "Race", value: 8},
{name: "Ethnicity", value: nil},
{name: "EmerName", source_name: "emergency_contact_name", source: :ods},
{name: "EmerPhone1", source_name: "emergency_contact_phone", source: :ods, mapper: Proc.new{|v| v.gsub /[^0-9]/, ""}},
{name: "EmerRelationship", value: nil},
]
# get the list of ldap attrs we want and ensure it contains datatelid
@attrs = map.select{|f| f[:source] == :ldap}
.map{|f| f[:source_name] || f[:name]} | ['datatelid']
SECRETS!!1
def lookup(uid)
l = @dir.search2 @base, LDAP::LDAP_SCOPE_SUBTREE, 'uid='+uid, @attrs
# TODO: blow up if more than one
r = {}
r[:ldap] = l[0].each{|k,v| l[0][k] = v[0]}
r[:ods] = {}
res = @vault.exec("select did,name,value from namevalue where did='#{r[:ldap]['datatelid']}'")
res.each do |i|
r[:ods][i['name']] = i['value']
end
p r
r
end
# HACK: I hate single-value arrays
class Array
def value_or_values
self.count == 1 ? self.first : self
end
end
# comparable to Reg Braithwaite's Object#into, tells an object to pass itself to
# a block for chaining goodness.
class Object
def mutate
yield(self)
end
end
CSV.open("medicat.psv", "wb", {col_sep: "|", row_sep: "\r\n", headers: true}) do |csv|
csv << map.map {|f| f[:name]}
ARGF.each do |uid|
uid.strip!
p uid
r = lookup uid
csv << map.map do |field|
if field.include? :value
field[:value]
else
# name(s) -> value(s) -> transformed value(s) -- driven by the map array
# HACK: [].flatten turns singles into arrays without messing arrays up
# would have been easier but gross to regularize on arrays
[(field[:source_name] || field[:name])].flatten
.map{|f| r[field[:source]][f] || ''}.value_or_values
.mutate{|o| (field[:mapper] || Proc.new {|v| v}).call o}
end
end.map{|i| i == "" ? nil : i}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment