Annotation of loncom/metadata_database/searchcat.pl, revision 1.65
1.1 harris41 1: #!/usr/bin/perl
2: # The LearningOnline Network
3: # searchcat.pl "Search Catalog" batch script
1.16 harris41 4: #
1.65 ! albertel 5: # $Id: searchcat.pl,v 1.64 2005/08/11 18:19:41 albertel 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;
1.65 ! albertel 68: BEGIN {
! 69: eval "use Apache2::compat();";
! 70: };
1.55 matthew 71: use DBI;
1.17 harris41 72: use lib '/home/httpd/lib/perl/';
1.55 matthew 73: use LONCAPA::lonmetadata;
1.17 harris41 74:
1.56 matthew 75: use Getopt::Long;
1.1 harris41 76: use IO::File;
77: use HTML::TokeParser;
1.21 www 78: use GDBM_File;
1.24 www 79: use POSIX qw(strftime mktime);
1.56 matthew 80:
1.63 matthew 81: use Apache::lonnet();
1.62 matthew 82:
1.55 matthew 83: use File::Find;
1.1 harris41 84:
1.56 matthew 85: #
86: # Set up configuration options
1.63 matthew 87: my ($simulate,$oneuser,$help,$verbose,$logfile,$debug);
1.56 matthew 88: GetOptions (
89: 'help' => \$help,
90: 'simulate' => \$simulate,
91: 'only=s' => \$oneuser,
92: 'verbose=s' => \$verbose,
93: 'debug' => \$debug,
94: );
95:
96: if ($help) {
97: print <<"ENDHELP";
98: $0
99: Rebuild and update the LON-CAPA metadata database.
100: Options:
101: -help Print this help
102: -simulate Do not modify the database.
103: -only=user Only compute for the given user. Implies -simulate
104: -verbose=val Sets logging level, val must be a number
105: -debug Turns on debugging output
106: ENDHELP
107: exit 0;
108: }
109:
110: if (! defined($debug)) {
111: $debug = 0;
112: }
113:
114: if (! defined($verbose)) {
115: $verbose = 0;
116: }
117:
118: if (defined($oneuser)) {
119: $simulate=1;
120: }
121:
1.55 matthew 122: ##
123: ## Use variables for table names so we can test this routine a little easier
124: my $oldname = 'metadata';
1.59 matthew 125: my $newname = 'newmetadata'.$$; # append pid to have unique temporary table
1.45 www 126:
1.55 matthew 127: #
128: # Only run if machine is a library server
1.63 matthew 129: exit if ($Apache::lonnet::perlvar{'lonRole'} ne 'library');
1.55 matthew 130: #
131: # Make sure this process is running from user=www
132: my $wwwid=getpwnam('www');
133: if ($wwwid!=$<) {
1.63 matthew 134: my $emailto="$Apache::lonnet::perlvar{'lonAdmEMail'},$Apache::lonnet::perlvar{'lonSysEMail'}";
135: my $subj="LON: $Apache::lonnet::perlvar{'lonHostID'} User ID mismatch";
1.55 matthew 136: system("echo 'User ID mismatch. searchcat.pl must be run as user www.' |\
1.63 matthew 137: mail -s '$subj' $emailto > /dev/null");
1.55 matthew 138: exit 1;
139: }
140: #
141: # Let people know we are running
1.63 matthew 142: open(LOG,'>>'.$Apache::lonnet::perlvar{'lonDaemons'}.'/logs/searchcat.log');
1.56 matthew 143: &log(0,'==== Searchcat Run '.localtime()."====");
1.57 matthew 144:
145:
1.56 matthew 146: if ($debug) {
147: &log(0,'simulating') if ($simulate);
148: &log(0,'only processing user '.$oneuser) if ($oneuser);
149: &log(0,'verbosity level = '.$verbose);
150: }
1.55 matthew 151: #
152: # Connect to database
153: my $dbh;
1.63 matthew 154: if (! ($dbh = DBI->connect("DBI:mysql:loncapa","www",$Apache::lonnet::perlvar{'lonSqlAccess'},
1.55 matthew 155: { RaiseError =>0,PrintError=>0}))) {
1.56 matthew 156: &log(0,"Cannot connect to database!");
1.55 matthew 157: die "MySQL Error: Cannot connect to database!\n";
158: }
159: # This can return an error and still be okay, so we do not bother checking.
160: # (perhaps it should be more robust and check for specific errors)
161: $dbh->do('DROP TABLE IF EXISTS '.$newname);
162: #
163: # Create the new table
164: my $request = &LONCAPA::lonmetadata::create_metadata_storage($newname);
165: $dbh->do($request);
166: if ($dbh->err) {
167: $dbh->disconnect();
1.56 matthew 168: &log(0,"MySQL Error Create: ".$dbh->errstr);
1.55 matthew 169: die $dbh->errstr;
170: }
171: #
172: # find out which users we need to examine
1.63 matthew 173: my @domains = sort(&Apache::lonnet::current_machine_domains());
174: &log(9,'domains ="'.join('","',@domains).'"');
1.62 matthew 175:
176: foreach my $dom (@domains) {
177: &log(9,'domain = '.$dom);
1.63 matthew 178: opendir(RESOURCES,"$Apache::lonnet::perlvar{'lonDocRoot'}/res/$dom");
1.62 matthew 179: my @homeusers =
180: grep {
1.63 matthew 181: &ishome("$Apache::lonnet::perlvar{'lonDocRoot'}/res/$dom/$_");
1.62 matthew 182: } grep {
183: !/^\.\.?$/;
184: } readdir(RESOURCES);
185: closedir RESOURCES;
186: &log(5,'users = '.$dom.':'.join(',',@homeusers));
187: #
188: if ($oneuser) {
189: @homeusers=($oneuser);
190: }
191: #
192: # Loop through the users
193: foreach my $user (@homeusers) {
194: &log(0,"=== User: ".$user);
195: &process_dynamic_metadata($user,$dom);
196: #
197: # Use File::Find to get the files we need to read/modify
198: find(
199: {preprocess => \&only_meta_files,
200: #wanted => \&print_filename,
201: #wanted => \&log_metadata,
202: wanted => \&process_meta_file,
1.63 matthew 203: }, join('/',($Apache::lonnet::perlvar{'lonDocRoot'},'res',$dom,$user)) );
1.62 matthew 204: }
1.55 matthew 205: }
206: #
207: # Rename the table
1.56 matthew 208: if (! $simulate) {
209: $dbh->do('DROP TABLE IF EXISTS '.$oldname);
210: if (! $dbh->do('RENAME TABLE '.$newname.' TO '.$oldname)) {
211: &log(0,"MySQL Error Rename: ".$dbh->errstr);
212: die $dbh->errstr;
213: } else {
214: &log(1,"MySQL table rename successful.");
215: }
1.55 matthew 216: }
217: if (! $dbh->disconnect) {
1.56 matthew 218: &log(0,"MySQL Error Disconnect: ".$dbh->errstr);
1.55 matthew 219: die $dbh->errstr;
220: }
221: ##
222: ## Finished!
1.56 matthew 223: &log(0,"==== Searchcat completed ".localtime()." ====");
1.55 matthew 224: close(LOG);
1.21 www 225:
1.55 matthew 226: &write_type_count();
227: &write_copyright_count();
1.36 www 228:
1.55 matthew 229: exit 0;
1.28 harris41 230:
1.56 matthew 231: ##
232: ## Status logging routine. Inputs: $level, $message
233: ##
234: ## $level 0 should be used for normal output and error messages
235: ##
236: ## $message does not need to end with \n. In the case of errors
237: ## the message should contain as much information as possible to
238: ## help in diagnosing the problem.
239: ##
240: sub log {
241: my ($level,$message)=@_;
242: $level = 0 if (! defined($level));
243: if ($verbose >= $level) {
244: print LOG $message.$/;
245: }
246: }
247:
1.55 matthew 248: ########################################################
249: ########################################################
250: ### ###
251: ### File::Find support routines ###
252: ### ###
253: ########################################################
254: ########################################################
255: ##
256: ## &only_meta_files
257: ##
258: ## Called by File::Find.
259: ## Takes a list of files/directories in and returns a list of files/directories
260: ## to search.
261: sub only_meta_files {
262: my @PossibleFiles = @_;
263: my @ChosenFiles;
264: foreach my $file (@PossibleFiles) {
265: if ( ($file =~ /\.meta$/ && # Ends in meta
266: $file !~ /\.\d+\.[^\.]+\.meta$/ # is not for a prior version
267: ) || (-d $file )) { # directories are okay
268: # but we do not want /. or /..
269: push(@ChosenFiles,$file);
270: }
1.38 www 271: }
1.55 matthew 272: return @ChosenFiles;
1.38 www 273: }
274:
1.55 matthew 275: ##
276: ##
277: ## Debugging routines, use these for 'wanted' in the File::Find call
278: ##
279: sub print_filename {
280: my ($file) = $_;
281: my $fullfilename = $File::Find::name;
1.56 matthew 282: if ($debug) {
283: if (-d $file) {
284: &log(5," Got directory ".$fullfilename);
285: } else {
286: &log(5," Got file ".$fullfilename);
287: }
1.38 www 288: }
1.55 matthew 289: $_=$file;
1.38 www 290: }
1.28 harris41 291:
1.55 matthew 292: sub log_metadata {
293: my ($file) = $_;
294: my $fullfilename = $File::Find::name;
295: return if (-d $fullfilename); # No need to do anything here for directories
1.56 matthew 296: if ($debug) {
297: &log(6,$fullfilename);
298: my $ref=&metadata($fullfilename);
299: if (! defined($ref)) {
300: &log(6," No data");
301: return;
302: }
303: while (my($key,$value) = each(%$ref)) {
304: &log(6," ".$key." => ".$value);
305: }
306: &count_copyright($ref->{'copyright'});
1.55 matthew 307: }
308: $_=$file;
1.31 harris41 309: }
1.21 www 310:
1.55 matthew 311: ##
312: ## process_meta_file
313: ## Called by File::Find.
314: ## Only input is the filename in $_.
315: sub process_meta_file {
316: my ($file) = $_;
1.56 matthew 317: my $filename = $File::Find::name; # full filename
1.55 matthew 318: return if (-d $filename); # No need to do anything here for directories
319: #
1.56 matthew 320: &log(3,$filename) if ($debug);
1.55 matthew 321: #
322: my $ref=&metadata($filename);
323: #
324: # $url is the original file url, not the metadata file
1.61 matthew 325: my $target = $filename;
326: $target =~ s/\.meta$//;
327: my $url='/res/'.&declutter($target);
1.56 matthew 328: &log(3," ".$url) if ($debug);
1.55 matthew 329: #
330: # Ignore some files based on their metadata
331: if ($ref->{'obsolete'}) {
1.56 matthew 332: &log(3,"obsolete") if ($debug);
1.55 matthew 333: return;
334: }
335: &count_copyright($ref->{'copyright'});
336: if ($ref->{'copyright'} eq 'private') {
1.56 matthew 337: &log(3,"private") if ($debug);
1.55 matthew 338: return;
339: }
340: #
341: # Find the dynamic metadata
342: my %dyn;
343: if ($url=~ m:/default$:) {
344: $url=~ s:/default$:/:;
1.56 matthew 345: &log(3,"Skipping dynamic data") if ($debug);
1.55 matthew 346: } else {
1.56 matthew 347: &log(3,"Retrieving dynamic data") if ($debug);
348: %dyn=&get_dynamic_metadata($url);
1.55 matthew 349: &count_type($url);
350: }
351: #
1.61 matthew 352: if (! defined($ref->{'creationdate'}) ||
353: $ref->{'creationdate'} =~ /^\s*$/) {
354: $ref->{'creationdate'} = (stat($target))[9];
355: }
356: if (! defined($ref->{'lastrevisiondate'}) ||
357: $ref->{'lastrevisiondate'} =~ /^\s*$/) {
358: $ref->{'lastrevisiondate'} = (stat($target))[9];
359: }
1.55 matthew 360: $ref->{'creationdate'} = &sqltime($ref->{'creationdate'});
361: $ref->{'lastrevisiondate'} = &sqltime($ref->{'lastrevisiondate'});
362: my %Data = (
363: %$ref,
364: %dyn,
365: 'url'=>$url,
366: 'version'=>'current');
1.56 matthew 367: if (! $simulate) {
368: my ($count,$err) = &LONCAPA::lonmetadata::store_metadata($dbh,$newname,
369: \%Data);
370: if ($err) {
371: &log(0,"MySQL Error Insert: ".$err);
372: }
373: if ($count < 1) {
374: &log(0,"Unable to insert record into MySQL database for $url");
375: }
1.55 matthew 376: }
377: #
378: # Reset $_ before leaving
379: $_ = $file;
380: }
381:
382: ########################################################
383: ########################################################
384: ### ###
385: ### &metadata($uri) ###
386: ### Retrieve metadata for the given file ###
387: ### ###
388: ########################################################
389: ########################################################
390: sub metadata {
391: my ($uri)=@_;
392: my %metacache=();
393: $uri=&declutter($uri);
394: my $filename=$uri;
395: $uri=~s/\.meta$//;
396: $uri='';
397: if ($filename !~ /\.meta$/) {
398: $filename.='.meta';
399: }
1.63 matthew 400: my $metastring=&getfile($Apache::lonnet::perlvar{'lonDocRoot'}.'/res/'.$filename);
1.55 matthew 401: return undef if (! defined($metastring));
402: my $parser=HTML::TokeParser->new(\$metastring);
403: my $token;
404: while ($token=$parser->get_token) {
405: if ($token->[0] eq 'S') {
406: my $entry=$token->[1];
407: my $unikey=$entry;
408: if (defined($token->[2]->{'part'})) {
409: $unikey.='_'.$token->[2]->{'part'};
410: }
411: if (defined($token->[2]->{'name'})) {
412: $unikey.='_'.$token->[2]->{'name'};
413: }
414: if ($metacache{$uri.'keys'}) {
415: $metacache{$uri.'keys'}.=','.$unikey;
416: } else {
417: $metacache{$uri.'keys'}=$unikey;
418: }
419: foreach ( @{$token->[3]}) {
420: $metacache{$uri.''.$unikey.'.'.$_}=$token->[2]->{$_};
421: }
422: if (! ($metacache{$uri.''.$unikey}=$parser->get_text('/'.$entry))){
423: $metacache{$uri.''.$unikey} =
424: $metacache{$uri.''.$unikey.'.default'};
425: }
426: } # End of ($token->[0] eq 'S')
427: }
428: return \%metacache;
1.31 harris41 429: }
1.28 harris41 430:
1.55 matthew 431: ##
432: ## &getfile($filename)
433: ## Slurps up an entire file into a scalar.
434: ## Returns undef if the file does not exist
435: sub getfile {
436: my $file = shift();
437: if (! -e $file ) {
438: return undef;
439: }
440: my $fh=IO::File->new($file);
441: my $contents = '';
442: while (<$fh>) {
443: $contents .= $_;
444: }
445: return $contents;
446: }
1.28 harris41 447:
1.55 matthew 448: ########################################################
449: ########################################################
450: ### ###
451: ### Dynamic Metadata ###
452: ### ###
453: ########################################################
454: ########################################################
1.56 matthew 455: ##
1.58 www 456: ## Dynamic metadata description (incomplete)
457: ##
458: ## For a full description of all fields,
459: ## see LONCAPA::lonmetadata
1.56 matthew 460: ##
461: ## Field Type
462: ##-----------------------------------------------------------
463: ## count integer
464: ## course integer
1.58 www 465: ## course_list comma separated list of course ids
1.56 matthew 466: ## avetries real
1.58 www 467: ## avetries_list comma separated list of real numbers
1.56 matthew 468: ## stdno real
1.58 www 469: ## stdno_list comma separated list of real numbers
1.56 matthew 470: ## usage integer
1.58 www 471: ## usage_list comma separated list of resources
1.56 matthew 472: ## goto scalar
1.58 www 473: ## goto_list comma separated list of resources
1.56 matthew 474: ## comefrom scalar
1.58 www 475: ## comefrom_list comma separated list of resources
1.56 matthew 476: ## difficulty real
1.58 www 477: ## difficulty_list comma separated list of real numbers
1.56 matthew 478: ## sequsage scalar
1.58 www 479: ## sequsage_list comma separated list of resources
1.56 matthew 480: ## clear real
481: ## technical real
482: ## correct real
483: ## helpful real
484: ## depth real
485: ## comments html of all the comments made
486: ##
487: {
488:
489: my %DynamicData;
490: my %Counts;
491:
492: sub process_dynamic_metadata {
493: my ($user,$dom) = @_;
494: undef(%DynamicData);
495: undef(%Counts);
496: #
497: my $prodir = &propath($dom,$user);
1.55 matthew 498: #
1.56 matthew 499: # Read in the dynamic metadata
1.55 matthew 500: my %evaldata;
501: if (! tie(%evaldata,'GDBM_File',
502: $prodir.'/nohist_resevaldata.db',&GDBM_READER(),0640)) {
1.56 matthew 503: return 0;
1.55 matthew 504: }
1.56 matthew 505: #
1.57 matthew 506: %DynamicData = &LONCAPA::lonmetadata::process_reseval_data(\%evaldata);
1.55 matthew 507: untie(%evaldata);
1.62 matthew 508: $DynamicData{'domain'} = $dom;
1.64 albertel 509: #print('user = '.$user.' domain = '.$dom.$/);
1.56 matthew 510: #
511: # Read in the access count data
512: &log(7,'Reading access count data') if ($debug);
513: my %countdata;
514: if (! tie(%countdata,'GDBM_File',
515: $prodir.'/nohist_accesscount.db',&GDBM_READER(),0640)) {
516: return 0;
517: }
518: while (my ($key,$count) = each(%countdata)) {
519: next if ($key !~ /^$dom/);
520: $key = &unescape($key);
521: &log(8,' Count '.$key.' = '.$count) if ($debug);
522: $Counts{$key}=$count;
523: }
524: untie(%countdata);
525: if ($debug) {
526: &log(7,scalar(keys(%Counts)).
527: " Counts read for ".$user."@".$dom);
528: &log(7,scalar(keys(%DynamicData)).
529: " Dynamic metadata read for ".$user."@".$dom);
530: }
531: #
532: return 1;
533: }
534:
535: sub get_dynamic_metadata {
536: my ($url) = @_;
537: $url =~ s:^/res/::;
1.57 matthew 538: my %data = &LONCAPA::lonmetadata::process_dynamic_metadata($url,
539: \%DynamicData);
1.56 matthew 540: # find the count
541: $data{'count'} = $Counts{$url};
542: #
543: # Log the dynamic metadata
544: if ($debug) {
545: while (my($k,$v)=each(%data)) {
546: &log(8," ".$k." => ".$v);
547: }
1.44 www 548: }
1.56 matthew 549: return %data;
1.30 www 550: }
1.28 harris41 551:
1.56 matthew 552: } # End of %DynamicData and %Counts scope
553:
1.55 matthew 554: ########################################################
555: ########################################################
556: ### ###
557: ### Counts ###
558: ### ###
559: ########################################################
560: ########################################################
561: {
1.1 harris41 562:
1.55 matthew 563: my %countext;
1.15 harris41 564:
1.55 matthew 565: sub count_type {
566: my $file=shift;
567: $file=~/\.(\w+)$/;
568: my $ext=lc($1);
569: $countext{$ext}++;
1.31 harris41 570: }
1.1 harris41 571:
1.55 matthew 572: sub write_type_count {
573: open(RESCOUNT,'>/home/httpd/html/lon-status/rescount.txt');
574: while (my ($extension,$count) = each(%countext)) {
575: print RESCOUNT $extension.'='.$count.'&';
1.47 www 576: }
1.55 matthew 577: print RESCOUNT 'time='.time."\n";
578: close(RESCOUNT);
1.31 harris41 579: }
1.27 www 580:
1.55 matthew 581: } # end of scope for %countext
1.34 matthew 582:
1.55 matthew 583: {
1.34 matthew 584:
1.55 matthew 585: my %copyrights;
1.44 www 586:
1.55 matthew 587: sub count_copyright {
588: $copyrights{@_[0]}++;
1.31 harris41 589: }
1.33 matthew 590:
1.55 matthew 591: sub write_copyright_count {
592: open(COPYCOUNT,'>/home/httpd/html/lon-status/copyrightcount.txt');
593: while (my ($copyright,$count) = each(%copyrights)) {
594: print COPYCOUNT $copyright.'='.$count.'&';
1.31 harris41 595: }
1.55 matthew 596: print COPYCOUNT 'time='.time."\n";
597: close(COPYCOUNT);
1.31 harris41 598: }
1.28 harris41 599:
1.55 matthew 600: } # end of scope for %copyrights
1.28 harris41 601:
1.55 matthew 602: ########################################################
603: ########################################################
604: ### ###
605: ### Miscellanous Utility Routines ###
606: ### ###
607: ########################################################
608: ########################################################
609: ##
610: ## &ishome($username)
611: ## Returns 1 if $username is a LON-CAPA author, 0 otherwise
612: ## (copied from lond, modification of the return value)
1.31 harris41 613: sub ishome {
614: my $author=shift;
615: $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
616: my ($udom,$uname)=split(/\//,$author);
617: my $proname=propath($udom,$uname);
618: if (-e $proname) {
619: return 1;
620: } else {
621: return 0;
622: }
623: }
1.28 harris41 624:
1.55 matthew 625: ##
626: ## &propath($udom,$uname)
627: ## Returns the path to the users LON-CAPA directory
628: ## (copied from lond)
1.31 harris41 629: sub propath {
630: my ($udom,$uname)=@_;
631: $udom=~s/\W//g;
632: $uname=~s/\W//g;
633: my $subdir=$uname.'__';
634: $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
1.63 matthew 635: my $proname="$Apache::lonnet::perlvar{'lonUsersDir'}/$udom/$subdir/$uname";
1.31 harris41 636: return $proname;
637: }
1.28 harris41 638:
1.55 matthew 639: ##
640: ## &sqltime($timestamp)
641: ##
642: ## Convert perl $timestamp to MySQL time. MySQL expects YYYY-MM-DD HH:MM:SS
643: ##
1.31 harris41 644: sub sqltime {
1.55 matthew 645: my ($time) = @_;
646: my $mysqltime;
647: if ($time =~
648: /(\d+)-(\d+)-(\d+) # YYYY-MM-DD
649: \s # a space
650: (\d+):(\d+):(\d+) # HH:MM::SS
651: /x ) {
652: # Some of the .meta files have the time in mysql
653: # format already, so just make sure they are 0 padded and
654: # pass them back.
655: $mysqltime = sprintf('%04d-%02d-%02d %02d:%02d:%02d',
656: $1,$2,$3,$4,$5,$6);
657: } elsif ($time =~ /^\d+$/) {
658: my @TimeData = gmtime($time);
659: # Alter the month to be 1-12 instead of 0-11
660: $TimeData[4]++;
661: # Alter the year to be from 0 instead of from 1900
662: $TimeData[5]+=1900;
663: $mysqltime = sprintf('%04d-%02d-%02d %02d:%02d:%02d',
664: @TimeData[5,4,3,2,1,0]);
1.56 matthew 665: } elsif (! defined($time) || $time == 0) {
666: $mysqltime = 0;
1.55 matthew 667: } else {
1.56 matthew 668: &log(0," sqltime:Unable to decode time ".$time);
1.55 matthew 669: $mysqltime = 0;
670: }
671: return $mysqltime;
1.31 harris41 672: }
1.28 harris41 673:
1.55 matthew 674: ##
675: ## &declutter($filename)
676: ## Given a filename, returns a url for the filename.
677: sub declutter {
678: my $thisfn=shift;
1.63 matthew 679: $thisfn=~s/^$Apache::lonnet::perlvar{'lonDocRoot'}//;
1.55 matthew 680: $thisfn=~s/^\///;
681: $thisfn=~s/^res\///;
682: return $thisfn;
1.31 harris41 683: }
1.28 harris41 684:
1.55 matthew 685: ##
686: ## Escape / Unescape special characters
687: sub unescape {
688: my $str=shift;
689: $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
690: return $str;
1.31 harris41 691: }
1.28 harris41 692:
1.55 matthew 693: sub escape {
694: my $str=shift;
695: $str =~ s/(\W)/"%".unpack('H2',$1)/eg;
696: return $str;
1.45 www 697: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>