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>