Annotation of loncom/misc/rebuild_lastlogin.pl, revision 1.1

1.1     ! raeburn     1: #!/usr/bin/perl
        !             2: # The LearningOnline Network
        !             3: #
        !             4: # $Id: rebuild_lastlogin.pl,v 1.1 2013/10/26 15:42:00 raeburn Exp $
        !             5: #
        !             6: # Copyright Michigan State University Board of Trustees
        !             7: #
        !             8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
        !             9: #
        !            10: # LON-CAPA is free software; you can redistribute it and/or modify
        !            11: # it under the terms of the GNU General Public License as published by
        !            12: # the Free Software Foundation; either version 2 of the License, or
        !            13: # (at your option) any later version.
        !            14: #
        !            15: # LON-CAPA is distributed in the hope that it will be useful,
        !            16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            18: # GNU General Public License for more details.
        !            19: #
        !            20: # You should have received a copy of the GNU General Public License
        !            21: # along with LON-CAPA; if not, write to the Free Software
        !            22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
        !            23: #
        !            24: # /home/httpd/html/adm/gpl.txt
        !            25: #
        !            26: # http://www.lon-capa.org/
        !            27: #
        !            28: #################################################
        !            29: 
        !            30: =pod
        !            31: 
        !            32: =head1 NAME
        !            33: 
        !            34: rebuild_lastlogin.pl
        !            35: 
        !            36: =head1 SYNOPSIS
        !            37: 
        !            38: rebuild_lastlogin.pl is run on a library server and gathers
        !            39: last login information for users in course(s) for which the current 
        !            40: server is the home server.
        !            41: 
        !            42: =head1 DESCRIPTION
        !            43: 
        !            44: For each selected course a hash containing keys of: 
        !            45: username:domain:section:role with values: UNIX timestamp of
        !            46: most recent role/section selection, i.e., "last log-in" will
        !            47: be stored in a nohist_crslastlogin.db file for the course.
        !            48: 
        !            49: usage: either enter the script name followed by at least one argument:
        !            50: the time interval prior to now, i.e., day, week, month, year or
        !            51: all for which last log-in activity is to be retrieved  or enter the 
        !            52: script name followed by at least two arguments:
        !            53: 1. a specific courseID and 2. a specific course domain.
        !            54: 
        !            55: Both cases support an additional, optional, final argument: 
        !            56: a two letter code for the desired language.
        !            57: 
        !            58:                  ar => ﺎﻠﻋﺮﺒﻳﺓ
        !            59:                  de => Deutsch
        !            60:                  en => English
        !            61:                  es => español
        !            62:                  fa => ﺍیﺭﺎﻧی
        !            63:                  fr => français
        !            64:                  he => עברית
        !            65:                  ja => 日本語
        !            66:                  pt => Português
        !            67:                  ru => Русский
        !            68:                  tr => türkçe
        !            69:                  zh => 简体中文
        !            70: 
        !            71: The default language is en.  If a translation exists for the language
        !            72: specified as the final argument, then if a time interval is being used
        !            73: that will be specified in that language, in the first argument when 
        !            74: calling the script. 
        !            75: 
        !            76: The script must be run as the user www.
        !            77: 
        !            78: Because the script will attempt to retrieve user activity log from
        !            79: the home server of each student or course personnel, the LON-CAPA
        !            80: daemons need to be running in case that person's home server is not
        !            81: the same as the course's home server.
        !            82: 
        !            83: =cut
        !            84: 
        !            85: #################################################
        !            86: 
        !            87: #! /usr/bin/perl
        !            88: 
        !            89: use strict;
        !            90: use lib '/home/httpd/lib/perl/';
        !            91: use Apache::lonnet;
        !            92: use Apache::loncommon;
        !            93: use Apache::loncoursedata;
        !            94: use Apache::lonlocal;
        !            95: use LONCAPA qw(:DEFAULT :match);
        !            96: 
        !            97: my %languages = (
        !            98:                   ar => 'ﺎﻠﻋﺮﺒﻳﺓ',
        !            99:                   de => 'Deutsch',
        !           100:                   en => 'English',
        !           101:                   es => 'español',
        !           102:                   fa => 'ﺍیﺭﺎﻧی',
        !           103:                   fr => 'français',
        !           104:                   he => 'עברית',
        !           105:                   ja => '日本語',
        !           106:                   pt => 'Português',
        !           107:                   ru => 'Русский',
        !           108:                   tr => 'türkçe',
        !           109:                   zh => '简体中文',
        !           110:                 );
        !           111: my $langchoices = "\n";
        !           112: foreach my $key (sort(keys(%languages))) {
        !           113:     $langchoices .= '                 '.$key.' => '.$languages{$key}."\n";
        !           114: }
        !           115: my $lang = 'en';
        !           116: my @args = @ARGV;
        !           117: 
        !           118: my @domains = sort(&Apache::lonnet::current_machine_domains());
        !           119: my @ids=&Apache::lonnet::current_machine_ids();
        !           120: my ($domfilter,$possdomstr);
        !           121: my $crsfilter = '.';
        !           122: if (@domains > 1) {
        !           123:     $possdomstr = join('|',@domains);
        !           124: } else {
        !           125:     $possdomstr = $domains[0];
        !           126: }
        !           127: if (@args>3) {
        !           128:     if (exists($languages{$args[2]})) {
        !           129:         $lang = $args[2];
        !           130:     }
        !           131:     &Apache::lonlocal::get_language_handle(undef,$lang);
        !           132:     print &mt('[_1] takes a maximum of 3 arguments.','rebuild_lastlogin.pl')."\n";
        !           133:     exit; 
        !           134: } elsif ((@args==3) && exists($languages{$args[2]})) {
        !           135:     $lang = pop(@args);
        !           136: } elsif ((@args==2) && exists($languages{$args[1]})) {
        !           137:     $lang = pop(@args);
        !           138: }
        !           139: &Apache::lonlocal::get_language_handle(undef,$lang);
        !           140: if (@args == 2) { 
        !           141:     my ($cnum,$cdom) = @args;
        !           142:     my $invalid;
        !           143:     if ($cnum =~ /^$match_courseid$/ && $cdom =~ /^$match_domain$/) {
        !           144:         if (grep(/^\Q$cdom\E$/,@domains)) {
        !           145:             my %crshash = &Apache::lonnet::coursedescription("$cdom/$cnum",{'one_time' => 1});
        !           146:             if ($crshash{'num'} eq $cnum) {
        !           147:                 $crsfilter = $cnum;
        !           148:                 $domfilter = $cdom;
        !           149:             } else {
        !           150:                 $invalid = 1;
        !           151:             }
        !           152:         } else {
        !           153:             $invalid = 1;
        !           154:         }
        !           155:     } else {
        !           156:         $invalid = 1;
        !           157:     }
        !           158:     if ($invalid) {
        !           159:         print "\n".&mt('usage for a single course: [_1] where [_2] is an optional two letter code.',
        !           160:                   'rebuild_lastlogin.pl [COURSEID] [COURSEDOMAIN] <LANG>',
        !           161:                   '<LANG>')."\n";
        !           162:         exit;
        !           163:     }
        !           164: }
        !           165: if ($crsfilter eq '.') {
        !           166:     if (!@args || ($args[0] ne &mt('all') && $args[0] ne &mt('year') && $args[0] ne &mt('month') &&
        !           167:                    $args[0] ne &mt('week') && $args[0] ne &mt('day'))) {
        !           168:         print "\n".
        !           169:               &mt('usage: either enter the script name followed by at least one argument')."\n".
        !           170:                   '       -- 1. '.&mt('the time interval from prior to now, i.e., day, week etc., for which log-in activity is to be checked')."\n\n".
        !           171:                   '      '.&mt('or enter the script name followed by at least two arguments')."\n".
        !           172:                   '        -- 1. '.&mt('a specific courseID')."\n".
        !           173:                   '        -- 2. '.&mt('a specific course domain')."\n\n".
        !           174:                   '              '.&mt('Both cases support an additional, optional, final argument: a two letter code for the desired language')."\n".
        !           175:                   '              '.&mt('-- one of: [_1]').$langchoices."\n".
        !           176:                   '      '.&mt('Accordingly use one of the following:')."\n\n".
        !           177:                   '      rebuild_lastlogin.pl '.&mt('all').' <LANG>'."\n".
        !           178:                   '      rebuild_lastlogin.pl '.&mt('year').' <LANG>'."\n".
        !           179:                   '      rebuild_lastlogin.pl '.&mt('month').' <LANG>'."\n".
        !           180:                   '      rebuild_lastlogin.pl '.&mt('week').' <LANG>'."\n".
        !           181:                   '      rebuild_lastlogin.pl '.&mt('day').' <LANG>'."\n".
        !           182:                   '      rebuild_lastlogin.pl [COURSEID] [COURSEDOMAIN] <LANG> '."\n";
        !           183:         exit;
        !           184:     }
        !           185: }
        !           186: 
        !           187: #  Make sure this process is running from user=www
        !           188: my $wwwid=getpwnam('www');
        !           189: if ($wwwid!=$<) {
        !           190:     my $emailto="$Apache::lonnet::perlvar{'lonAdmEMail'}}";
        !           191:     my $subj="LON: $Apache::lonnet::perlvar{'lonHostID'} User ID mismatch";
        !           192:     my $msg=&mt('User ID mismatch.').' '.&mt('[_1] must be run as user www.','rebuild_lastlogin.pl');
        !           193:     system("echo '$msg' | mail -s '$subj' $emailto > /dev/null");
        !           194:     exit 1;
        !           195: }
        !           196: 
        !           197: if ($Apache::lonnet::perlvar{'lonRole'} ne 'library') {
        !           198:     print &mt('[_1] only runs on a LON-CAPA library server.','rebuild_lastlogin.pl')."\n";
        !           199:     exit;  
        !           200: }
        !           201: 
        !           202: # Log script run
        !           203: open(my $fh,'>>'.$Apache::lonnet::perlvar{'lonDaemons'}.'/logs/buildlastlogin.log');
        !           204: print $fh "==== buildlastlogindb.pl Run ".localtime()."====\n";
        !           205: 
        !           206: my %users;
        !           207: my $timefilter = 1;
        !           208: my $now = time;
        !           209: if ($crsfilter eq '.') {
        !           210:     if ($ARGV[0] eq &mt('year')) {
        !           211:         $timefilter = $now-31536000;
        !           212:     } elsif ($ARGV[0] eq &mt('month')) {
        !           213:         $timefilter = $now-2592000;
        !           214:     } elsif ($ARGV[0] eq &mt('week')) {
        !           215:         $timefilter = $now-604800;
        !           216:     } elsif ($ARGV[0] eq &mt('day')) {
        !           217:         $timefilter = $now-86400;
        !           218:     }
        !           219: }
        !           220: 
        !           221: foreach my $dom (@domains) {
        !           222:     if ($domfilter) {
        !           223:         next if ($dom ne $domfilter);
        !           224:     }
        !           225:     my %courseshash;
        !           226:     my %currhash = &Apache::lonnet::courseiddump($dom,'.',$timefilter,'.','.',$crsfilter,1,\@ids,'.');
        !           227:     foreach my $cid (sort(keys(%currhash))) {
        !           228:         my ($cdom,$cnum) = split(/_/,$cid);
        !           229:         my $path = &propath($cdom,$cnum);
        !           230:         my %advrolehash = &Apache::lonnet::get_my_roles($cnum,$cdom,undef,
        !           231:                               ['previous','active','future']);
        !           232:         my %coursehash = &Apache::lonnet::coursedescription("$cdom/$cnum",{'one_time' => 1});
        !           233:         my %nothidden;
        !           234:         if ($coursehash{'nothideprivileged'}) {
        !           235:             foreach my $item (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
        !           236:                 my $user;
        !           237:                 if ($item =~ /:/) {
        !           238:                     $user = $item;
        !           239:                 } else {
        !           240:                     $user = join(':',split(/[\@]/,$item));
        !           241:                 }
        !           242:                 $nothidden{$user} = 1;
        !           243:             }
        !           244:         }
        !           245:         foreach my $user (keys(%advrolehash)) {
        !           246:             my ($uname,$udom,$rest) = split(/:/,$user,3);
        !           247:             next if ($users{$uname.':'.$udom});
        !           248:             my @privdoms = ($cdom);
        !           249:             if ($udom ne $cdom) {
        !           250:                 @privdoms = ($udom,$cdom);
        !           251:             }
        !           252:             if (&Apache::lonnet::privileged($uname,$udom,\@privdoms)) {
        !           253:                 unless ($nothidden{$uname.':'.$udom}) {
        !           254:                     next;
        !           255:                 }
        !           256:             }
        !           257:             $users{$uname.':'.$udom} = 1;
        !           258:         }
        !           259:         my $classlist=&Apache::loncoursedata::get_classlist($cdom,$cnum);
        !           260:         if (ref($classlist) eq 'HASH') {
        !           261:             foreach my $student (keys(%{$classlist})) {
        !           262:                 next if ($users{$student});
        !           263:                 if ($student =~/^($match_username)\:($match_domain)$/) {
        !           264:                     my ($tuname,$tudom)=($1,$2);
        !           265:                     my @privdoms = ($cdom);
        !           266:                     if ($tudom ne $cdom) {
        !           267:                         @privdoms = ($tudom,$cdom);
        !           268:                     }
        !           269:                     if (&Apache::lonnet::privileged($tuname,$tudom,\@privdoms)) {
        !           270:                         unless ($nothidden{$student}) {
        !           271:                             next;
        !           272:                         }
        !           273:                     }
        !           274:                     $users{$student} = 1;
        !           275:                 }
        !           276:             }
        !           277:         }
        !           278:     }
        !           279: }
        !           280: 
        !           281: my %lastlogin;
        !           282: my (%numlib,%domservers,%filter);
        !           283: $filter{'action'} = 'Role';
        !           284: my $possdom;
        !           285: if ($domfilter) {
        !           286:     $possdom = $domfilter;
        !           287: } else {
        !           288:     $possdom = $possdomstr;
        !           289: }
        !           290: foreach my $dom (@domains) {
        !           291:     my %servers = &Apache::lonnet::get_servers($dom,'library');
        !           292:     $numlib{$dom} = scalar(keys(%servers));
        !           293:     if ($numlib{$dom} > 1) {
        !           294:         $domservers{$dom} = %servers;
        !           295:     }
        !           296: }
        !           297: foreach my $user (sort(keys(%users))) {
        !           298:     my ($uname,$udom) = split(/:/,$user);
        !           299:     if (grep(/^\Q$udom\E$/,@domains)) {
        !           300:         if ($numlib{$udom} == 1) {
        !           301:             &readlocalactivitylog($uname,$udom,$possdom,$timefilter,$crsfilter,\%lastlogin);
        !           302:         } else {
        !           303:             if (ref($domservers{$udom}) eq 'HASH') {
        !           304:                 my $uhome = &Apache::lonnet::homeserver($uname,$udom);
        !           305:                 if ($uhome ne 'no_host') {
        !           306:                     if ($domservers{$udom}{$uhome}) {
        !           307:                         &readlocalactivitylog($uname,$udom,$possdom,$timefilter,$crsfilter,\%lastlogin);
        !           308:                     } else {
        !           309:                         &readremoteactivitylog($uname,$udom,$possdom,$timefilter,$crsfilter,\%filter,
        !           310:                                                \%lastlogin);
        !           311:                     }
        !           312:                 }
        !           313:             }
        !           314:         }
        !           315:     } else {
        !           316:         my $uhome = &Apache::lonnet::homeserver($uname,$udom);
        !           317:         if ($uhome ne 'no_host') {
        !           318:             &readremoteactivitylog($uname,$udom,$possdom,$timefilter,$crsfilter,\%filter,\%lastlogin);
        !           319:         }
        !           320:     }
        !           321: }
        !           322: 
        !           323: foreach my $key (sort(keys(%lastlogin))) {
        !           324:     if (ref($lastlogin{$key}) eq 'HASH') {
        !           325:         if ($key =~ /^($match_domain)_($match_courseid)$/) {
        !           326:             my $cdom = $1;
        !           327:             my $cnum = $2;
        !           328:             my $putresult = &Apache::lonnet::put('nohist_crslastlogin',$lastlogin{$key},
        !           329:                                                  $cdom,$cnum);
        !           330:             if ($putresult eq 'ok') {
        !           331:                 print $fh "stored last login data for $key\n";
        !           332:             }
        !           333:         }
        !           334:     }
        !           335: }
        !           336: 
        !           337: ## Finished!
        !           338: print $fh "==== buildlastlogindb.pl completed ".localtime()." ====\n";
        !           339: close($fh);
        !           340: exit;
        !           341: 
        !           342: sub readlocalactivitylog {
        !           343:     my($uname,$udom,$possdomstr,$timefilter,$crsfilter,$lastlogin) = @_;
        !           344:     my $path = &propath($udom,$uname);
        !           345:     my $posscnum;
        !           346:     if ($crsfilter eq '.') {
        !           347:         $posscnum = $match_courseid;
        !           348:     } else {
        !           349:         $posscnum = $crsfilter;
        !           350:     } 
        !           351:     if (-e "$path/activity.log") {
        !           352:         if (open(my $fh,"<$path/activity.log")) {
        !           353:             my @lines = <$fh>;
        !           354:             @lines = reverse(@lines);
        !           355:             foreach my $line (@lines) {
        !           356:                 chomp($line);
        !           357:                 if ($line =~ m{^(\d+):\w+:Role\s+(cc|in|ta|ep|st|ad)\./($possdomstr)/($posscnum)/?([^/]*)}) {
        !           358:                     if (($timefilter > 1) && ($1<$timefilter)) {
        !           359:                         last;
        !           360:                     }
        !           361:                     if (ref($lastlogin->{$3.'_'.$4}) eq 'HASH') {
        !           362:                         if ($lastlogin->{$3.'_'.$4}{$uname.':'.$udom.':'.$5.':'.$2}) {
        !           363:                             if ($crsfilter ne '.') {
        !           364:                                 last;
        !           365:                             } else {
        !           366:                                 next;
        !           367:                             }
        !           368:                         }
        !           369:                     }
        !           370:                     $lastlogin->{$3.'_'.$4}{$uname.':'.$udom.':'.$5.':'.$2} = $1;
        !           371:                 }
        !           372:             }
        !           373:             close($fh);
        !           374:         }
        !           375:     }
        !           376:     return;
        !           377: }
        !           378: 
        !           379: sub readremoteactivitylog {
        !           380:     my ($uname,$udom,$possdomstr,$timefilter,$crsfilter,$filter,$lastlogin) = @_;
        !           381:     if (ref($filter) eq 'HASH') {
        !           382:         my %filters = %{$filter};
        !           383:         my $result = &Apache::lonnet::userlog_query($uname,$udom,%filters);
        !           384:         my $posscnum;
        !           385:         if ($crsfilter eq '.') {
        !           386:             $posscnum = $match_courseid;
        !           387:         } else {
        !           388:             $posscnum = $crsfilter;
        !           389:         }
        !           390:         my $now = time;
        !           391:         if (($result ne 'file_error') && ($result ne 'error: reply_file_error') && ($result !~ /^timeout:/)) {
        !           392:             my $now = time;
        !           393:             foreach my $item (map { &unescape($_); } (split(/&/,$result))) {
        !           394:                 if ($item =~ m{^(\d+):\w+:Role\s+(cc|in|ta|ep|st|ad)\./($possdomstr)/($posscnum)/?([^/]*)}) {
        !           395:                     if (($timefilter > 1) && ($1<$timefilter)) {
        !           396:                         last;
        !           397:                     }
        !           398:                     if (ref($lastlogin->{$3.'_'.$4}) eq 'HASH') {
        !           399:                         if ($lastlogin->{$3.'_'.$4}{$uname.':'.$udom.':'.$5.':'.$2}) {
        !           400:                             if ($crsfilter ne '.') {
        !           401:                                 last;
        !           402:                             } else {
        !           403:                                 next;
        !           404:                             }
        !           405:                         }
        !           406:                     }
        !           407:                     $lastlogin->{$3.'_'.$4}{$uname.':'.$udom.':'.$5.':'.$2} = $1;
        !           408:                 }
        !           409:             }
        !           410:         }
        !           411:     }
        !           412:     return;
        !           413: }
        !           414: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>