]> git.evergreen-ils.org Git - Evergreen.git/blob - Evergreen/src/extras/import/parse_patron_xml.pl
data cleansing
[Evergreen.git] / Evergreen / src / extras / import / parse_patron_xml.pl
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
4
5 use lib '/openils/lib/perl5';
6
7 use OpenSRF::System;
8 use OpenSRF::EX qw/:try/;
9 use OpenSRF::AppSession;
10 use OpenSRF::Utils::SettingsClient;
11 use OpenILS::Utils::Fieldmapper;
12 use Digest::MD5 qw/md5_hex/;
13 use Getopt::Long;
14 use JSON;
15 use DateTime;
16 use Time::HiRes qw/time/;
17 use XML::LibXML;
18
19 my ($file,$config,$profileid,$identtypeid,$default_profile,$profile_map,$usermap) =
20         ('return_file_0623-2.xml', '/openils/conf/bootstrap.conf', 1, 1, 1, 'profile.map');
21
22 GetOptions(
23         'usermap=s'        => \$usermap,
24         'file=s'        => \$file,
25         'config=s'      => \$config,
26         'default_profile=i'      => \$default_profile,
27         'profile_map=s'      => \$profile_map,
28         'profile_statcat_id=i'      => \$profileid,
29         'identtypeid=i'      => \$identtypeid,
30 );
31
32 my %u_map;
33 if ($usermap) {
34         open F, $usermap;
35         while (my $line = <F>) {
36                 chomp($line);
37                 my ($b,$i) = split(/\|/, $line);
38                 $b =~ s/^\s*(\S+)\s*$/$1/o;
39                 $i =~ s/^\s*(\S+)\s*$/$1/o;
40                 $u_map{$b} = $i;
41         }
42         close F;
43 }
44
45 my %p_map;
46 if ($profile_map) {
47         open F, $profile_map;
48         while (my $line = <F>) {
49                 chomp($line);
50                 my ($b,$i) = split(/\|/, $line);
51                 $b =~ s/^\s*(\S+)\s*$/$1/o;
52                 $i =~ s/^\s*(\S+)\s*$/$1/o;
53                 $p_map{$b} = $i;
54         }
55         close F;
56 }
57
58 my $doc = XML::LibXML->new->parse_file($file);
59
60 OpenSRF::System->bootstrap_client( config_file => $config );
61 Fieldmapper->import(IDL => OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
62
63 my $cstore = OpenSRF::AppSession->create( 'open-ils.cstore' );
64
65 my $profiles = $cstore->request(
66                 'open-ils.cstore.direct.permission.grp_tree.search.atomic',
67                 { id => { '!=' => undef } },
68 )->gather(1);
69
70 my $orgs = $cstore->request(
71                 'open-ils.cstore.direct.actor.org_unit.search.atomic',
72                 { id => { '!=' => undef } },
73 )->gather(1);
74
75 $profiles = { map { ($_->name => $_->id) } @$profiles };
76 $orgs = { map { ($_->shortname => $_->id) } @$orgs };
77
78 my $starttime = time;
79 my $count = 1;
80 for my $patron ( $doc->documentElement->childNodes ) {
81         my $p = new Fieldmapper::actor::user;
82         my $card = new Fieldmapper::actor::card;
83         my $profile_sce = new Fieldmapper::actor::stat_cat_entry_user_map;
84
85         my $old_profile = $patron->findvalue( 'user_profile' );
86
87         my $bc = $patron->findvalue( 'user_id' );
88
89         next unless $bc;
90
91         my $uid;
92         if (keys %u_map) {
93                 $uid = $u_map{$bc};
94                 unless ($uid) {
95                         $count++;
96                         next;
97                 }
98         } else {
99                 next;
100         }
101
102         unless ($uid > 1) {
103                 $count++;
104                 next;
105         }
106         
107         $card->barcode( $bc );
108         $card->usr( $uid );
109         $card->active( 't' );
110
111         $p->id( $uid );
112         $p->usrname( $bc );
113         $p->passwd( $patron->findvalue( 'user_pin' ) );
114
115         my $new_profile = $p_map{$old_profile};
116         unless ($new_profile) {
117                 $count++;
118                 next;
119         }
120
121         $p->profile( $$profiles{$new_profile} || $default_profile );
122
123         # some defaults
124         $p->standing(1);
125         $p->active('t');
126         $p->master_account('f');
127         $p->super_user('f');
128         $p->usrgroup($uid);
129         $p->claims_returned_count(0);
130         $p->credit_forward_balance(0);
131         $p->last_xact_id('IMPORT-'.$starttime);
132
133         $p->barred('f');
134         $p->barred('t') if ( $patron->findvalue( 'user_status' ) eq 'BARRED' );
135
136         $p->ident_type( $identtypeid );
137         my $id_val = $patron->findvalue( 'user_altid' );
138         $p->ident_value( $id_val ) if ($id_val);
139
140         my ($fname,$mname,$lname) = ($patron->findvalue('first_name'),$patron->findvalue('middle_name'),$patron->findvalue('last_name'));
141
142         $fname =~ s/^\s*//o;
143         $mname =~ s/^\s*//o;
144         $lname =~ s/^\s*//o;
145
146         $fname =~ s/\s*$//o;
147         $mname =~ s/\s*$//o;
148         $lname =~ s/\s*$//o;
149
150         $p->first_given_name( $fname );
151         $p->second_given_name( $mname );
152         $p->family_name( $lname );
153
154         $p->day_phone( $patron->findvalue( 'Address/dayphone' ) );
155         $p->evening_phone( $patron->findvalue( 'Address/homephone' ) );
156         $p->other_phone( $patron->findvalue( 'Address/workphone' ) );
157         $p->email( $patron->findvalue( 'email' ) );
158
159         my $hlib = $$orgs{$patron->findvalue( 'user_library' )};
160         unless ($hlib) {
161                 $count++;
162                 next;
163         }
164         $p->home_ou( $hlib );
165
166         $p->dob( parse_date( $patron->findvalue( 'birthdate' ) ) );
167         $p->create_date( parse_date( $patron->findvalue( 'user_priv_granted' ) ) );
168         $p->expire_date( parse_date( $patron->findvalue( 'user_priv_expires' ) ) );
169
170         $p->alert_message("Legacy Import Message: old profile was FIXME")
171                 if ($old_profile eq 'FIXME');
172
173         my $net_access = 1;
174         $net_access = 2 if ($old_profile =~ /^U.I/o);
175         $net_access = 3 if ($old_profile =~ /^X.I/o);
176
177         $p->net_access_level( $net_access );
178
179         $profile_sce->target_usr( $uid );
180         $profile_sce->stat_cat( $profileid );
181         $profile_sce->stat_cat_entry( $old_profile );
182
183         my @addresses;
184         my $mailing_addr_id = $patron->findvalue( 'user_mailingaddr' );
185
186         for my $addr ( $patron->findnodes( "Address" ) ) {
187                 my $prefix = 'coa_';
188
189                 my $line1 = $addr->findvalue( "${prefix}line1" );
190                 $prefix = 'std_' if (!$line1);
191
192                 $line1 = $addr->findvalue( "${prefix}line1" );
193                 next unless ($line1);
194
195                 my $a = new Fieldmapper::actor::user_address;
196                 $a->usr( $uid );
197                 $a->street1( $line1 );
198                 $a->street2( $addr->findvalue( "${prefix}line2" ) );
199                 $a->city( $addr->findvalue( "${prefix}city" ) );
200                 $a->state( $addr->findvalue( "${prefix}state" ) );
201                 $a->post_code(
202                         $addr->findvalue( "${prefix}zip" ) .
203                         '-' . $addr->findvalue( "${prefix}zip4" )
204                 );
205                 
206                 $a->valid( 'f' );
207                 $a->valid( 't' ) if ($prefix eq 'std_');
208                 
209                 $a->within_city_limits( 'f' );
210                 $a->country('USA');
211
212                 if ($addr->getAttribute('addr_type') == $mailing_addr_id) {
213                         $a->address_type( 'LEGACY MAILING' );
214                 } else {
215                         $a->address_type( 'LEGACY' );
216                 }
217
218                 push @addresses, $a;
219
220                 if ($prefix eq 'coa_') {
221                         $prefix = 'std_';
222
223                         $line1 = $addr->findvalue( "${prefix}line1" );
224                         next unless ($line1);
225
226                         $a = new Fieldmapper::actor::user_address;
227                         $a->usr( $uid );
228                         $a->street1( $line1 );
229                         $a->street2( $addr->findvalue( "${prefix}line2" ) );
230                         $a->city( $addr->findvalue( "${prefix}city" ) );
231                         $a->state( $addr->findvalue( "${prefix}state" ) );
232                         $a->post_code(
233                                 $addr->findvalue( "${prefix}zip" ) .
234                                 '-' . $addr->findvalue( "${prefix}zip4" )
235                         );
236                 
237                         $a->valid( 'f' );
238                         $a->valid( 't' ) if ($prefix eq 'std_');
239                 
240                         $a->within_city_limits( 'f' );
241                         $a->country('USA');
242
243                         $a->address_type( 'LEGACY' );
244
245                         push @addresses, $a;
246                 }
247         }
248
249         my @notes;
250         for my $note_field ( qw#note comment voter bus_school Address/phone1 Address/phone2# ) {
251                 for my $note ( $patron->findnodes( $note_field) ) {
252                         my $a = new Fieldmapper::actor::usr_note;
253
254                         $a->creator(1);
255                         $a->create_date('now');
256                         $a->usr( $uid );
257                         $a->title( "Legacy ".$note->localName );
258                         $a->value( $note->textContent );
259                         $a->pub( 'f' );
260                         push @notes, $a;
261                 }
262         }
263
264         print STDERR "\r$count     ".$count/(time - $starttime) unless ($count % 100);
265         print JSON->perl2JSON( $_ )."\n" for ($p,$card,$profile_sce,@addresses,@notes);
266
267         $count++;
268 }
269
270 print STDERR "\n";
271
272
273 sub parse_date {
274         my $string = shift;
275         my $group = shift;
276
277         my ($y,$m,$d);
278
279         if ($string eq 'NEVER') {
280                 my (undef,undef,undef,$d,$m,$y) = localtime();
281                 return sprintf('%04d-%02d-%02d', $y + 1920, $m + 1, $d);
282         } elsif (length($string) == 8 && $string =~ /^(\d{4})(\d{2})(\d{2})$/o) {
283                 ($y,$m,$d) = ($1,$2,$3);
284         } elsif ($string =~ /(\d+)\D(\d+)\D(\d+)/o) { #looks like it's parsable
285                 if ( length($3) > 2 )  { # looks like mm.dd.yyyy
286                         if ( $1 < 99 && $2 < 99 && $1 > 0 && $2 > 0 && $3 > 0) {
287                                 if ($1 > 12 && $1 < 31 && $2 < 13) { # well, actually it looks like dd.mm.yyyy
288                                         ($y,$m,$d) = ($3,$2,$1);
289                                 } elsif ($2 > 12 && $2 < 31 && $1 < 13) {
290                                         ($y,$m,$d) = ($3,$1,$2);
291                                 }
292                         }
293                 } elsif ( length($1) > 3 ) { # format probably yyyy.mm.dd
294                         if ( $3 < 99 && $2 < 99 && $1 > 0 && $2 > 0 && $3 > 0) {
295                                 if ($2 > 12 && $2 < 32 && $3 < 13) { # well, actually it looks like yyyy.dd.mm -- why, I don't konw
296                                         ($y,$m,$d) = ($1,$3,$2);
297                                 } elsif ($3 > 12 && $3 < 31 && $2 < 13) {
298                                         ($y,$m,$d) = ($1,$2,$3);
299                                 }
300                         }
301                 } elsif ( $1 < 99 && $2 < 99 && $3 < 99 && $1 > 0 && $2 > 0 && $3 > 0) {
302                         if ($3 < 7) { # probably 2000 or greater, mm.dd.yy
303                                 my $y = $3 + 2000;
304                                 if ($1 > 12 && $1 < 32 && $2 < 13) { # well, actually it looks like dd.mm.yyyy
305                                         ($m,$d) = ($2,$1);
306                                 } elsif ($2 > 12 && $2 < 32 && $1 < 13) {
307                                         ($m,$d) = ($1,$2);
308                                 }
309                         } else { # probably before 2000, mm.dd.yy
310                                 my $y = $3 + 1900;
311                                 if ($1 > 12 && $1 < 32 && $2 < 13) { # well, actually it looks like dd.mm.yyyy
312                                         ($m,$d) = ($2,$1);
313                                 } elsif ($2 > 12 && $2 < 32 && $1 < 13) {
314                                         ($m,$d) = ($1,$2);
315                                 }
316                         }
317                 }
318         }
319
320         my $date;
321         if ($y && $m && $d) {
322                 try {
323                         $date = sprintf('%04d-%02d-%-2d',$y, $m, $d)
324                                 if (new DateTime ( year => $y, month => $m, day => $d ));
325                 } otherwise {};
326         }
327
328         return $date;
329 }
330