[system] / trunk / webwork2 / lib / WeBWorK / Utils.pm Repository:
ViewVC logotype

View of /trunk/webwork2/lib/WeBWorK/Utils.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1529 - (download) (as text) (annotate)
Thu Sep 25 02:02:19 2003 UTC (9 years, 8 months ago) by sh002i
File size: 9017 byte(s)
removed overly chatty error message: instead, we should add this sort of
thing to the generic "software error" message.

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9