Skip to content

Commit

Permalink
Add support for graduating undergraduate dual majors & relax program …
Browse files Browse the repository at this point in the history
…checks (#2282)

* Add support for graduating undergraduate dual majors

The undergraduate college and business school have launched a dual
major program that allows students to complete both a liberal arts
and a business undergraduate degree.

**ISSUE**
Emory would like these student to list themselves as being part of
the "Emory College" program, but the Registrar is showing their
graduation record associated with the "Undergraduate Business" school.

This means the application expects to find a graudation record like
`PPIDxxx-UCOL-BBA`, but the Registrar data provides `PPIDxxx-UBUS-BBA`.

There are other students graduating in "UCOL" programs, so we can't
just change the mapping; we have to add an additional check for this
specific case.

**SOLUTION**
This commit adds a secondary check when-and-only-when the student
has entered "Emory College" and a "B.B.A" degree that also scans
Registrar data for the same student and degree within the "Undergraduate
Business" program.

* Relax program/degree checks in the GraduationService

**ISSUE**
Emory has indicated that they would like to process graduation records
and ETD publication even if the student provided degree does not
exactly match the program data in the registrar data.

**CHANGES**
* When an exact match cannot be found, look for student and school matches
ignoring program & degree mismatches
* Use graduation status from exact matches when possible; otherwise, use
relaxed matches if present, and log the differing data
* Relax program checking for undergaduate dual degree candidates in the
same way
  • Loading branch information
mark-dce committed Jul 19, 2022
1 parent f20fd8f commit 29c505d
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 28 deletions.
51 changes: 32 additions & 19 deletions app/services/graduation_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,28 @@ def confirm_registrar_status(candidate_etds)

# Search registrar data for a student record with matching PPID, School, and Degree
# @param [Hash] etd_solr_doc - Solr doc hash for corresponding ETD record
# @return [String, Hash]
# @return Array[Time, Hash{String->String}]
# grad_date - ISO formatted date if the student has graduated;
# grad_record - the corresponding registrar record
def find_registrar_match(etd_solr_doc)
ppid = etd_solr_doc['depositor_ssim']&.first
school = SCHOOL_MAP[etd_solr_doc['school_tesim']&.first]
degree = DEGREE_MAP[etd_solr_doc['degree_tesim']&.first]
registrar_index = "#{ppid}-#{school}-#{degree}"

grad_record = @registrar_data[registrar_index] || { 'degree status descr' => 'Unmatched' }
ppid = etd_solr_doc['depositor_ssim']&.first || "no PPID present"
school = SCHOOL_MAP[etd_solr_doc['school_tesim']&.first] || "unrecognized school"
program = PROGRAM_MAP[etd_solr_doc['degree_tesim']&.first] || "unrecognized degree"
registrar_key = "#{ppid}-#{school}-#{program}"
dual_major_fragment = "#{ppid}-UBUS" # ignore program when checking for dual majors
school_fragment = "#{ppid}-#{school}" # check for student and school match

# find potential matches in the registrar data
exact_match = @registrar_data[registrar_key]
school_match = @registrar_data.select { |k, _v| k.match school_fragment }.values.last unless exact_match
dual_major_match = @registrar_data.select { |k, _v| k.match dual_major_fragment }.values.last if school == 'UCOL' && !(exact_match || school_match)

# use the closest match in order of priority
grad_record = exact_match || school_match || dual_major_match || { 'degree status descr' => 'Unmatched', 'etd record key' => registrar_key }

# extract the graduation date if a degree has been awarded
grad_date = extract_date(grad_record)
log_registrar_match(etd_solr_doc, registrar_index, grad_record, grad_date)
log_registrar_match(etd_solr_doc, registrar_key, grad_record, grad_date)
[grad_date, grad_record]
end

Expand All @@ -91,43 +101,46 @@ def extract_date(grad_record)
end

# Log status data to assist auditing and reporting on this run of the GraduationService
def log_registrar_match(etd_solr_doc, registrar_index, grad_record, grad_date)
def log_registrar_match(etd_solr_doc, registrar_key, grad_record, grad_date)
actual_key = grad_record['etd record key']

case grad_record['degree status descr']

# Exact match found with valid graduation date
when /Awarded/i
msg = "awarded\", graduation_date: \"#{grad_date}"
msg = "awarded\", graduation_date: #{grad_date.strftime('%Y-%m-%d')}"
msg += ", matched_key: #{actual_key}" if registrar_key != actual_key

# No match found in registrar data, look for similar records with matching PPID
when /Unmatched/i
msg = "PPID not found in registrar data"

# list any keys matching PPID for other schools
ppid = etd_solr_doc['depositor_ssim']&.first
id_matches = @registrar_data.select { |k, _v| k.match ppid }
msg = if id_matches.count > 0
"no match. Other records with matching PPID: " + id_matches.map { |_k, v| "#{v['etd record key']} (#{v['degree status date']})" }.join(', ')
else
"PPID not found in registrar data"
end
ppid_matches = @registrar_data.select { |k, _v| k.match ppid }.keys
msg += ", similar_keys: #{ppid_matches.inspect}" if ppid_matches.count > 0

# Match found in registrar data, but no graduation date present
else
msg = "graduation pending"
end

Rails.logger.warn "GraduationService: - ETD: #{etd_solr_doc['id']}, registrar_key: #{registrar_index}, msg: \"#{msg}\""
Rails.logger.warn "GraduationService: - ETD: #{etd_solr_doc['id']}, registrar_key: #{registrar_key}, msg: \"#{msg}\""
end

# Return a filtered version of the grad record that only includes data required for potential Proquest submission
def filter_address(grad_record)
grad_record.slice('home address 1', 'home address 2', 'home address 3', 'home address city', 'home address state', 'home address postal code', 'home address country code')
end

# DEGREE_MAP: Keys = Laevigata degree codes (degree_tesim); Values = corresponding Registrar academic program codes
# PROGRAM_MAP: Keys = Laevigata degree codes (degree_tesim); Values = corresponding Registrar academic program codes
# We're using program codes to match instead of degree codes because program codes are always present in registrar data
# degree codes extracted from live data which currently include both id and term values from https://github.com/curationexperts/laevigata/blob/main/config/authorities/degree.yml#L29
# "BA", "BBA", "BS", "CRG", "DM", "DNP", "MA", "MDP", "MDV", "MPH", "MRL", "MRPL", "MS", "MSN", "MSPH", "MT", "MTS", "PHD"
# academic program codes extracted from registrar_data*.json files:
# {"BA"=>"LIBAS", "BBA"=>"BBA", "BS"=>"LIBAS", "CRG"=>"CRGGS", "DM"=>"DM", "DNP"=>"DNP", "MA"=>"MA", "MDP"=>"MDP",
# "MDV"=>"MDV", "MPH"=>"MPH", "MRPL"=>"MRPL", "MS"=>"MS", "MSN"=>"MSN", "MSPH"=>"MSPH", "MT"=>"MT", "MTS"=>"MTS", "PHD"=>"PHD"}
DEGREE_MAP = {
PROGRAM_MAP = {
"B.A." => "LIBAS", "BA" => "LIBAS",
"B.B.A." => "BBA", "BBA" => "BBA",
"B.S." => "LIBAS", "BS" => "LIBAS",
Expand Down
13 changes: 7 additions & 6 deletions spec/fixtures/registrar_sample.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{"P0000001-GSAS-PHD":{"etd record key":"P0000001-GSAS-PHD","public person id":"P0000001","directory last name":"Doe","directory first name":"John","directory middle name":" ","preferred email address":"jdoe@example.com","home address 1":"123 Fake St","home address 2":" ","home address 3":" ","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N","acad career code":"GSAS","acad career descr":"School of Graduate Studies","acad program code":"PHD","acad program descr":"Doctor of Philosophy","primary acad plan code":"BBSPHD","primary acad plan descr":"Biological and Biomedical Sci.","program status descr":"AC"},
"P0000002-UCOL-LIBAS":{"etd record key":"P0000002-UCOL-LIBAS","public person id":"P0000002","directory last name":"Smith","directory first name":"Jane","directory middle name":"Cinderlla","preferred email address":"jsmith@example.com","home address 1":"321 Ash Way","home address 2":" ","home address 3":" ","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N","acad career code":"UCOL","acad career descr":"Undergraduate Emory College","acad program code":"LIBAS","acad program descr":"Liberal Arts & Sciences","primary acad plan code":"POLISCIBA","primary acad plan descr":"Political Science","secondary acad plan code":"LACSND","secondary acad plan descr":"Latin Amer. & Caribbean Stu.","program status descr":"AC","degree code":"BS","degree status descr":"Awarded","degree status date":"2017-05-18"},
"P0000003-UCOL-LIBAS":{"etd record key":"P0000003-UCOL-LIBAS","public person id":"P0000003","directory last name":"Hood","directory first name":"Riding","directory middle name":"Red","preferred email address":"rhood@example.com","home address 1":"12 Nana Ct","home address 2":"","home address 3":"","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N","acad career code":"UCOL","acad career descr":"Undergraduate Emory College","acad program code":"LIBAS","acad program descr":"Liberal Arts & Sciences","primary acad plan code":"MATHCSBS","primary acad plan descr":"Mathematics & Computer Science","program status descr":"CM","degree code":"BS","degree status descr":"Awarded","degree status date":"2017-03-16"},
"P0000004-THEO-MDV":{"etd record key":"P0000004-THEO-MDV","public person id":"P0000004","directory last name":"Smith","directory first name":"Jim","directory middle name":"James","preferred email address":"jim.s@example.com","home address 1":"123 Fake Dr","home address 2":"","home address 3":"","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N","acad career code":"THEO","acad career descr":"Theology","acad program code":"MDV","acad program descr":"Master of Divinity","primary acad plan code":"MDVDIVIN","primary acad plan descr":"Divinity","program status descr":"CM","degree code":"MDV","degree status descr":"Awarded","degree status date":"2018-01-12"},
"P0000004-THEO-THD":{"etd record key":"P0000004-THEO-THD","public person id":"P0000004","directory last name":"Smith","directory first name":"Jim","directory middle name":"James","preferred email address":"jim.s@example.com","home address 1":"123 Fake Dr","home address 2":"","home address 3":"","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N","acad career code":"THEO","acad career descr":"Theology","acad program code":"THD","acad program descr":"Doctor of Theology","primary acad plan code":"THDCOUNSEL","primary acad plan descr":"Pastoral Counseling","program status descr":"AC","degree code":"THD","degree status descr":"Awarded","degree status date":"2020-05-23"},
"P0000005-GSAS-PHD":{"etd record key":"P0000005-GSAS-PHD","public person id":"P0000005","directory last name":"Anderson","directory first name":"John","directory middle name":" ","preferred email address":"janders@example.com","home address 1":"123 Fake Dr","home address 2":"","home address 3":"","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N","acad career code":"GSAS","acad career descr":"School of Graduate Studies","acad program code":"PHD","acad program descr":"Doctor of Philosophy","primary acad plan code":"CHEMPHD","primary acad plan descr":"Chemistry","program status descr":"CM","degree status date":"2020-05-25","degree status descr":"Awarded"}
{"P0000001-GSAS-PHD": {"etd record key":"P0000001-GSAS-PHD", "public person id":"P0000001", "acad career code":"GSAS", "acad career descr":"School of Graduate Studies", "acad program code":"PHD", "acad program descr":"Doctor of Philosophy", "degree code":"PHD", "degree status date":" ", "degree status descr":" ", "primary acad plan code":"BBSPHD", "primary acad plan descr":"Biological and Biomedical Sci.", "program status descr":"AC", "secondary acad plan code":" ","secondary acad plan descr":" ", "directory last name":"Doe", "directory first name":"John","directory middle name":" ","preferred email address":"jdoe@example.com","home address 1":"123 Fake St","home address 2":" ","home address 3":" ","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N"},
"P0000002-UCOL-LIBAS":{"etd record key":"P0000002-UCOL-LIBAS","public person id":"P0000002", "acad career code":"UCOL", "acad career descr":"Undergraduate Emory College", "acad program code":"LIBAS", "acad program descr":"Liberal Arts & Sciences", "degree code":"BS", "degree status date":"2017-05-18", "degree status descr":"Awarded", "primary acad plan code":"POLISCIBA", "primary acad plan descr":"Political Science", "program status descr":"AC", "secondary acad plan code":"LACSND","secondary acad plan descr":"Latin Amer. & Caribbean Stu.", "directory last name":"Smith", "directory first name":"Jane","directory middle name":"Cinderlla","preferred email address":"jsmith@example.com","home address 1":"321 Ash Way","home address 2":" ","home address 3":" ","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N"},
"P0000003-UCOL-LIBAS":{"etd record key":"P0000003-UCOL-LIBAS","public person id":"P0000003", "acad career code":"UCOL", "acad career descr":"Undergraduate Emory College", "acad program code":"LIBAS", "acad program descr":"Liberal Arts & Sciences", "degree code":"BS", "degree status date":"2017-03-16", "degree status descr":"Awarded", "primary acad plan code":"MATHCSBS", "primary acad plan descr":"Mathematics & Computer Science", "program status descr":"CM", "secondary acad plan code":" ","secondary acad plan descr":" ", "directory last name":"Hood", "directory first name":"Riding","directory middle name":"Red","preferred email address":"rhood@example.com","home address 1":"12 Nana Ct","home address 2":"","home address 3":"","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N"},
"P0000004-THEO-MDV": {"etd record key":"P0000004-THEO-MDV", "public person id":"P0000004", "acad career code":"THEO", "acad career descr":"Theology", "acad program code":"MDV", "acad program descr":"Master of Divinity", "degree code":"MDV", "degree status date":"2018-01-12", "degree status descr":"Awarded", "primary acad plan code":"MDVDIVIN", "primary acad plan descr":"Divinity", "program status descr":"CM", "secondary acad plan code":" ","secondary acad plan descr":" ", "directory last name":"Smith", "directory first name":"Jim","directory middle name":"James","preferred email address":"jim.s@example.com","home address 1":"123 Fake Dr","home address 2":"","home address 3":"","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N"},
"P0000004-THEO-THD": {"etd record key":"P0000004-THEO-THD", "public person id":"P0000004", "acad career code":"THEO", "acad career descr":"Theology", "acad program code":"THD", "acad program descr":"Doctor of Theology", "degree code":"THD", "degree status date":"2020-05-23", "degree status descr":"Awarded", "primary acad plan code":"THDCOUNSEL", "primary acad plan descr":"Pastoral Counseling", "program status descr":"AC", "secondary acad plan code":" ","secondary acad plan descr":" ", "directory last name":"Smith", "directory first name":"Jim","directory middle name":"James","preferred email address":"jim.s@example.com","home address 1":"123 Fake Dr","home address 2":"","home address 3":"","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N"},
"P0000005-GSAS-PHD": {"etd record key":"P0000005-GSAS-PHD", "public person id":"P0000005", "acad career code":"GSAS", "acad career descr":"School of Graduate Studies", "acad program code":"PHD", "acad program descr":"Doctor of Philosophy", "degree code":"PHD", "degree status date":"2020-05-25", "degree status descr":"Awarded", "primary acad plan code":"CHEMPHD", "primary acad plan descr":"Chemistry", "program status descr":"CM", "secondary acad plan code":" ","secondary acad plan descr":" ", "directory last name":"Anderson", "directory first name":"John","directory middle name":" ","preferred email address":"janders@example.com","home address 1":"123 Fake Dr","home address 2":"","home address 3":"","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N"},
"P0000006-UBUS-BBA": {"etd record key":"P0000006-UBUS-BBA", "public person id":"P0000006", "acad career code":"UBUS", "acad career descr":"Undergraduate Business", "acad program code":"BBA", "acad program descr":"Bachelor of Business Admin", "degree code":"BBA", "degree status date":"2022-05-25", "degree status descr":"Awarded", "primary acad plan code":"BUSBBA", "primary acad plan descr":"Business Administration", "program status descr":"CM", "secondary acad plan code":"BUSMUSIC","secondary acad plan descr":"Music", "directory last name":"Dieu-le-Veut","directory first name":"Anne","directory middle name":"","preferred email address":"adv@example.com","home address 1":"50 Rue de Mer","home address 2":"","home address 3":"","home address city":"Atlanta","home address state":"GA","home address postal code":"30301","home address country code":"USA","home address country descr":"United States","ferpa suppression flag":"N"}
}
25 changes: 22 additions & 3 deletions spec/services/graduation_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,34 @@
end
end
end
describe "for undergrad business dual majors" do
let(:etd_solr_doc) { { 'id' => 'MatchingETD', 'depositor_ssim' => ['P0000006'], 'school_tesim' => ['Emory College'], 'degree_tesim' => ['B.B.A.'] } }
it 'accepts UBUS as relaxed match for UCOL' do
grad_date = grad_service.find_registrar_match(etd_solr_doc)[0]
expect(grad_date).to eq '2022-05-25'.to_time
end
end
describe "for program mis-matchess" do
let(:etd_solr_doc) { { 'id' => 'SameSchoolDifferentProgram', 'depositor_ssim' => ['P0000005'], 'school_tesim' => ['Laney Graduate School'], 'degree_tesim' => ['M.A.'] } }
it 'logs warning and graduates', :aggregate_failures do
allow(Rails.logger).to receive(:warn)
_grad_date, _grad_record = grad_service.find_registrar_match(etd_solr_doc)
expect(Rails.logger).to have_received(:warn).with(/SameSchoolDifferentProgram/)
expect(Rails.logger).to have_received(:warn).with(/registrar_key: P0000005-GSAS-MA/)
expect(Rails.logger).to have_received(:warn).with(/matched_key: P0000005-GSAS-PHD/)
expect(Rails.logger).to have_received(:warn).with(/graduation_date: 2020-05-25/)
end
end

describe "for non-matches" do
let(:etd_solr_doc) { { 'id' => 'UnmatchedETD', 'depositor_ssim' => ['P0000004'], 'school_tesim' => ['Emory College'], 'degree_tesim' => ['B.S.'] } }
it 'logs warnings for near matches with the same PPID', :aggregate_failures do
allow(Rails.logger).to receive(:warn)
_grad_date, _grad_record = grad_service.find_registrar_match(etd_solr_doc)
expect(Rails.logger).to have_received(:warn).with(/UnmatchedETD/)
expect(Rails.logger).to have_received(:warn).with(/P0000004-UCOL-LIBAS/)
expect(Rails.logger).to have_received(:warn).with(/P0000004-THEO-MDV \(2018-01-12\)/)
expect(Rails.logger).to have_received(:warn).with(/P0000004-THEO-THD \(2020-05-23\)/)
expect(Rails.logger).to have_received(:warn).with(/P0000004-THEO-MDV/)
expect(Rails.logger).to have_received(:warn).with(/P0000004-THEO-THD/)
end
it 'logs when no matches exist' do
etd_solr_doc['depositor_ssim'] = ['P1234567']
Expand All @@ -88,7 +107,7 @@
end
it 'returns a dummy registrar record' do
_grad_date, grad_record = grad_service.find_registrar_match(etd_solr_doc)
expect(grad_record).to eq({ 'degree status descr' => 'Unmatched' })
expect(grad_record).to include({ 'degree status descr' => 'Unmatched' })
end
end
end
Expand Down

0 comments on commit 29c505d

Please sign in to comment.