adding basic program to convert from Apollo LDF to staging tables
[migration-tools.git] / apollo / apollo_exporter.pl
diff --git a/apollo/apollo_exporter.pl b/apollo/apollo_exporter.pl
new file mode 100755 (executable)
index 0000000..f1bc568
--- /dev/null
@@ -0,0 +1,671 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use Getopt::Long;
+use XML::LibXML;
+use Switch;
+use open ':encoding(utf8)';
+
+select(STDERR);
+$| = 1;
+select(STDOUT); 
+$| = 1;
+
+my $mig_schema;
+my $ldif_xml;
+
+my $ret = GetOptions(
+    'ldif_xml:s'     => \$ldif_xml
+);
+if (!defined $ldif_xml) {$ldif_xml = 'export.xml'};
+
+print "Parsing LDIF ... ";
+my $parser = XML::LibXML->new();
+my $dom = $parser->parse_file($ldif_xml);
+print "done.\n\n";
+
+user_defined_values($dom,'user_defined_values.tsv');
+asset_copy_locations($dom,'asset_copy_location_legacy.tsv');
+actor_usr_groups($dom,'actor_usr_legacy_groups.tsv');
+actor_usr($dom,'actor_usr_legacy.tsv','actor_usr_address_legacy.tsv','actor_usr_phones.tsv','actor_usr_note_legacy.tsv','money_billing_legacy.tsv');
+biblio_record_entry($dom,'biblio_record_entry_legacy.tsv');
+action_uses($dom,'action_in_house_use_legacy.tsv','in-library'); 
+action_uses($dom,'action_circulation_legacy.tsv','circulation');
+action_uses($dom,'action_transit_copy_legacy.tsv','transit');
+action_uses($dom,'action_hold_request_shelf.tsv','reserve');
+action_hold_request($dom,'action_hold_request_legacy.tsv');
+asset_copy($dom,'asset_copy_legacy.tsv');
+asset_copy_note($dom,'asset_copy_note_legacy.tsv');
+
+print "Staging tables conversion completed.\n\n";
+
+#################################################################
+sub user_defined_values {
+    my $dom = shift;
+    my $outfile = shift;
+
+    print "Parsing for user defined values... ";
+    my @user_defined; 
+    foreach my $def ($dom->findnodes('//userdef')) {
+        push @user_defined, {
+            id          => $def->findvalue('./@id'),
+            record_type => $def->findvalue('./@record'),
+            name        => $def->findvalue('./@name'),
+            type        => $def->findvalue('./@type')
+        };  
+    }
+   
+    my $num_rows = @user_defined;
+        
+    open my $out_fh, ">:utf8", $outfile or die "can not open $outfile file for writing.\n";
+
+    print "writing $outfile ... ";
+
+    print $out_fh "id\trecord_type\tname\ttype\n";
+
+    foreach my $built_hash( @user_defined ) { 
+        print $out_fh "$built_hash->{id}\t";
+        print $out_fh "$built_hash->{record_type}\t";
+        print $out_fh "$built_hash->{name}\t";
+        print $out_fh "$built_hash->{type}\n";
+    }   
+
+    close $out_fh;
+    print "$num_rows rows written plus headers.\n";
+}
+
+sub asset_copy_note {
+    my $dom = shift;
+    my $outfile = shift;
+
+    print "Parsing for holdings values... ";
+    my @holdings;
+    foreach my $copy ($dom->findnodes('//holding')) {
+        if (length($copy->findvalue('./@deleted')) > 1) { next; }
+        foreach my $note ($copy->findnodes('.//holdingNote')) {
+            push @holdings, {
+                copy_id => $copy->findvalue('./@id'),
+                active  => $note->findvalue('./@active'),
+                note    => $note->findvalue('./@note')
+            };
+        }
+    }
+
+    my $num_rows = @holdings;
+
+    open my $out_fh, ">:utf8", $outfile or die "can not open $outfile file for writing.\n";
+    print "writing $outfile ... ";
+    print $out_fh "copy_id\tactive\tnote\n";
+    foreach my $built_hash( @holdings ) {
+        print $out_fh "$built_hash->{copy_id}\t";
+        print $out_fh "$built_hash->{active}\t";
+        print $out_fh "$built_hash->{note}\n";
+    }
+
+    close $out_fh;
+    print "$num_rows rows written plus headers.\n";
+}
+
+sub asset_copy {
+    my $dom = shift;
+    my $outfile = shift;
+
+    print "Parsing for holdings values... ";
+    my @holdings;
+    foreach my $copy ($dom->findnodes('//holding')) {
+        if (length($copy->findvalue('./@deleted')) > 1) { next; }
+        my $memberships;
+        foreach my $mship ($copy->findnodes('./membership')) {
+            if (defined $memberships) { $memberships = $memberships . '|' . $mship->to_literal; }
+                else { $memberships = $mship->to_literal; }
+        }
+        push @holdings, {
+            id           => $copy->findvalue('./@id'),
+            memberships  => $memberships,
+            location     => $copy->findvalue('./@location'),
+            deleted_type => $copy->findvalue('./@deletedType'),
+            status       => $copy->findvalue('./@status'),
+            deleted      => $copy->findvalue('./@deleted'),
+            barcode      => $copy->findvalue('./@barcode'),
+            biblio       => $copy->findvalue('./@biblio'),
+            usage_count  => $copy->findvalue('./@usageCount'),
+            price_cents  => $copy->findvalue('./@priceCents'),
+            call         => $copy->findvalue('./@call'),
+            edited       => $copy->findvalue('./@edited')
+        }; 
+    }
+  
+    my $num_rows = @holdings;
+
+    open my $out_fh, ">:utf8", $outfile or die "can not open $outfile file for writing.\n";
+    print "writing $outfile ... ";
+    print $out_fh "id\tmemberships\tlocation\tdeleted_type\tstatus\tdeleted\tbarcode\tbiblio\tusage_count\tprice_cents\tcall\tedited\n";
+    foreach my $built_hash( @holdings ) {
+        print $out_fh "$built_hash->{id}\t";
+        print $out_fh "$built_hash->{memberships}\t";
+        print $out_fh "$built_hash->{location}\t";
+        print $out_fh "$built_hash->{deleted_type}\t";
+        print $out_fh "$built_hash->{status}\t";
+        print $out_fh "$built_hash->{deleted}\t";
+        print $out_fh "$built_hash->{barcode}\t";
+        print $out_fh "$built_hash->{biblio}\t";
+        print $out_fh "$built_hash->{usage_count}\t";
+        print $out_fh "$built_hash->{price_cents}\t";
+        print $out_fh "$built_hash->{call}\t";
+        print $out_fh "$built_hash->{edited}\n";
+    }  
+
+    close $out_fh;
+    print "$num_rows rows written plus headers.\n";
+}
+
+sub action_hold_request {
+    my $dom = shift;
+    my $out_file = shift;
+
+    print "Parsing users data for hold requests ... ";
+    my @holds;
+    foreach my $patron ($dom->findnodes('///patron')) {
+        foreach my $request ($patron->findnodes('.//reserve')) {
+            if (length($request->findvalue('./@resolved')) > 1) { next; }
+            push @holds, {   #patron id, reserve id should be redundant but leaving in for error checking
+                id => $request->findvalue('./@id'),
+                patron_barcode => $patron->findvalue('./@barcode'),
+                patron_id => $patron->findvalue('./@id'),
+                biblio => $request->findvalue('./@biblio'),
+                status => $request->findvalue('./@status'),
+                resolved => $request->findvalue('./@resolved'),
+                placed => $request->findvalue('./@placed'),
+            };
+        }
+    }
+
+    my $num_rows = @holds;
+    open my $out_fh, ">:utf8", $out_file or die "can not open $out_file file for writing.\n";
+    print "writing $out_file ... \n";
+    print $out_fh "id\tpatron_barcode\tpatron_id\tbiblio\tstatus\tresolved\tplaced\n";
+    foreach my $built_hash( @holds ) {
+        print $out_fh "$built_hash->{id}\t";
+        print $out_fh "$built_hash->{patron_barcode}\t";
+        print $out_fh "$built_hash->{patron_id}\t";
+        print $out_fh "$built_hash->{biblio}\t";
+        print $out_fh "$built_hash->{status}\t";
+        print $out_fh "$built_hash->{resolved}\t";
+        print $out_fh "$built_hash->{placed}\n";
+    }
+    close $out_fh;
+    print "$num_rows rows written plus headers to $out_file.\n";
+}
+
+sub asset_copy_locations {
+    my $dom = shift;
+    my $outfile = shift;
+
+    print "Parsing for copy locations... ";
+    my @copy_locations;  
+    foreach my $membershiplist ($dom->findnodes('///holdingMembershipList')) {
+        my $membershiplist_id = $membershiplist->findvalue('./@id'); 
+        my $membershiplist_name = $membershiplist->findvalue('./@name');
+        foreach my $membership ($membershiplist->findnodes('./holdingMembership')) {
+            push @copy_locations, {
+                membership_list_id   => $membershiplist_id,
+                membership_list_name => $membershiplist_name,
+                membership_number    => $membership->findvalue('./@number'),
+                membership_name      => $membership->findvalue('./@name'),
+                membership_id        => $membership->findvalue('./@id')
+            };
+        }
+    } 
+
+    my $num_rows = @copy_locations;
+    
+    open my $out_fh, ">:utf8", $outfile or die "can not open $outfile file for writing.\n";
+
+    print "writing $outfile ... ";
+
+    #print headers
+    print $out_fh "membership_list_id\tmembership_list_name\tmembership_id\tmembership_number\tmembership_name\n";
+
+    foreach my $built_hash( @copy_locations ) {
+        print $out_fh "$built_hash->{membership_list_id}\t";
+        print $out_fh "$built_hash->{membership_list_name}\t";
+        print $out_fh "$built_hash->{membership_number}\t";
+        print $out_fh "$built_hash->{membership_name}\t";
+        print $out_fh "$built_hash->{membership_id}\n";
+    }
+
+    close $out_fh;
+    print "$num_rows rows written plus headers.\n";
+}
+
+
+sub action_uses {
+    my $dom = shift;
+    my $outfile = shift;
+    my $table_type = shift;
+
+    switch ($table_type) {
+        case 'transit'      {print "Parsing for copy transits ... ";}
+        case 'in-library'   {print "Parsing for in house uses ... ";}
+        case 'circulation'  {print "Parsing for circulations ... ";}
+    }
+
+    my @checkouts;  #not exporting the memberships since we have the patron id to link
+    foreach my $circ ($dom->findnodes('//checkout')) {
+        my $renewals = 0;
+        if ($table_type eq 'circulation') {
+            if ($circ->findvalue('./@type') ne 'normal') { next; }
+            if (length($circ->findvalue('./@due')) < 1) { next; } #bunch of records with no due dates, cause not clear
+            if (length($circ->findvalue('./@returned')) > 0) { next; }
+        }
+        if ($table_type eq 'transit') {
+            if ($circ->findvalue('./@type') ne 'transt') { next; }
+            if (length($circ->findvalue('./@returned')) > 0) { next; }
+        }
+        if ($table_type eq 'in-library') {
+            if ($circ->findvalue('./@type') ne 'in-library') { next; }
+        }
+        if ($table_type eq 'reserve') {
+            if ($circ->findvalue('./@type') ne 'reserve') { next; } 
+            if ($circ->findvalue('./@status') ne 'out') { next; }
+        }
+
+        foreach ($circ->findnodes('./renewal')) { 
+            $renewals++;  #normally don't move move over previous circs in renewal sequence so just counting them
+        }
+        push @checkouts, {
+            id                  => $circ->findvalue('./@id'),
+            patron              => $circ->findvalue('./@patron'),
+            out                 => $circ->findvalue('./@out'),
+            due                 => $circ->findvalue('./@due'),
+            status              => $circ->findvalue('./@status'),
+            holding             => $circ->findvalue('./@holding'),
+            returned            => $circ->findvalue('./@returned'),
+            type                => $circ->findvalue('./@type'),
+            out_location        => $circ->findvalue('./@outLocation'),
+            returned_location   => $circ->findvalue('./@returnedLocation'),
+            reserve_id          => $circ->findvalue('./@reserveId'),
+            self_check          => $circ->findvalue('./@selfCheck'),
+            renewals            => $renewals
+        };  
+    }   
+
+    my $num_rows = @checkouts;
+            
+    open my $out_fh, ">:utf8", $outfile or die "can not open $outfile file for writing.\n";
+
+    print "writing $outfile ... ";
+    
+    print $out_fh "id\tpatron\tout\tdue\tstatus\tholding\treturned\ttype\tout_location\treturned_location\treserve_id\tself_check\trenewals\n";
+
+    foreach my $built_hash( @checkouts ) { 
+        print $out_fh "$built_hash->{id}\t";
+        print $out_fh "$built_hash->{patron}\t";
+        print $out_fh "$built_hash->{out}\t";
+        print $out_fh "$built_hash->{due}\t";
+        print $out_fh "$built_hash->{status}\t";
+        print $out_fh "$built_hash->{holding}\t";
+        print $out_fh "$built_hash->{returned}\t";
+        print $out_fh "$built_hash->{type}\t";
+        print $out_fh "$built_hash->{out_location}\t";
+        print $out_fh "$built_hash->{returned_location}\t";
+        print $out_fh "$built_hash->{reserve_id}\t";
+        print $out_fh "$built_hash->{self_check}\t";
+        print $out_fh "$built_hash->{renewals}\n";
+    }   
+    close $out_fh;
+    print "$num_rows rows written plus headers.\n";
+
+}
+
+sub actor_usr_groups {
+    my $dom = shift;
+    my $outfile = shift;
+
+    print "Parsing for user groups... ";
+    my @user_groups;  
+    foreach my $membershiplist ($dom->findnodes('///patronMembershipList')) {
+
+        my $membershiplist_id = $membershiplist->findvalue('./@id'); 
+        my $membershiplist_name = $membershiplist->findvalue('./@name');
+        foreach my $membership ($membershiplist->findnodes('./patronMembership')) {
+            push @user_groups, {
+                membership_list_id   => $membershiplist_id,
+                membership_list_name => $membershiplist_name,
+                membership_length_months    => $membership->findvalue('./@membershipLengthMonths'),
+                membership_name      => $membership->findvalue('./@name'),
+                membership_id        => $membership->findvalue('./@id'),
+                membership_number    => $membership->findvalue('./@number')
+            };  
+        }   
+    }   
+
+    my $num_rows = @user_groups;
+        
+    open my $out_fh, ">:utf8", $outfile or die "can not open $outfile file for writing.\n";
+
+    print "writing $outfile ... ";
+
+    print $out_fh "membership_list_id\tmembership_list_name\tmembership_id\tmembership_number\tmembership_name\tmembership_length_months\n";
+
+    foreach my $built_hash( @user_groups ) { 
+        print $out_fh "$built_hash->{membership_list_id}\t";
+        print $out_fh "$built_hash->{membership_list_name}\t";
+        print $out_fh "$built_hash->{membership_number}\t";
+        print $out_fh "$built_hash->{membership_name}\t";
+        print $out_fh "$built_hash->{membership_id}\t";
+        print $out_fh "$built_hash->{membership_length_months}\n";
+    }
+
+    close $out_fh;
+    print "$num_rows rows written plus headers.\n";
+}
+
+sub actor_usr {
+    my $dom = shift;
+    my $out_user_file = shift;
+    my $out_address_file = shift;
+    my $out_phone_file = shift;
+    my $out_note_file = shift;
+    my $out_fine_file = shift;
+
+    my @users; 
+    my @user_addresses;
+    my @user_phones;
+    my @user_notes;
+    my @patron_fines;
+
+    print "Parsing users for addresses, patrons, fines, notes, etc... \n";
+    foreach my $patron ($dom->findnodes('///patron')) {
+        my $first_names;
+        my $middle_names;
+        my $preferred_names;
+        my $birthdates;
+        foreach my $f_name ($patron->findnodes('.//firstName')) {
+            if (defined $first_names) { $first_names = $first_names . '|' . $f_name->findvalue('./@name'); }
+                else { $first_names = $f_name->findvalue('./@name'); }
+            if (defined $birthdates) { $birthdates = $birthdates . '|' . $f_name->findvalue('./@birthdate'); }
+                else { $birthdates = $f_name->findvalue('./@birthdate'); }
+            if (defined $middle_names) { $middle_names = $middle_names . '|' . $f_name->findvalue('./@middleName'); }
+                else { $middle_names = $f_name->findvalue('./@middleName'); }
+            if (defined $preferred_names) { $preferred_names = $preferred_names . '|' . $f_name->findvalue('./@preferredName'); }
+                else { $preferred_names = $f_name->findvalue('./@preferredName'); }
+        }       
+        my $memberships;
+        foreach my $mship ($patron->findnodes('.//membership')) {
+            if (defined $memberships) { $memberships = $memberships . '|' . $mship->to_literal; }
+                else { $memberships = $mship->to_literal; }
+        }
+        my $emails;  
+        foreach my $email ($patron->findnodes('.//email')) {
+            if (defined $emails) { $emails = $emails . ',' . $email->findvalue('./@address'); }
+                else { $emails = $email->findvalue('./@address'); }
+        }  
+        #lazy code, do programatically later
+        my $u3710;
+        my $u3714;
+        foreach my $ud ($patron->findnodes('./userdefVal')) {
+            if ($ud->findvalue('./@uID') eq 'u3710') { $u3710 = $ud->findvalue('./@value')};
+            if ($ud->findvalue('./@uID') eq 'u3714') { $u3714 = $ud->findvalue('./@value')};
+        } 
+        push @users, {
+            patron_barcode => $patron->findvalue('./@barcode'),
+            created => $patron->findvalue('./@created'),
+            usage_count => $patron->findvalue('./@usageCount'),
+            family_id => $patron->findvalue('./@familyID'),
+            expiration => $patron->findvalue('./@expiration'),
+            patron_id => $patron->findvalue('./@id'),
+            last_name => $patron->findvalue('./@lastName'),
+            edited => $patron->findvalue('./@edited'),
+            emails => $emails,
+            first_names => $first_names,
+            middle_names => $middle_names,
+            preferred_names => $preferred_names,
+            birthdates => $birthdates,
+            memberships => $memberships,
+            reserve_contact => $patron->findvalue('./reserveContactDefault/@contact'),
+            reserve_contact_sms => $patron->findvalue('./reserveContactDefault/@sms'),
+            overdue_contact => $patron->findvalue('./overdueContact/@contact'),
+            overdue_contact_sms => $patron->findvalue('./overdueContact/@sms'),
+            due_warning_contact => $patron->findvalue('./dueWarningContact/@contact'),
+            due_warning_contact_sms => $patron->findvalue('./dueWarningContact/@sms'),
+            internet => $u3714,
+            birthdate => $u3710
+        };
+        
+        my $lines;
+        foreach my $address ($patron->findnodes('.//address')) {
+            foreach my $add_line ($address->findnodes('./line')) {
+                if (defined $lines) { $lines = $lines . '|' . $add_line->to_literal; }
+                    else { $lines = $add_line->to_literal; }
+                }
+            push @user_addresses, {
+                patron_barcode => $patron->findvalue('./@barcode'),
+                patron_id => $patron->findvalue('./@id'),
+                lines => $lines,
+                locality => $address->findvalue('./@locality'),
+                country_division => $address->findvalue('./@countryDivision'),
+                country => $address->findvalue('./@country'),
+                postal_code => $address->findvalue('./@postalCode'),
+                mailing => $address->findvalue('./@mailing')
+            };
+        }
+
+        foreach my $phone ($patron->findnodes('.//phone')) {
+            push @user_phones, {
+                patron_barcode => $patron->findvalue('./@barcode'),
+                patron_id => $patron->findvalue('./@id'),
+                number => $phone->findvalue('./@number'),
+                area_code => $phone->findvalue('./@areaCode'),
+                type => $phone->findvalue('./@type'),  #should be home/work/mobile/other/none
+                phone_id => $phone->findvalue('./@id'),
+                country_code => $phone->findvalue('./@countryCode')
+            };
+        }
+
+        foreach my $note ($patron->findnodes('.//patronNote')) {
+            my $message = $note->findvalue('./@message');
+            $message =~ s/\\$//;
+            push @user_notes, {
+                patron_barcode => $patron->findvalue('./@barcode'),
+                patron_id => $patron->findvalue('./@id'),
+                active => $note->findvalue('./@active'),
+                signature => $note->findvalue('./@signature'),
+                urgent => $note->findvalue('./@urgent'),
+                sensitive => $note->findvalue('./@sensitive'),
+                date_added => $note->findvalue('./dateAdded'),
+                date_cleared => $note->findvalue('./dateCleared'),
+                last_updated => $note->findvalue('./dateUpdated'),
+                message => $message
+            };  
+        }   
+        
+        foreach my $fine ($patron->findnodes('.//fine')) {
+            push @patron_fines, {
+                patron_barcode => $patron->findvalue('./@barcode'),
+                patron_id => $patron->findvalue('./@id'),
+                fine_id => $fine->findvalue('./@id'), 
+                checkout => $fine->findvalue('./@checkout'),
+                amount_paid_cents => $fine->findvalue('./@amountPaidCents'),
+                status => $fine->findvalue('./@status'),
+                updated => $fine->findvalue('./@updated'),
+                amountCents=> $fine->findvalue('./@amountCents')
+            };
+        }
+
+    }
+
+    my $num_user_rows = @users;
+    open my $out_user_fh, ">:utf8", $out_user_file or die "can not open $out_user_file file for writing.\n";
+    print "writing $out_user_file ... \n";
+    print $out_user_fh "patron_id\tpatron_barcode\tcreated\tusage_count\tfamily_id\texpiration\tedited\tlast_name\tfirst_name\tmiddle_name\tpreferred_name\tbirthdates\temails\tmemberships\treserve_contact\treserve_contact_sms\toverdue_contact\toverdue_contact_sms\tdue_warning_contact\tdue_warning_sms\tbirthdate\tinternet\n";
+    foreach my $built_user_hash( @users ) {
+        print $out_user_fh "$built_user_hash->{patron_id}\t";
+        print $out_user_fh "$built_user_hash->{patron_barcode}\t";
+        print $out_user_fh "$built_user_hash->{created}\t";
+        print $out_user_fh "$built_user_hash->{usage_count}\t";
+        print $out_user_fh "$built_user_hash->{family_id}\t";
+        print $out_user_fh "$built_user_hash->{expiration}\t";
+        print $out_user_fh "$built_user_hash->{edited}\t";
+        print $out_user_fh "$built_user_hash->{last_name}\t";
+        print $out_user_fh "$built_user_hash->{first_names}\t"; #plural names indicate arrays seperated by pipe
+        print $out_user_fh "$built_user_hash->{middle_names}\t";
+        print $out_user_fh "$built_user_hash->{preferred_names}\t";
+        print $out_user_fh "$built_user_hash->{birthdates}\t";
+        print $out_user_fh "$built_user_hash->{emails}\t";
+        print $out_user_fh "$built_user_hash->{memberships}\t";
+        print $out_user_fh "$built_user_hash->{reserve_contact}\t";
+        print $out_user_fh "$built_user_hash->{reserve_contact_sms}\t";
+        print $out_user_fh "$built_user_hash->{overdue_contact}\t";
+        print $out_user_fh "$built_user_hash->{overdue_contact_sms}\t";
+        print $out_user_fh "$built_user_hash->{due_warning_contact}\t";
+        print $out_user_fh "$built_user_hash->{due_warning_contact_sms}\t";
+        print $out_user_fh "$built_user_hash->{birthdate}\t";
+        print $out_user_fh "$built_user_hash->{internet}\n";
+    } 
+    close $out_user_fh;
+    print "$num_user_rows rows written plus headers to $out_user_file.\n";
+
+    my $num_address_rows = @user_addresses;
+    open my $out_address_fh, ">:utf8", $out_address_file or die "can not open $out_address_file file for writing.\n";
+    print "writing $out_address_file ... \n";
+    print $out_address_fh "patron_id\tpatron_barcode\tlines\tlocality\tcountry_division\tcountry\tpostal_code\tmailing\n";
+    foreach my $built_address_hash( @user_addresses ) { 
+        print $out_address_fh "$built_address_hash->{patron_id}\t";
+        print $out_address_fh "$built_address_hash->{patron_barcode}\t";
+        print $out_address_fh "$built_address_hash->{lines}\t";
+        print $out_address_fh "$built_address_hash->{locality}\t";
+        print $out_address_fh "$built_address_hash->{country_division}\t";
+        print $out_address_fh "$built_address_hash->{country}\t";
+        print $out_address_fh "$built_address_hash->{postal_code}\t";
+        print $out_address_fh "$built_address_hash->{mailing}\n";
+    }   
+    close $out_address_fh;
+    print "$num_address_rows rows written plus headers to $out_address_file.\n";
+
+    my $num_phone_rows = @user_phones;
+    open my $out_phone_fh, ">:utf8", $out_phone_file or die "can not open $out_phone_file file for writing.\n";
+    print "writing $out_phone_file ... \n";
+    print $out_phone_fh "patron_id\tpatron_barcode\tphone_id\tnumber\tarea_code\ttype\tcountry_code\n";
+    foreach my $built_phone_hash( @user_phones ) {
+        print $out_phone_fh "$built_phone_hash->{patron_id}\t";
+        print $out_phone_fh "$built_phone_hash->{patron_barcode}\t";
+        print $out_phone_fh "$built_phone_hash->{phone_id}\t";
+        print $out_phone_fh "$built_phone_hash->{number}\t";
+        print $out_phone_fh "$built_phone_hash->{area_code}\t";
+        print $out_phone_fh "$built_phone_hash->{type}\t";
+        print $out_phone_fh "$built_phone_hash->{country_code}\n";
+    }
+    close $out_phone_fh;
+    print "$num_phone_rows rows written plus headers to $out_phone_file.\n";
+
+    my $num_note_rows = @user_notes;
+    open my $out_note_fh, ">:utf8", $out_note_file or die "can not open $out_note_file file for writing.\n";
+    print "writing $out_note_file ... \n";
+    print $out_note_fh "patron_id\tpatron_barcode\tactive\tsignature\turgent\tsensitive\tdate_added\tdate_cleared\tlast_updated\tmessage\n";
+    foreach my $built_note_hash( @user_notes ) {
+        print $out_note_fh "$built_note_hash->{patron_id}\t";
+        print $out_note_fh "$built_note_hash->{patron_barcode}\t";
+        print $out_note_fh "$built_note_hash->{active}\t";
+        print $out_note_fh "$built_note_hash->{signature}\t";
+        print $out_note_fh "$built_note_hash->{urgent}\t";
+        print $out_note_fh "$built_note_hash->{sensitive}\t";
+        print $out_note_fh "$built_note_hash->{date_added}\t";
+        print $out_note_fh "$built_note_hash->{date_cleared}\t";
+        print $out_note_fh "$built_note_hash->{last_updated}\t";
+        print $out_note_fh "$built_note_hash->{message}\n";
+    }
+    close $out_note_fh;
+    print "$num_note_rows rows written plus headers to $out_note_file.\n";
+
+    my $num_fine_rows = @patron_fines;
+    open my $out_fine_fh, ">:utf8", $out_fine_file or die "can not open $out_fine_file file for writing.\n";
+    print "writing $out_fine_file ... \n";
+    print $out_fine_fh "patron_id\tpatron_barcode\tfine_id\tcheckout\tamount_paid_cents\tstatus\tupdated\tamountCents\n";
+    foreach my $built_fine_hash( @patron_fines ) {
+        print $out_fine_fh "$built_fine_hash->{patron_id}\t";
+        print $out_fine_fh "$built_fine_hash->{patron_barcode}\t";
+        print $out_fine_fh "$built_fine_hash->{fine_id}\t";
+        print $out_fine_fh "$built_fine_hash->{checkout}\t";
+        print $out_fine_fh "$built_fine_hash->{amount_paid_cents}\t";
+        print $out_fine_fh "$built_fine_hash->{status}\t";
+        print $out_fine_fh "$built_fine_hash->{updated}\t";
+        print $out_fine_fh "$built_fine_hash->{amountCents}\n";
+    }
+    close $out_fine_fh;
+    print "$num_fine_rows rows written plus headers to $out_fine_file.\n";
+}
+
+sub biblio_record_entry {
+    my $dom = shift;
+    my $outfile = shift;
+
+    open my $out_fh, ">:utf8", $outfile or die "can not open $outfile file for writing.\n";
+    my $num_rows = 0;
+    print "Parsing for bib records...\n";
+    print $out_fh "id\tusage_count\tstatus\tadded\tedited\tmarc\n"; 
+    foreach my $bib ($dom->findnodes('///biblio')) {
+        my $id = $bib->findvalue('./@id');
+        my $usageCount = $bib->findvalue('./@usageCount');
+        my $status = $bib->findvalue('./@status');
+        my $added = $bib->findvalue('./@added');
+        my $edited = $bib->findvalue('./@edited');
+        my $marc = $bib->toString;
+        $marc =~ s/<marc:/</g;
+        $marc =~ s/<\/marc:/<\//g;
+        $marc =~ s/<bib.+?>//g;
+        $marc =~ s/<\/bib.+?>//g;
+        $marc =~ s/\R//g;
+        $marc =~ s/\s+</</g;
+        $marc =~ s/>\s+/>/g;
+        print $out_fh "$id\t";
+        print $out_fh "$usageCount\t";
+        print $out_fh "$status\t";
+        print $out_fh "$added\t";
+        print $out_fh "$edited\t";
+        print $out_fh "$marc\n"; 
+        $num_rows++;
+    }
+    close $out_fh;
+    print "$num_rows rows written plus headers.\n";
+}
+
+sub abort {
+    my $msg = shift;
+    print STDERR "$0: $msg", "\n";
+    print_usage();
+    exit 1;
+}
+
+   
+sub print_usage {
+    print <<_USAGE_;
+
+LDIF Exporter loads an ldif xml file version 032 into staging tables 
+to load into Evergreen.  This implementation assumes that the user 
+is using a distinct Postgres schema to stage tables from.  The Exporter
+creates linked tables in the schema to production tables that inherit 
+sequences and then child tables with data to manipulate.  
+
+For example: patron data will be loaded in m_foo.actor_usr which inherits
+from actor.usr and a m_foo.actor_usr_legacy is created.  The table is 
+prepopulated with likely data but legacy value is suppplied in the legacy 
+table in l_ so it can be non-destrutively manipulated.
+
+LDIF Exporter requires the following parameter:   
+
+  --mig_schema - the staging schema of the migration
+
+Optionally you may also provide a file with --ldif_xml 
+
+If --ldif_xml is not used then it will assume there is an export.xml file and
+fail if not found.
+
+
+
+_USAGE_
+}
+