[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 1397 - (download) (as text) (annotate)
Wed Jul 16 12:58:29 2003 UTC (9 years, 10 months ago) by gage
File size: 8952 byte(s)
Abstracted "read directory" functions in SendMail and moved them
to Instructor.  The instructor calls WeBWorK::Utils::readDirectory
and then filters the output according to a pattern match. Sorts as well.

Also move read_scoring_file to instructor since it will need to be used
in other scripts as well.
--Mike

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9