Annotation of loncom/metadata_database/searchcat.pl, revision 1.56
1.1 harris41 1: #!/usr/bin/perl
2: # The LearningOnline Network
3: # searchcat.pl "Search Catalog" batch script
1.16 harris41 4: #
1.56 ! matthew 5: # $Id: searchcat.pl,v 1.55 2004/04/08 15:57:32 matthew Exp $
1.16 harris41 6: #
7: # Copyright Michigan State University Board of Trustees
8: #
1.29 albertel 9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
1.16 harris41 10: #
1.29 albertel 11: # LON-CAPA is free software; you can redistribute it and/or modify
1.16 harris41 12: # it under the terms of the GNU General Public License as published by
13: # the Free Software Foundation; either version 2 of the License, or
14: # (at your option) any later version.
15: #
1.29 albertel 16: # LON-CAPA is distributed in the hope that it will be useful,
1.16 harris41 17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19: # GNU General Public License for more details.
20: #
21: # You should have received a copy of the GNU General Public License
1.29 albertel 22: # along with LON-CAPA; if not, write to the Free Software
1.16 harris41 23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24: #
25: # /home/httpd/html/adm/gpl.txt
26: #
1.29 albertel 27: # http://www.lon-capa.org/
1.16 harris41 28: #
29: ###
1.33 matthew 30:
1.32 www 31: =pod
1.1 harris41 32:
1.32 www 33: =head1 NAME
34:
35: B<searchcat.pl> - put authoritative filesystem data into sql database.
36:
37: =head1 SYNOPSIS
38:
39: Ordinarily this script is to be called from a loncapa cron job
40: (CVS source location: F<loncapa/loncom/cron/loncapa>; typical
41: filesystem installation location: F</etc/cron.d/loncapa>).
42:
43: Here is the cron job entry.
44:
45: C<# Repopulate and refresh the metadata database used for the search catalog.>
46: C<10 1 * * 7 www /home/httpd/perl/searchcat.pl>
47:
48: This script only allows itself to be run as the user C<www>.
49:
50: =head1 DESCRIPTION
51:
52: This script goes through a loncapa resource directory and gathers metadata.
53: The metadata is entered into a SQL database.
54:
55: This script also does general database maintenance such as reformatting
56: the C<loncapa:metadata> table if it is deprecated.
57:
58: This script evaluates dynamic metadata from the authors'
1.48 www 59: F<nohist_resevaldata.db> database file in order to store it in MySQL.
1.32 www 60:
61: This script is playing an increasingly important role for a loncapa
62: library server. The proper operation of this script is critical for a smooth
63: and correct user experience.
64:
65: =cut
1.1 harris41 66:
1.45 www 67: use strict;
68:
1.55 matthew 69: use DBI;
1.17 harris41 70: use lib '/home/httpd/lib/perl/';
71: use LONCAPA::Configuration;
1.55 matthew 72: use LONCAPA::lonmetadata;
1.17 harris41 73:
1.56 ! matthew 74: use Getopt::Long;
1.1 harris41 75: use IO::File;
76: use HTML::TokeParser;
1.21 www 77: use GDBM_File;
1.24 www 78: use POSIX qw(strftime mktime);
1.56 ! matthew 79:
1.55 matthew 80: use File::Find;
1.1 harris41 81:
1.56 ! matthew 82: #
! 83: # Set up configuration options
! 84: my ($simulate,$oneuser,$help,$verbose,$logfile,$debug);
! 85: GetOptions (
! 86: 'help' => \$help,
! 87: 'simulate' => \$simulate,
! 88: 'only=s' => \$oneuser,
! 89: 'verbose=s' => \$verbose,
! 90: 'debug' => \$debug,
! 91: );
! 92:
! 93: if ($help) {
! 94: print <<"ENDHELP";
! 95: $0
! 96: Rebuild and update the LON-CAPA metadata database.
! 97: Options:
! 98: -help Print this help
! 99: -simulate Do not modify the database.
! 100: -only=user Only compute for the given user. Implies -simulate
! 101: -verbose=val Sets logging level, val must be a number
! 102: -debug Turns on debugging output
! 103: ENDHELP
! 104: exit 0;
! 105: }
! 106:
! 107: if (! defined($debug)) {
! 108: $debug = 0;
! 109: }
! 110:
! 111: if (! defined($verbose)) {
! 112: $verbose = 0;
! 113: }
! 114:
! 115: if (defined($oneuser)) {
! 116: $simulate=1;
! 117: }
! 118:
1.55 matthew 119: ##
120: ## Use variables for table names so we can test this routine a little easier
121: my $oldname = 'metadata';
122: my $newname = 'newmetadata';
1.45 www 123:
1.55 matthew 124: #
125: # Read loncapa_apache.conf and loncapa.conf
126: my $perlvarref=LONCAPA::Configuration::read_conf('loncapa.conf');
127: my %perlvar=%{$perlvarref};
128: undef $perlvarref;
129: delete $perlvar{'lonReceipt'}; # remove since sensitive (really?) & not needed
130: #
131: # Only run if machine is a library server
132: exit if ($perlvar{'lonRole'} ne 'library');
133: #
134: # Make sure this process is running from user=www
135: my $wwwid=getpwnam('www');
136: if ($wwwid!=$<) {
137: my $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
138: my $subj="LON: $perlvar{'lonHostID'} User ID mismatch";
139: system("echo 'User ID mismatch. searchcat.pl must be run as user www.' |\
140: mailto $emailto -s '$subj' > /dev/null");
141: exit 1;
142: }
143: #
144: # Let people know we are running
145: open(LOG,'>'.$perlvar{'lonDaemons'}.'/logs/searchcat.log');
1.56 ! matthew 146: &log(0,'==== Searchcat Run '.localtime()."====");
! 147: if ($debug) {
! 148: &log(0,'simulating') if ($simulate);
! 149: &log(0,'only processing user '.$oneuser) if ($oneuser);
! 150: &log(0,'verbosity level = '.$verbose);
! 151: }
1.55 matthew 152: #
153: # Connect to database
154: my $dbh;
155: if (! ($dbh = DBI->connect("DBI:mysql:loncapa","www",$perlvar{'lonSqlAccess'},
156: { RaiseError =>0,PrintError=>0}))) {
1.56 ! matthew 157: &log(0,"Cannot connect to database!");
1.55 matthew 158: die "MySQL Error: Cannot connect to database!\n";
159: }
160: # This can return an error and still be okay, so we do not bother checking.
161: # (perhaps it should be more robust and check for specific errors)
162: $dbh->do('DROP TABLE IF EXISTS '.$newname);
163: #
164: # Create the new table
165: my $request = &LONCAPA::lonmetadata::create_metadata_storage($newname);
166: $dbh->do($request);
167: if ($dbh->err) {
168: $dbh->disconnect();
1.56 ! matthew 169: &log(0,"MySQL Error Create: ".$dbh->errstr);
1.55 matthew 170: die $dbh->errstr;
171: }
172: #
173: # find out which users we need to examine
1.56 ! matthew 174: my $dom = $perlvar{'lonDefDomain'};
! 175: opendir(RESOURCES,"$perlvar{'lonDocRoot'}/res/$dom");
1.55 matthew 176: my @homeusers =
177: grep {
1.56 ! matthew 178: &ishome("$perlvar{'lonDocRoot'}/res/$dom/$_");
1.55 matthew 179: } grep {
180: !/^\.\.?$/;
181: } readdir(RESOURCES);
182: closedir RESOURCES;
183: #
1.56 ! matthew 184: if ($oneuser) {
! 185: @homeusers=($oneuser);
! 186: }
! 187: #
1.55 matthew 188: # Loop through the users
189: foreach my $user (@homeusers) {
1.56 ! matthew 190: &log(0,"=== User: ".$user);
! 191: &process_dynamic_metadata($user,$dom);
1.55 matthew 192: #
193: # Use File::Find to get the files we need to read/modify
194: find(
195: {preprocess => \&only_meta_files,
196: # wanted => \&print_filename,
197: # wanted => \&log_metadata,
198: wanted => \&process_meta_file,
199: },
200: "$perlvar{'lonDocRoot'}/res/$perlvar{'lonDefDomain'}/$user");
201: }
202: #
203: # Rename the table
1.56 ! matthew 204: if (! $simulate) {
! 205: $dbh->do('DROP TABLE IF EXISTS '.$oldname);
! 206: if (! $dbh->do('RENAME TABLE '.$newname.' TO '.$oldname)) {
! 207: &log(0,"MySQL Error Rename: ".$dbh->errstr);
! 208: die $dbh->errstr;
! 209: } else {
! 210: &log(1,"MySQL table rename successful.");
! 211: }
1.55 matthew 212: }
1.56 ! matthew 213:
1.55 matthew 214: if (! $dbh->disconnect) {
1.56 ! matthew 215: &log(0,"MySQL Error Disconnect: ".$dbh->errstr);
1.55 matthew 216: die $dbh->errstr;
217: }
218: ##
219: ## Finished!
1.56 ! matthew 220: &log(0,"==== Searchcat completed ".localtime()." ====");
1.55 matthew 221: close(LOG);
1.21 www 222:
1.55 matthew 223: &write_type_count();
224: &write_copyright_count();
1.36 www 225:
1.55 matthew 226: exit 0;
1.28 harris41 227:
1.56 ! matthew 228: ##
! 229: ## Status logging routine. Inputs: $level, $message
! 230: ##
! 231: ## $level 0 should be used for normal output and error messages
! 232: ##
! 233: ## $message does not need to end with \n. In the case of errors
! 234: ## the message should contain as much information as possible to
! 235: ## help in diagnosing the problem.
! 236: ##
! 237: sub log {
! 238: my ($level,$message)=@_;
! 239: $level = 0 if (! defined($level));
! 240: if ($verbose >= $level) {
! 241: print LOG $message.$/;
! 242: }
! 243: }
! 244:
1.55 matthew 245: ########################################################
246: ########################################################
247: ### ###
248: ### File::Find support routines ###
249: ### ###
250: ########################################################
251: ########################################################
252: ##
253: ## &only_meta_files
254: ##
255: ## Called by File::Find.
256: ## Takes a list of files/directories in and returns a list of files/directories
257: ## to search.
258: sub only_meta_files {
259: my @PossibleFiles = @_;
260: my @ChosenFiles;
261: foreach my $file (@PossibleFiles) {
262: if ( ($file =~ /\.meta$/ && # Ends in meta
263: $file !~ /\.\d+\.[^\.]+\.meta$/ # is not for a prior version
264: ) || (-d $file )) { # directories are okay
265: # but we do not want /. or /..
266: push(@ChosenFiles,$file);
267: }
1.38 www 268: }
1.55 matthew 269: return @ChosenFiles;
1.38 www 270: }
271:
1.55 matthew 272: ##
273: ##
274: ## Debugging routines, use these for 'wanted' in the File::Find call
275: ##
276: sub print_filename {
277: my ($file) = $_;
278: my $fullfilename = $File::Find::name;
1.56 ! matthew 279: if ($debug) {
! 280: if (-d $file) {
! 281: &log(5," Got directory ".$fullfilename);
! 282: } else {
! 283: &log(5," Got file ".$fullfilename);
! 284: }
1.38 www 285: }
1.55 matthew 286: $_=$file;
1.38 www 287: }
1.28 harris41 288:
1.55 matthew 289: sub log_metadata {
290: my ($file) = $_;
291: my $fullfilename = $File::Find::name;
292: return if (-d $fullfilename); # No need to do anything here for directories
1.56 ! matthew 293: if ($debug) {
! 294: &log(6,$fullfilename);
! 295: my $ref=&metadata($fullfilename);
! 296: if (! defined($ref)) {
! 297: &log(6," No data");
! 298: return;
! 299: }
! 300: while (my($key,$value) = each(%$ref)) {
! 301: &log(6," ".$key." => ".$value);
! 302: }
! 303: &count_copyright($ref->{'copyright'});
1.55 matthew 304: }
305: $_=$file;
1.31 harris41 306: }
1.21 www 307:
1.22 www 308:
1.55 matthew 309: ##
310: ## process_meta_file
311: ## Called by File::Find.
312: ## Only input is the filename in $_.
313: sub process_meta_file {
314: my ($file) = $_;
1.56 ! matthew 315: my $filename = $File::Find::name; # full filename
1.55 matthew 316: return if (-d $filename); # No need to do anything here for directories
317: #
1.56 ! matthew 318: &log(3,$filename) if ($debug);
1.55 matthew 319: #
320: my $ref=&metadata($filename);
321: #
322: # $url is the original file url, not the metadata file
323: my $url='/res/'.&declutter($filename);
324: $url=~s/\.meta$//;
1.56 ! matthew 325: &log(3," ".$url) if ($debug);
1.55 matthew 326: #
327: # Ignore some files based on their metadata
328: if ($ref->{'obsolete'}) {
1.56 ! matthew 329: &log(3,"obsolete") if ($debug);
1.55 matthew 330: return;
331: }
332: &count_copyright($ref->{'copyright'});
333: if ($ref->{'copyright'} eq 'private') {
1.56 ! matthew 334: &log(3,"private") if ($debug);
1.55 matthew 335: return;
336: }
337: #
338: # Find the dynamic metadata
339: my %dyn;
340: if ($url=~ m:/default$:) {
341: $url=~ s:/default$:/:;
1.56 ! matthew 342: &log(3,"Skipping dynamic data") if ($debug);
1.55 matthew 343: } else {
1.56 ! matthew 344: &log(3,"Retrieving dynamic data") if ($debug);
! 345: %dyn=&get_dynamic_metadata($url);
1.55 matthew 346: &count_type($url);
347: }
348: #
349: $ref->{'creationdate'} = &sqltime($ref->{'creationdate'});
350: $ref->{'lastrevisiondate'} = &sqltime($ref->{'lastrevisiondate'});
351: my %Data = (
352: %$ref,
353: %dyn,
354: 'url'=>$url,
355: 'version'=>'current');
1.56 ! matthew 356: if (! $simulate) {
! 357: my ($count,$err) = &LONCAPA::lonmetadata::store_metadata($dbh,$newname,
! 358: \%Data);
! 359: if ($err) {
! 360: &log(0,"MySQL Error Insert: ".$err);
! 361: die $err;
! 362: }
! 363: if ($count < 1) {
! 364: &log(0,"Unable to insert record into MySQL database for $url");
! 365: die "Unable to insert record into MySQl database for $url";
! 366: }
1.55 matthew 367: }
368: #
369: # Reset $_ before leaving
370: $_ = $file;
371: }
372:
373: ########################################################
374: ########################################################
375: ### ###
376: ### &metadata($uri) ###
377: ### Retrieve metadata for the given file ###
378: ### ###
379: ########################################################
380: ########################################################
381: sub metadata {
382: my ($uri)=@_;
383: my %metacache=();
384: $uri=&declutter($uri);
385: my $filename=$uri;
386: $uri=~s/\.meta$//;
387: $uri='';
388: if ($filename !~ /\.meta$/) {
389: $filename.='.meta';
390: }
391: my $metastring=&getfile($perlvar{'lonDocRoot'}.'/res/'.$filename);
392: return undef if (! defined($metastring));
393: my $parser=HTML::TokeParser->new(\$metastring);
394: my $token;
395: while ($token=$parser->get_token) {
396: if ($token->[0] eq 'S') {
397: my $entry=$token->[1];
398: my $unikey=$entry;
399: if (defined($token->[2]->{'part'})) {
400: $unikey.='_'.$token->[2]->{'part'};
401: }
402: if (defined($token->[2]->{'name'})) {
403: $unikey.='_'.$token->[2]->{'name'};
404: }
405: if ($metacache{$uri.'keys'}) {
406: $metacache{$uri.'keys'}.=','.$unikey;
407: } else {
408: $metacache{$uri.'keys'}=$unikey;
409: }
410: foreach ( @{$token->[3]}) {
411: $metacache{$uri.''.$unikey.'.'.$_}=$token->[2]->{$_};
412: }
413: if (! ($metacache{$uri.''.$unikey}=$parser->get_text('/'.$entry))){
414: $metacache{$uri.''.$unikey} =
415: $metacache{$uri.''.$unikey.'.default'};
416: }
417: } # End of ($token->[0] eq 'S')
418: }
419: return \%metacache;
1.31 harris41 420: }
1.28 harris41 421:
1.55 matthew 422: ##
423: ## &getfile($filename)
424: ## Slurps up an entire file into a scalar.
425: ## Returns undef if the file does not exist
426: sub getfile {
427: my $file = shift();
428: if (! -e $file ) {
429: return undef;
430: }
431: my $fh=IO::File->new($file);
432: my $contents = '';
433: while (<$fh>) {
434: $contents .= $_;
435: }
436: return $contents;
437: }
1.28 harris41 438:
1.55 matthew 439: ########################################################
440: ########################################################
441: ### ###
442: ### Dynamic Metadata ###
443: ### ###
444: ########################################################
445: ########################################################
1.56 ! matthew 446: ##
! 447: ## Dynamic metadata description
! 448: ##
! 449: ## Field Type
! 450: ##-----------------------------------------------------------
! 451: ## count integer
! 452: ## course integer
! 453: ## course_list comma seperated list of course ids
! 454: ## avetries real
! 455: ## avetries_list comma seperated list of real numbers
! 456: ## stdno real
! 457: ## stdno_list comma seperated list of real numbers
! 458: ## usage integer
! 459: ## usage_list comma seperated list of resources
! 460: ## goto scalar
! 461: ## goto_list comma seperated list of resources
! 462: ## comefrom scalar
! 463: ## comefrom_list comma seperated list of resources
! 464: ## difficulty real
! 465: ## difficulty_list comma seperated list of real numbers
! 466: ## sequsage scalar
! 467: ## sequsage_list comma seperated list of resources
! 468: ## clear real
! 469: ## technical real
! 470: ## correct real
! 471: ## helpful real
! 472: ## depth real
! 473: ## comments html of all the comments made
! 474: ##
! 475: {
! 476:
! 477: my %DynamicData;
! 478: my %Counts;
! 479:
! 480: sub process_dynamic_metadata {
! 481: my ($user,$dom) = @_;
! 482: undef(%DynamicData);
! 483: undef(%Counts);
! 484: #
! 485: my $prodir = &propath($dom,$user);
1.55 matthew 486: #
1.56 ! matthew 487: # Read in the dynamic metadata
1.55 matthew 488: my %evaldata;
489: if (! tie(%evaldata,'GDBM_File',
490: $prodir.'/nohist_resevaldata.db',&GDBM_READER(),0640)) {
1.56 ! matthew 491: return 0;
1.55 matthew 492: }
1.56 ! matthew 493: #
! 494: # Process every stored element
! 495: while (my ($storedkey,$value) = each(%evaldata)) {
! 496: my ($source,$file,$type) = split('___',$storedkey);
! 497: $source = &unescape($source);
! 498: $file = &unescape($file);
! 499: $value = &unescape($value);
! 500: " got ".$file."\n ".$type." ".$source."\n";
! 501: if ($type =~ /^(avetries|count|difficulty|stdno|timestamp)$/) {
! 502: #
! 503: # Statistics: $source is course id
! 504: $DynamicData{$file}->{'statistics'}->{$source}->{$type}=$value;
! 505: } elsif ($type =~ /^(clear|comments|depth|technical|helpful)$/){
! 506: #
! 507: # Evaluation $source is username, check if they evaluated it
! 508: # more than once. If so, pad the entry with a space.
! 509: while(exists($DynamicData{$file}->{'evaluation'}->{$type}->{$source})) {
! 510: $source .= ' ';
1.55 matthew 511: }
1.56 ! matthew 512: $DynamicData{$file}->{'evaluation'}->{$type}->{$source}=$value;
! 513: } elsif ($type =~ /^(course|comefrom|goto|usage)$/) {
! 514: #
! 515: # Context $source is course id or resource
! 516: push(@{$DynamicData{$file}->{$type}},&unescape($source));
! 517: } else {
! 518: &log(0," ".$user."@".$dom.":Process metadata: Unable to decode ".$type);
1.55 matthew 519: }
520: }
521: untie(%evaldata);
1.56 ! matthew 522: #
! 523: # Read in the access count data
! 524: &log(7,'Reading access count data') if ($debug);
! 525: my %countdata;
! 526: if (! tie(%countdata,'GDBM_File',
! 527: $prodir.'/nohist_accesscount.db',&GDBM_READER(),0640)) {
! 528: return 0;
! 529: }
! 530: while (my ($key,$count) = each(%countdata)) {
! 531: next if ($key !~ /^$dom/);
! 532: $key = &unescape($key);
! 533: &log(8,' Count '.$key.' = '.$count) if ($debug);
! 534: $Counts{$key}=$count;
! 535: }
! 536: untie(%countdata);
! 537: if ($debug) {
! 538: &log(7,scalar(keys(%Counts)).
! 539: " Counts read for ".$user."@".$dom);
! 540: &log(7,scalar(keys(%DynamicData)).
! 541: " Dynamic metadata read for ".$user."@".$dom);
! 542: }
! 543: #
! 544: return 1;
! 545: }
! 546:
! 547: sub get_dynamic_metadata {
! 548: my ($url) = @_;
! 549: $url =~ s:^/res/::;
! 550: if (! exists($DynamicData{$url})) {
! 551: &log(7,' No dynamic data for '.$url) if ($debug);
! 552: return ();
! 553: }
! 554: my %data;
! 555: my $resdata = $DynamicData{$url};
! 556: #
! 557: # Get the statistical data
! 558: foreach my $type (qw/avetries difficulty stdno/) {
! 559: my $count;
! 560: my $sum;
! 561: my @Values;
! 562: foreach my $coursedata (values(%{$resdata->{'statistics'}})) {
! 563: if (ref($coursedata) eq 'HASH' && exists($coursedata->{$type})) {
! 564: $count++;
! 565: $sum += $coursedata->{$type};
! 566: push(@Values,$coursedata->{$type});
1.55 matthew 567: }
568: }
1.56 ! matthew 569: if ($count) {
! 570: $data{$type} = $sum/$count;
! 571: $data{$type.'_list'} = join(',',@Values);
! 572: }
! 573: }
! 574: # find the count
! 575: $data{'count'} = $Counts{$url};
! 576: #
! 577: # Get the context data
! 578: foreach my $type (qw/course goto comefrom/) {
! 579: if (defined($resdata->{$type}) &&
! 580: ref($resdata->{$type}) eq 'ARRAY') {
! 581: $data{$type} = scalar(@{$resdata->{$type}});
! 582: $data{$type.'_list'} = join(',',@{$resdata->{$type}});
! 583: }
! 584: }
! 585: if (defined($resdata->{'usage'}) &&
! 586: ref($resdata->{'usage'}) eq 'ARRAY') {
! 587: $data{'sequsage'} = scalar(@{$resdata->{'usage'}});
! 588: $data{'sequsage_list'} = join(',',@{$resdata->{'usage'}});
! 589: }
! 590: #
! 591: # Get the evaluation data
! 592: foreach my $type (qw/clear technical correct helpful depth/) {
! 593: my $count;
! 594: my $sum;
! 595: foreach my $evaluator (keys(%{$resdata->{'evaluation'}->{$type}})){
! 596: $sum += $resdata->{'evaluation'}->{$type}->{$evaluator};
! 597: $count++;
! 598: }
! 599: if ($count > 0) {
! 600: $data{$type}=$sum/$count;
! 601: }
1.44 www 602: }
1.55 matthew 603: #
1.56 ! matthew 604: # put together comments
! 605: my $comments = '<div class="LCevalcomments">';
! 606: foreach my $evaluator (keys(%{$resdata->{'evaluation'}->{'comments'}})){
! 607: $comments .= $evaluator.':'.
! 608: $resdata->{'evaluation'}->{'comments'}->{$evaluator}.'<hr />';
! 609: }
! 610: $comments .= '</div>';
! 611: #
! 612: # Log the dynamic metadata
! 613: if ($debug) {
! 614: while (my($k,$v)=each(%data)) {
! 615: &log(8," ".$k." => ".$v);
! 616: }
1.44 www 617: }
1.56 ! matthew 618: #
! 619: return %data;
1.30 www 620: }
1.28 harris41 621:
1.56 ! matthew 622: } # End of %DynamicData and %Counts scope
! 623:
1.55 matthew 624: ########################################################
625: ########################################################
626: ### ###
627: ### Counts ###
628: ### ###
629: ########################################################
630: ########################################################
631: {
1.1 harris41 632:
1.55 matthew 633: my %countext;
1.15 harris41 634:
1.55 matthew 635: sub count_type {
636: my $file=shift;
637: $file=~/\.(\w+)$/;
638: my $ext=lc($1);
639: $countext{$ext}++;
1.31 harris41 640: }
1.1 harris41 641:
1.55 matthew 642: sub write_type_count {
643: open(RESCOUNT,'>/home/httpd/html/lon-status/rescount.txt');
644: while (my ($extension,$count) = each(%countext)) {
645: print RESCOUNT $extension.'='.$count.'&';
1.47 www 646: }
1.55 matthew 647: print RESCOUNT 'time='.time."\n";
648: close(RESCOUNT);
1.31 harris41 649: }
1.27 www 650:
1.55 matthew 651: } # end of scope for %countext
1.34 matthew 652:
1.55 matthew 653: {
1.34 matthew 654:
1.55 matthew 655: my %copyrights;
1.44 www 656:
1.55 matthew 657: sub count_copyright {
658: $copyrights{@_[0]}++;
1.31 harris41 659: }
1.33 matthew 660:
1.55 matthew 661: sub write_copyright_count {
662: open(COPYCOUNT,'>/home/httpd/html/lon-status/copyrightcount.txt');
663: while (my ($copyright,$count) = each(%copyrights)) {
664: print COPYCOUNT $copyright.'='.$count.'&';
1.31 harris41 665: }
1.55 matthew 666: print COPYCOUNT 'time='.time."\n";
667: close(COPYCOUNT);
1.31 harris41 668: }
1.28 harris41 669:
1.55 matthew 670: } # end of scope for %copyrights
1.28 harris41 671:
1.55 matthew 672: ########################################################
673: ########################################################
674: ### ###
675: ### Miscellanous Utility Routines ###
676: ### ###
677: ########################################################
678: ########################################################
679: ##
680: ## &ishome($username)
681: ## Returns 1 if $username is a LON-CAPA author, 0 otherwise
682: ## (copied from lond, modification of the return value)
1.31 harris41 683: sub ishome {
684: my $author=shift;
685: $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
686: my ($udom,$uname)=split(/\//,$author);
687: my $proname=propath($udom,$uname);
688: if (-e $proname) {
689: return 1;
690: } else {
691: return 0;
692: }
693: }
1.28 harris41 694:
1.55 matthew 695: ##
696: ## &propath($udom,$uname)
697: ## Returns the path to the users LON-CAPA directory
698: ## (copied from lond)
1.31 harris41 699: sub propath {
700: my ($udom,$uname)=@_;
701: $udom=~s/\W//g;
702: $uname=~s/\W//g;
703: my $subdir=$uname.'__';
704: $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
705: my $proname="$perlvar{'lonUsersDir'}/$udom/$subdir/$uname";
706: return $proname;
707: }
1.28 harris41 708:
1.55 matthew 709: ##
710: ## &sqltime($timestamp)
711: ##
712: ## Convert perl $timestamp to MySQL time. MySQL expects YYYY-MM-DD HH:MM:SS
713: ##
1.31 harris41 714: sub sqltime {
1.55 matthew 715: my ($time) = @_;
716: my $mysqltime;
717: if ($time =~
718: /(\d+)-(\d+)-(\d+) # YYYY-MM-DD
719: \s # a space
720: (\d+):(\d+):(\d+) # HH:MM::SS
721: /x ) {
722: # Some of the .meta files have the time in mysql
723: # format already, so just make sure they are 0 padded and
724: # pass them back.
725: $mysqltime = sprintf('%04d-%02d-%02d %02d:%02d:%02d',
726: $1,$2,$3,$4,$5,$6);
727: } elsif ($time =~ /^\d+$/) {
728: my @TimeData = gmtime($time);
729: # Alter the month to be 1-12 instead of 0-11
730: $TimeData[4]++;
731: # Alter the year to be from 0 instead of from 1900
732: $TimeData[5]+=1900;
733: $mysqltime = sprintf('%04d-%02d-%02d %02d:%02d:%02d',
734: @TimeData[5,4,3,2,1,0]);
1.56 ! matthew 735: } elsif (! defined($time) || $time == 0) {
! 736: $mysqltime = 0;
1.55 matthew 737: } else {
1.56 ! matthew 738: &log(0," sqltime:Unable to decode time ".$time);
1.55 matthew 739: $mysqltime = 0;
740: }
741: return $mysqltime;
1.31 harris41 742: }
1.28 harris41 743:
1.55 matthew 744: ##
745: ## &declutter($filename)
746: ## Given a filename, returns a url for the filename.
747: sub declutter {
748: my $thisfn=shift;
749: $thisfn=~s/^$perlvar{'lonDocRoot'}//;
750: $thisfn=~s/^\///;
751: $thisfn=~s/^res\///;
752: return $thisfn;
1.31 harris41 753: }
1.28 harris41 754:
1.55 matthew 755: ##
756: ## Escape / Unescape special characters
757: sub unescape {
758: my $str=shift;
759: $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
760: return $str;
1.31 harris41 761: }
1.28 harris41 762:
1.55 matthew 763: sub escape {
764: my $str=shift;
765: $str =~ s/(\W)/"%".unpack('H2',$1)/eg;
766: return $str;
1.45 www 767: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>