File:
[LON-CAPA] /
loncom /
misc /
rebuild_lastlogin.pl
Revision
1.1:
download - view:
text,
annotated -
select for diffs
Sat Oct 26 20:19:56 2013 UTC (10 years, 10 months ago) by
raeburn
Branches:
MAIN
CVS tags:
version_2_12_X,
version_2_11_X,
version_2_11_5_msu,
version_2_11_5,
version_2_11_4_uiuc,
version_2_11_4_msu,
version_2_11_4,
version_2_11_3_uiuc,
version_2_11_3_msu,
version_2_11_3,
version_2_11_2_uiuc,
version_2_11_2_msu,
version_2_11_2_educog,
version_2_11_2,
version_2_11_1,
version_2_11_0_RC3,
version_2_11_0_RC2,
version_2_11_0,
HEAD
rebuild_lastlogin.pl is run on a library server and gathers
last login information for users in course(s) for which the current
server is the home server.
For each selected course a hash containing keys of:
username:domain:section:role with values: UNIX timestamp of
most recent role/section selection, i.e., "last log-in" will
be stored in a nohist_crslastlogin.db file for the course.
This script is intended to be run after installation of LON-CAPA
2.11.0 on a library server to populate nohist_crslastlogin.db files
for the domain's courses. (Thereafter role selection after log-in
on a server running 2.11.0 will update nohist_crslastlogin.db
when a course role is selected.)
This script might be run at other times. For example:
(a) if course user sessions are being routinely hosted on pre-2.11.0 servers.
(b) if nohist_crslastlogin.db is lost or corrupted, and needs to be rebuilt.
1: #!/usr/bin/perl
2: # The LearningOnline Network
3: #
4: # $Id: rebuild_lastlogin.pl,v 1.1 2013/10/26 20:19:56 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>