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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1900 - (view) (download) (as text)

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9