Change default group from 1 (daemon) to something more sensible: 100
[mspang/plceo2.git] / ceo.pl.in
1 #!/usr/bin/perl -w
2
3 use lib 'MODULE_PATH';
4 use strict;
5
6 # the main CEO script
7
8 require UI;
9 require Members;
10 require Accounts;
11 require Common;
12 require Terms;
13 require Quotas;
14 require Scripts;
15 require Format;
16 require BookQuery;
17 require Books;
18
19 ### Displays the menu #########################################################
20
21 sub menu_library {
22         &UI::Menu('CEO Library functions', [
23                 ['a', 'Add book', \&action_library_add_book],
24                 ['i', 'Import book list', \&action_library_import_list],
25                 ['nil'],
26                 ['g', 'Get Library Of Congress info', \&action_library_loc_info],
27                 ['nil'],
28                 ['d', 'Done', \&action_exit]
29         ]);
30         
31         return 1;
32 }
33
34 sub menu_ceo {
35
36         &UI::Menu('Welcome to the CSC Electronic Office', [
37                 ['n', 'New member', \&action_new_member],
38                 ['r', 'Register for a term', \&action_register],
39                 ['m', 'Register for multiple terms', \&action_register_multiple],
40                 ['d', 'Display a member', \&action_show_member],
41                 ['t', 'List members registered in a term', \&action_list_registered],
42                 ['s', 'Search for a member by name', \&action_list_by_name],
43                 ['i', 'Search for a member by student id', \&action_list_by_studentid],
44                 ['nil'],
45                 ['a', 'Create an account', \&action_create_account],
46                 ['r', 'Re Create an account', \&action_re_create_account],
47                 ['nil'],
48                 ['l', 'Library functions', \&menu_library],
49                 ['nil'],
50                 ['x', 'Exit', \&action_exit]
51         ]);
52 }
53
54
55 ### Methods for performing menu actions #######################################
56
57 sub action_new_member {
58 #{{{
59         # get name of member
60         my $name =
61                 &UI::Prompt('      Name: ', 'string');
62
63         # get student id of member
64         my $studentid =
65                 &UI::Prompt('Student id: ', 'regex', q(\\d+));
66
67         # get member's program
68         my $program =
69                 &UI::Prompt('   Program: ', 'string');
70
71         my @list = &Members::GetByStudentID($studentid);
72         if (@list) {
73             &UI::MsgWait("A member with this student ID exists.");
74             &UI::Page(&Format::format_member_list(@list));
75             return 1;
76         }
77         
78         $Common::CEODB->{AutoCommit} = 0; # enable transactions
79         # add member
80         my $memberid = &Members::Add({
81                 name => $name,
82                 studentid => $studentid,
83                 program => $program,
84                 type => 'user',
85         });
86
87         # verify add
88         if($memberid == 0) {
89                 $Common::CEODB->rollback();
90                 &UI::MsgWait("Failure trying to add you to the database");
91                 $Common::CEODB->{AutoCommit} = 1;
92                 return 1;
93         }
94
95         # register member for current term
96         my $status = &Terms::Register(
97                 $memberid,
98                 &Common::CurrentTerm());
99
100         if(!$status) {
101                 $Common::CEODB->rollback();
102                 &Common::Log(
103                         5,
104                         "ceo.pl::action_new_member",
105                         "new member registration",
106                         "Added member $memberid, but could not register!");
107
108                 &UI::MsgWait(
109                         "Failure trying to add you to the DB.");
110                 $Common::CEODB->{AutoCommit} = 1;
111                 return 1;
112         }
113         $Common::CEODB->commit();
114         $Common::CEODB->{AutoCommit} = 1;
115
116         &UI::MsgWait(
117                 "Success! Your memberid is $memberid.  You are now registered\n" .
118                 "for the " . &Common::CurrentTerm() . " term.");
119 #       print "\n";
120 #       my $do_account = &UI::Prompt(
121 #               'Do you want an account(yes/no)? ',
122 #               'yesno');
123 #       if($do_account) {
124 #               action_create_account($memberid);
125 #               return 1;
126 #       }
127         return 1;
128 #}}}
129 }
130
131 sub action_show_member {
132 #{{{
133         # get the user's memberid
134         my $memberid = &UI::Prompt(
135                 'Memberid: ',
136                 'custom',
137                 \&prompt_memberid_or_userid);
138
139         # retrieve the associated member
140         my $member = &Members::Get($memberid);
141
142 #       print "\n";
143         # print it out
144         if(!$member) {
145                 &UI::MsgWait("No member with id: $memberid");
146         } else {
147                 &Members::Print($member);
148         }
149         return 1;
150 #}}}
151 }
152
153 sub action_create_account {
154 #{{{
155         # takes an optional argument specifying memberid (if this method
156         # is invoked after action_new_member)
157         my ($memberid) = @_;
158
159         if(!$memberid) {
160                 # memberid was not provided in the arg, prompt for one
161                 $memberid = &UI::Prompt(
162                         'Enter member ID (exit to cancel): ',
163                         'custom',
164                         \&prompt_memberid_or_userid);
165         }
166
167         my $member = &Members::Get($memberid);
168         if(!$member) {
169                 &UI::MsgWait("No member with id: $memberid");
170                 return 1;
171         } else {
172                 &Members::Print($member);
173         }
174
175         if (!(&UI::Prompt("Is this the correct member?", 'yesno'))) {
176             &UI::MsgWait("I suggest searching for the member by userid or name from the main menu.");
177             return 1;
178         }
179
180         # get the account's userid
181         my $userid = &UI::Prompt('Userid: ', 'regex', q(^\\w+$));
182
183         # add the account
184         my $result = &Accounts::Add($memberid, $userid);
185
186         if(!$result) {
187                 &UI::MsgWait(
188                         "Error adding your account to the database.\n" .
189                         "Please talk to the sysadmin");
190                 return 1;
191         }
192
193         # all good
194         &UI::MsgWait("Success! Your account has been added");
195
196         return 1;
197
198 #}}}
199 }
200
201 sub action_re_create_account {
202 #{{{
203         # takes an optional argument specifying memberid (if this method
204         # is invoked after action_new_member)
205         my ($memberid) = @_;
206
207         if(!$memberid) {
208                 # memberid was not provided in the arg, prompt for one
209                 $memberid = &UI::Prompt(
210                         'Enter member ID (exit to cancel): ',
211                         'custom',
212                         \&prompt_memberid_or_userid);
213         }
214
215         my $member = &Members::Get($memberid);
216         if(!$member) {
217                 &UI::MsgWait("No member with id: $memberid");
218                 return 1;
219         } else {
220                 &Members::Print($member);
221         }
222
223         if (!(&UI::Prompt("Is this the correct member?", 'yesno'))) {
224             &UI::MsgWait("I suggest searching for the member by userid or name from the main menu.");
225             return 1;
226         }
227
228         # get the account's userid
229         my $userid = &UI::Prompt('Userid: ', 'regex', q(^\\w+$));
230
231         # add the account
232         my $result = &Accounts::ReAdd($memberid, $userid);
233
234         if(!$result) {
235                 &UI::MsgWait(
236                         "Error adding your account to the database.\n" .
237                         "Please talk to the sysadmin");
238                 return 1;
239         }
240
241         # all good
242         &UI::MsgWait("Success! Your account has been added");
243
244         return 1;
245
246 #}}}
247 }
248
249
250 sub action_list_registered {
251     my $term = &UI::Prompt(
252                            'Which term to list members for ([fws]20nn): ',
253                            'regex',
254                            q(^[swf]\\d{4}$));
255     my @list = &Members::GetByTerm($term, "members.name");
256
257     my $dis_string = &Format::format_member_list(@list);
258     &UI::Page($dis_string);
259     return 1;
260 }
261
262 sub action_list_by_name {
263     my $name = &UI::Prompt(
264                            'Enter the member\'s name: ',
265                            'string');
266     my @list = &Members::GetByName($name);
267
268     my $dis_string = &Format::format_member_list(@list);
269     &UI::Page($dis_string);
270     return 1;
271 }
272
273 sub action_list_by_studentid {
274     my $name = &UI::Prompt(
275                            'Enter the member\'s student id: ',
276                            'regex',
277                            q(\\d+));
278     my @list = &Members::GetByStudentID($name);
279
280     my $dis_string = &Format::format_member_list(@list);
281     &UI::Page($dis_string);
282     return 1;
283 }
284
285 sub action_buy_quota {
286 #{{{
287
288         # get the userid
289         my $userid = &UI::Prompt(
290                 'Enter userid: ',
291                 'custom',
292                 \&prompt_valid_userid);
293
294         # get the term to buy quota for
295         my $term = &UI::Prompt(
296                 'Which term to buy quota for ([fws]20nn): ',
297                 'regex',
298                 q(^[swf]\\d{4}$));
299
300         # get amount of quota to buy
301         my $quota = &UI::Prompt(
302                 'How much quota to buy: ',
303                 'int');
304
305         # make sure that member for account is registered for that term
306
307         # first get the account info (for memberid)
308         my $member = &Accounts::Get($userid);
309         if(!$member) {
310                 # could not get that account.  Wierd.  Print an error message
311                 &UI::MsgWait("Error retrieving account info!");
312                 return 1;
313         }
314
315         # check the registration for that term for the member
316         my $result = &Terms::IsRegistered($member->{'memberid'}, $term);
317         if(!$result) {
318                 &UI::MsgWait(
319                         "You are not registered for that term.  A member must register\n" .
320                         "for a term before she is allowed buy account quota for that\n" .
321                         "term.");
322                 return 1;
323         } elsif($result == -1) {
324                 &UI::MsgWait("Error while trying to verify member registration!");
325                 return 1;
326         }
327
328         # the member is registered for that term, go ahead and buy quota
329
330         $result = &Quotas::BuyQuota($userid, $term, $quota);
331         if(!$result) {
332                 &UI::MsgWait(
333                         "Error occurred while trying to update quota in database.\n" .
334                         "Please notify a sysadmin to resolve this.");
335                 return 1;
336         }
337
338         # add a command to scripts to set quota for user
339         my $current_term = &Common::CurrentTerm();
340         if($current_term eq $term) {
341                 # if quota is being bought for current term, issue command
342                 # to cronjobs to set quota
343                 my $result = &Scripts::SetQuota($userid, $quota);
344
345                 if(!$result) {
346                         &UI::MsgWait(
347                                 "You have bought $quota quota for account $userid \n" .
348                                 "for term $term.  But an error occurred trying to \n" .
349                                 "set the actual quota.  Please notify a sysadmin.\n");
350                         return 1;
351                 }
352         }
353
354         &UI::MsgWait(
355                 "Success! You have bought $quota quota for account $userid \n" .
356                 " for term $term.  Thank you.");
357
358         return 1;
359 #}}}
360 }
361
362 sub action_register {
363 #{{{
364         my $memberid = &UI::Prompt(
365                 'Enter memberid ("exit" to cancel): ',
366                 'custom',
367                 \&prompt_memberid_or_userid);
368
369         # retrieve the associated member
370         my $member = &Members::Get($memberid);
371
372 #       print "\n";
373         # print it out
374         if(!$member) {
375                 &UI::MsgWait("No member with id: $memberid");
376         } else {
377                 &Members::Print($member);
378         }
379         
380         my $term = &UI::Prompt(
381                 'Which term to register for ([fws]20nn): ',
382                 'regex',
383                 q(^[swf]\\d{4}$));
384
385         # check if the member is registerd for that term
386         my $status = &Terms::IsRegistered($memberid, $term);
387         if($status == -1) {
388                 # error doing registration lookup
389                 &UI::MsgWait(
390                         'Error trying to retrieve registration information.\n' .
391                         'Please contact the sysadmin to get this resolved.');
392                 return 1;
393         }
394         if($status == 1) {
395                 # already registered for that term
396                 &UI::MsgWait("You are already registered for term $term");
397                 return 1;
398         }
399         # not registered yet
400         $status = &Terms::Register($memberid, $term);
401         if($status == 0) {
402                 # error registering
403                 &UI::MsgWait(
404                         "Error registering you for term $term.\n" .
405                         "Please contact the sysadmin to get this resolved.");
406                 return 1;
407         }
408         # success registering
409         &UI::MsgWait("Your are now registered for term $term\n");
410
411         return 1;
412
413 #}}}
414 }
415         
416 sub action_register_multiple {
417 #{{{
418         my $memberid = &UI::Prompt(
419                 'Enter memberid ("exit" to cancel): ',
420                 'custom',
421                 \&prompt_memberid_or_userid);
422
423         # retrieve the associated member
424         my $member = &Members::Get($memberid);
425
426 #       print "\n";
427         # print it out
428         if(!$member) {
429                 &UI::MsgWait("No member with id: $memberid");
430         } else {
431                 &Members::Print($member);
432         }
433
434         my $term = &UI::Prompt(
435                 'Which term to start registering ([fws]20nn): ',
436                 'regex',
437                 q(^[swf]\\d{4}$));
438
439         # check if the member is registerd for that term
440         my $status = &Terms::IsRegistered($memberid, $term);
441         if($status == -1) {
442                 # error doing registration lookup
443                 &UI::MsgWait(
444                         'Error trying to retrieve registration information.\n' .
445                         'Please contact the sysadmin to get this resolved.');
446                 return 1;
447         }
448         if($status == 1) {
449                 # already registered for that term
450                 &UI::MsgWait("You are already registered for term $term");
451                 return 1;
452         }
453         # not registered yet
454         my $num = &UI::Prompt('How many terms? ', 'int');
455         $status = &Terms::RegisterMultiple($memberid, $term, $num);
456         if($status == 0) {
457                 # error registering
458                 &UI::MsgWait(
459                         "Error registering you for $num terms starting at term $term.\n" .
460                         "Please contact the sysadmin to get this resolved.");
461                 return 1;
462         }
463         # success registering
464         &UI::MsgWait("You are now registered for $num terms starting at $term\n");
465
466         return 1;
467
468 #}}}
469 }
470
471 sub action_exit { return 0; }
472
473 ### Helper functions ##########################################################
474
475 sub prompt_memberid_or_userid {
476 #{{{
477         # converts a memberid or userid into a valid memberid
478         # written for use with the &UI::Prompt function
479         my ($id) = @_;
480
481         if($id !~ /^\d+$/) {
482                 # guess that it's a userid and try to get the memberid
483                 my $account = &Members::GetByUserId($id);
484                 if(!$account) {
485                     &UI::MsgWait("$id is an invalid account userid\n");
486                     return 0;
487                 }
488                 $id = $account->{'memberid'};
489         }
490
491         # check member id to be valid
492         my $member = &Members::Get($id);
493         if(!$member) {
494                 &UI::MsgWait("$id is an invalid memberid\n");
495                 return 0;
496         }
497
498         return (1, $id);
499 #}}}
500 }
501
502 sub prompt_valid_userid {
503 #{{{
504         # function to be used with &UI::Prompt
505         # checks if an input is a valid userid
506         my ($id) = @_;
507
508         my $account = &Accounts::Get($id);
509         if(!$account) {
510                 # not a valid account
511                 return 0;
512         } else {
513                 return (1, $id);
514         }
515 #}}}
516 }
517
518 sub action_library_loc_info {
519     my $barcode;
520     do {
521         $barcode = &UI::Prompt("Please scan/enter the book's barcode: ",
522                                'regex', q([0-9]{13}));
523     } while (!(&Books::CheckBarcode($barcode)));
524
525     my $isbn = &Books::GetISBNFromBarcode($barcode);
526     my %session = &BookQuery::InitiateSession("z3950.loc.gov", "7090",
527                                               "VOYAGER");
528     if ($session{'success'} == 0) {
529         &UI::MsgWait("Error logging in to LOC.");
530         return 1;
531     }
532     my %book = &BookQuery::LookupBook(\%session, $isbn);
533
534     if ($book{'success'} == 0) {
535         &UI::MsgWait("Error fetching book with ISBN " . $isbn . ".");
536         return 1;
537     }
538     my $output;
539     foreach my $key (keys %book) {
540         if ($key eq "content" || $key eq "success") { next; }
541         $output .= $key . ": " . $book{$key} . "\n";
542     }
543     &UI::MsgWait($output);
544     return 1;
545 }
546
547 sub action_library_add_book {
548     my $code = 'N';
549 #     my $code = &UI::Prompt("[r]ed/[b]lue/[n]one: ", 'regex',
550 #                            q(r|b|n));
551     my $barcode = &UI::Prompt("Please scan/enter the book's barcode: ",
552                               'regex', q([0-9]{13}));
553     my $isbn = &Books::GetISBNFromBarcode($barcode);
554
555     my $bookexists = &Books::GetByISBN($isbn);
556     if ($bookexists) {
557         &UI::MsgWait("Sorry, the book with ISBN " . $isbn . " is already in the database.");
558         return 1;
559     }
560
561     my $count = &UI::Prompt("Number of copies: ", 'regex',
562                             q([1-9][0-9]*));
563     my $bookid;
564
565     my $msg;
566
567     my %book;
568     do {
569         if (&UI::Prompt("Use Library of Congress? ", 'yesno')) {
570             my %session = &BookQuery::InitiateSession("z3950.loc.gov", "7090",
571                                                       "VOYAGER");
572             if ($session{'success'} == 0) {
573                 &UI::MsgWait("Error logging in to LOC.");
574                 return 1;
575             }
576             %book = &BookQuery::LookupBook(\%session, $isbn);
577             
578             if ($book{'success'} == 0) {
579                 &UI::MsgWait("Error fetching book with ISBN " . $isbn
580                              . ".");
581                 return 1;
582             }
583             $book{'class'} = $code;
584             $book{'count'} = $count;
585         } else {
586             $book{'count'} = $count;
587             $book{'class'} = $code;
588             $book{'title'} = &UI::Prompt("Title: ", 'string');
589             $book{'author'} = &UI::Prompt("Author: ", 'string');
590             $book{'published'} = &UI::Prompt("Publisher: ", 'string');
591             $book{'isbn'} = $isbn;
592         }
593         
594         $msg = "--- Book information ---\n";
595         foreach my $key (keys %book) {
596             if ($key eq "content" || $key eq "success") { next; }
597             $msg .= $key . ": " . $book{$key} . "\n";
598         }
599         
600     } while (!(&UI::Prompt($msg . "Is this good? ", 'yesno')));
601     
602     $bookid = &Books::Add(\%book);
603     &UI::MsgWait("Added book with ID $bookid.");
604     return 1;
605 }
606
607 sub action_library_import_list {
608     my $pipe = $|;
609     $| = 1;
610     print "Logging into Library of Congress...";
611     $| = $pipe;
612
613     my %session = &BookQuery::InitiateSession("z3950.loc.gov", "7090",
614                                               "VOYAGER");
615     if ($session{'success'} != 1) {
616         print " failed.\n";
617         &UI::MsgWait("Error logging in to LOC.");
618         return 1;
619     } else {
620         print " successful.\n";
621     }
622     
623     my $filename = &UI::Prompt("File to import: ", 'string');
624     
625     my $count = 0;
626     my $noisbn = "";
627     my $noloc = "";
628     my $noadd = "";
629     open BOOKLIST, "<" . $filename;
630     while (my $line = <BOOKLIST>) {
631         chomp $line;
632         my $isbn; my $field;
633         if ($line =~ /^[0-9]{13}$/) { # Barcode
634             $isbn = &Books::GetISBNFromBarcode($line);
635             $field = "isbn";
636         } elsif ($line =~ /^[0-9]{9}[0-9X]$/) { # ISBN
637             $isbn = $line;
638             $field = "isbn";
639         } elsif ($line =~ /^[0-9]{8}/) { # LCCN
640             $isbn = $line;
641             $field = "lccn";
642         } else {
643             $noisbn .= $line . "\n";
644             next;
645         }
646
647         my $oldbook = &Books::GetByISBN($isbn);
648         if (defined $oldbook) {
649             print "\"" . $oldbook->{'title'}
650                 . " is already in the library\n";
651             next;
652         }
653         my %book;
654         %book = &BookQuery::LookupBook(\%session, $isbn, $field);
655         if ($book{success} != 1) {
656             $noloc .= $isbn . "\n";
657             next;
658         }
659         $book{'count'} = 1;
660         if (&Books::Add(\%book) == 0) {
661             print "Error cataloguing " . $book{'title'} . "\n";
662             $noadd .= "$isbn (" . $book{'title'} . ")\n";
663         } else {
664             print "Catalogued " . $book{'title'} . "\n";
665             $count++;
666         }
667     }
668     close BOOKLIST;
669     
670     my $page = "";
671     if ($noisbn ne "") {
672         $page .= "The following lines could not be read as ISBN numbers:\n"
673                . $noisbn;   
674     }
675     if ($noloc ne "") {
676         $page .= "The following ISBNs could not be retrieved from the LOC:\n"
677                . $noloc;
678     }
679     if ($noadd ne "") {
680         $page .= "The following ISBNs could not be added to the database:\n"
681                . $noadd;
682     }
683     if ($page ne "") {
684         &UI::Page($page);
685     }
686     
687     &UI::MsgWait("Imported $count books from \"$filename\".");
688     return 1;
689 }
690
691 $ENV{'PATH'} = "/bin:/usr/bin:/usr/local/bin";
692 &Common::DBConnect();
693 menu_ceo;
694
695 print "Good bye.\n";