[system] / trunk / webwork-modperl / lib / WeBWorK / Utils.pm Repository:
ViewVC logotype

View of /trunk/webwork-modperl/lib/WeBWorK/Utils.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1663 - (download) (as text) (annotate)
Tue Dec 9 01:12:32 2003 UTC (9 years, 6 months ago) by sh002i
File size: 9674 byte(s)
Normalized headers. All files now contain the text below as a header.
This is important since all files now (a) use the full name of the
package, (b) assign copyright to "The WeBWorK Project", (c) give the
full path of the file (relative to CVSROOT) instead of simply the file
name, and (d) include license and warranty information.

Here is the new header:

################################################################################
# WeBWorK Online Homework Delivery System
# Copyright © 2000-2003 The WeBWorK Projcct, http://openwebwork.sf.net/
# $CVSHeader$
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
# Artistic License for more details.
################################################################################

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader$
    5 #
    6 # This program is free software; you can redistribute it and/or modify it under
    7 # the terms of either: (a) the GNU General Public License as published by the
    8 # Free Software Foundation; either version 2, or (at your option) any later
    9 # version, or (b) the "Artistic License" which comes with this package.
   10 #
   11 # This program is distributed in the hope that it will be useful, but WITHOUT
   12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   13 # FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
   14 # Artistic License for more details.
   15 ################################################################################
   16 
   17 package WeBWorK::Utils;
   18 use base qw(Exporter);
   19 
   20 =head1 NAME
   21 
   22 WeBWorK::Utils - useful utilities used by other WeBWorK modules.
   23 
   24 =cut
   25 
   26 use strict;
   27 use warnings;
   28 #use Apache::DB;
   29 use Date::Format;
   30 use Date::Parse;
   31 use Errno;
   32 use File::Path qw(rmtree);
   33 
   34 use constant MKDIR_ATTEMPTS => 10;
   35 
   36 our @EXPORT    = ();
   37 our @EXPORT_OK = qw(
   38   runtime_use
   39   readFile
   40   readDirectory
   41   formatDateTime
   42   parseDateTime
   43   writeLog
   44   writeCourseLog
   45   writeTimingLogEntry
   46   list2hash
   47   max
   48   dbDecode
   49   dbEncode
   50   decodeAnswers
   51   encodeAnswers
   52   ref2string
   53   sortByName
   54   makeTempDirectory
   55   removeTempDirectory
   56   pretty_print_rh
   57   cryptPassword
   58 );
   59 
   60 sub runtime_use($) {
   61   return unless @_;
   62   eval "package Main; require $_[0]; import $_[0]";
   63   die $@ if $@;
   64 }
   65 
   66 #sub backtrace {
   67 # my ($style) = @_;
   68 # $style = "warn" unless $style;
   69 # my @bt = DB->backtrace;
   70 # shift @bt; # Remove "backtrace" from the backtrace;
   71 # if ($style eq "die") {
   72 #   die join "\n", @bt;
   73 # } elsif ($style eq "warn") {
   74 #   warn join "\n", @bt;
   75 # } elsif ($style eq "print") {
   76 #   print join "\n", @bt;
   77 # } elsif ($style eq "return") {
   78 #   return @bt;
   79 # }
   80 #}
   81 
   82 sub readFile($) {
   83   my $fileName = shift;
   84   local $/ = undef; # slurp the whole thing into one string
   85   open my $dh, "<", $fileName
   86     or die "failed to read file $fileName: $!";
   87   my $result = <$dh>;
   88   close $dh;
   89   return $result;
   90 }
   91 
   92 sub readDirectory($) {
   93   my $dirName = shift;
   94   opendir my $dh, $dirName
   95     or die "Failed to read directory $dirName: $!";
   96   my @result = readdir $dh;
   97   close $dh;
   98   return @result;
   99 }
  100 
  101 sub formatDateTime($) {
  102   my $dateTime = shift;
  103   # "standard" WeBWorK date/time format (for set definition files):
  104   # %m  month number, starting with 01
  105   # %d  numeric day of the month, with leading zeros (eg 01..31)
  106   # %y  year (2 digits)
  107   # %I  hour, 12 hour clock, leading 0's)
  108   # %M  minute, leading 0's
  109   # %P  am or pm (Yes %p and %P are backwards :)
  110   #return time2str("%m/%d/%y %I:%M%P", $dateTime);
  111   return time2str("%m/%d/%y at %I:%M%P", $dateTime);
  112 }
  113 
  114 sub parseDateTime($) {
  115   my $string = shift;
  116   # need to bring our string from  "%m/%d/%y at %I:%M%P" to "%m/%d/%y %I:%M%P" format.
  117   $string =~ s/\bat\b/ /;
  118   return str2time($string);
  119 }
  120 
  121 sub writeLog($$@) {
  122   my ($ce, $facility, @message) = @_;
  123   unless ($ce->{webworkFiles}->{logs}->{$facility}) {
  124     warn "There is no log file for the $facility facility defined.\n";
  125     return;
  126   }
  127   my $logFile = $ce->{webworkFiles}->{logs}->{$facility};
  128   local *LOG;
  129   if (open LOG, ">>", $logFile) {
  130     print LOG "[", time2str("%a %b %d %H:%M:%S %Y", time), "] @message\n";
  131     close LOG;
  132   } else {
  133     warn "failed to open $logFile for writing: $!";
  134   }
  135 }
  136 
  137 sub writeCourseLog($$@) {
  138   my ($ce, $facility, @message) = @_;
  139   unless ($ce->{courseFiles}->{logs}->{$facility}) {
  140     warn "There is no course log file for the $facility facility defined.\n";
  141     return;
  142   }
  143   my $logFile = $ce->{courseFiles}->{logs}->{$facility};
  144   local *LOG;
  145   if (open LOG, ">>", $logFile) {
  146     print LOG "[", time2str("%a %b %d %H:%M:%S %Y", time), "] @message\n";
  147     close LOG;
  148   } else {
  149     warn "failed to open $logFile for writing: $!";
  150   }
  151 }
  152 
  153 
  154 # $ce - a WeBWork::CourseEnvironment object
  155 # $function - fully qualified function name
  156 # $details - any information, do not use the characters '[' or ']'
  157 # $beginEnd - the string "begin", "intermediate", or "end"
  158 # use the intermediate step begun or completed for INTERMEDIATE
  159 # use an empty string for $details when calling for END
  160 sub writeTimingLogEntry($$$$) {
  161   my ($ce, $function, $details, $beginEnd) = @_;
  162   return unless defined $ce->{webworkFiles}->{logs}->{timing};
  163   $beginEnd = ($beginEnd eq "begin") ? ">" : ($beginEnd eq "end") ? "<" : "-";
  164   writeLog($ce, "timing", "$$ ".time." $beginEnd $function [$details]");
  165 }
  166 
  167 sub list2hash {
  168   map {$_ => "0"} @_;
  169 }
  170 
  171 sub max {
  172   my $soFar;
  173   foreach my $item (@_) {
  174     $soFar = $item unless defined $soFar;
  175     if ($item > $soFar) {
  176       $soFar = $item;
  177     }
  178   }
  179   return defined $soFar ? $soFar : 0;
  180 }
  181 
  182 sub decodeAnswers($) {
  183   my $string = shift;
  184   return unless defined $string and $string;
  185   my @array = split m/##/, $string;
  186   $array[$_] =~ s/\\#\\/#/g foreach 0 .. $#array;
  187   push @array, "" if @array%2;
  188   return @array; # it's actually a hash ;)
  189 }
  190 
  191 sub encodeAnswers(\%\@) {
  192   my %hash = %{ shift() };
  193   my @order = @{ shift() };
  194   my $string;
  195   foreach my $name (@order) {
  196     my $value = defined $hash{$name} ? $hash{$name} : "";
  197     $name  =~ s/#/\\#\\/g; # this is a WEIRD way to escape things
  198     $value =~ s/#/\\#\\/g; # and it's not my fault!
  199     if ($value =~ m/\\$/) {
  200       # if the value ends with a backslash, string2hash will
  201       # interpret that as a normal escape sequence (not part
  202       # of the weird pound escape sequence) if the next
  203       # character is &. So we have to protect against this.
  204       # will adding a spcae at the end of the last answer
  205       # hurt anything? i don't think so...
  206       $value .= " ";
  207     }
  208     $string .= "$name##$value##"; # this is also not my fault
  209   }
  210   $string =~ s/##$//; # remove last pair of hashs
  211   return $string;
  212 }
  213 
  214 sub ref2string($;$);
  215 sub ref2string($;$) {
  216   my $ref = shift;
  217   my $dontExpand = shift || {};
  218   my $refType = ref $ref;
  219   my $result;
  220   if ($refType and not $dontExpand->{$refType}) {
  221     my $baseType = refBaseType($ref);
  222     $result .= '<font size="1" color="grey">' . $refType;
  223     $result .= " ($baseType)" if $baseType and $refType ne $baseType;
  224     $result .= ":</font><br>";
  225     $result .= '<table border="1" cellpadding="2">';
  226     if ($baseType eq "HASH") {
  227       my %hash = %$ref;
  228       foreach (sort keys %hash) {
  229         $result .= '<tr valign="top">';
  230         $result .= "<td>$_</td>";
  231         $result .= "<td>" . ref2string($hash{$_}, $dontExpand) . "</td>";
  232         $result .= "</tr>";
  233       }
  234     } elsif ($baseType eq "ARRAY") {
  235       my @array = @$ref;
  236       # special case for Problem, Set, and User objects, which are defined
  237       # using lists and contain a @FIELDS package variable:
  238       no strict 'refs';
  239       my @FIELDS = eval { @{$refType."::FIELDS"} };
  240       use strict 'refs';
  241       undef @FIELDS unless scalar @FIELDS == scalar @array and not $@;
  242       foreach (0 .. $#array) {
  243         $result .= '<tr valign="top">';
  244         $result .= "<td>$_</td>";
  245         $result .= "<td>".$FIELDS[$_]."</td>" if @FIELDS;
  246         $result .= "<td>" . ref2string($array[$_], $dontExpand) . "</td>";
  247         $result .= "</tr>";
  248       }
  249     } elsif ($baseType eq "SCALAR") {
  250       my $scalar = $$ref;
  251       $result .= '<tr valign="top">';
  252       $result .= "<td>$scalar</td>";
  253       $result .= "</tr>";
  254     } else {
  255       # perhaps a coderef? in any case, i don't feel like dealing with it!
  256       $result .= '<tr valign="top">';
  257       $result .= "<td>$ref</td>";
  258       $result .= "</tr>";
  259     }
  260     $result .= "</table>"
  261   } else {
  262     $result .= defined $ref ? $ref : '<font color="red">undef</font>';
  263   }
  264 }
  265 
  266 sub refBaseType($) {
  267   my $ref = shift;
  268   $ref =~ m/(\w+)\(/; # this might not be robust...
  269   return $1;
  270 }
  271 
  272 # p. 101, Camel, 3rd ed.
  273 # The <=> and cmp operators return -1 if the left operand is less than the
  274 # right operand, 0 if they are equal, and +1 if the left operand is greater
  275 # than the right operand.
  276 
  277 sub sortByName {
  278   my ($field, @items) = @_;
  279   return sort {
  280     my @aParts = split m/(?<=\D)(?=\d)|(?<=\d)(?=\D)/, $a->$field;
  281     my @bParts = split m/(?<=\D)(?=\d)|(?<=\d)(?=\D)/, $b->$field;
  282     while (@aParts and @bParts) {
  283       my $aPart = shift @aParts;
  284       my $bPart = shift @bParts;
  285       my $aNumeric = $aPart =~ m/^\d*$/;
  286       my $bNumeric = $bPart =~ m/^\d*$/;
  287 
  288       # numbers should come before words
  289       return -1 if     $aNumeric and not $bNumeric;
  290       return +1 if not $aNumeric and     $bNumeric;
  291 
  292       # both have the same type
  293       if ($aNumeric and $bNumeric) {
  294         next if $aPart == $bPart; # check next pair
  295         return $aPart <=> $bPart; # compare numerically
  296       } else {
  297         next if $aPart eq $bPart; # check next pair
  298         return $aPart cmp $bPart; # compare lexicographically
  299       }
  300     }
  301     return +1 if @aParts; # a has more sections, should go second
  302     return -1 if @bParts; # a had fewer sections, should go first
  303   } @items;
  304 }
  305 
  306 sub makeTempDirectory($$) {
  307   my ($parent, $basename) = @_;
  308   # Loop until we're able to create a directory, or it fails for some
  309   # reason other than there already being something there.
  310   my $triesRemaining = MKDIR_ATTEMPTS;
  311   my ($fullPath, $success);
  312   do {
  313     my $suffix = join "", map { ('A'..'Z','a'..'z','0'..'9')[int rand 62] } 1 .. 8;
  314     $fullPath = "$parent/$basename.$suffix";
  315     $success = mkdir $fullPath;
  316   } until ($success or not $!{EEXIST});
  317   die "Failed to create directory $fullPath: $!"
  318     unless $success;
  319   return $fullPath;
  320 }
  321 
  322 sub removeTempDirectory($) {
  323   my ($dir) = @_;
  324   rmtree($dir, 0, 0);
  325 }
  326 
  327 sub pretty_print_rh {
  328   my $rh = shift;
  329   foreach my $key (sort keys %{$rh})  {
  330     warn "  $key => ",$rh->{$key},"\n";
  331   }
  332 }
  333 
  334 sub cryptPassword {
  335   my ($clearPassword) = @_;
  336   my $salt = join("", ('.','/','0'..'9','A'..'Z','a'..'z')[rand 64, rand 64]);
  337   my $cryptPassword = crypt($clearPassword, $salt);
  338   return $cryptPassword;
  339 }
  340 
  341 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9