[system] / branches / rel-2-1-a1 / webwork2 / lib / WeBWorK / Utils.pm Repository:
ViewVC logotype

Annotation of /branches/rel-2-1-a1/webwork2/lib/WeBWorK/Utils.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1529 - (view) (download) (as text)
Original Path: trunk/webwork2/lib/WeBWorK/Utils.pm

1 : sh002i 440 ################################################################################
2 : sh002i 494 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
3 : sh002i 440 # $Id$
4 :     ################################################################################
5 :    
6 : sh002i 410 package WeBWorK::Utils;
7 : sh002i 818 use base qw(Exporter);
8 : sh002i 410
9 : sh002i 455 =head1 NAME
10 :    
11 :     WeBWorK::Utils - useful utilities used by other WeBWorK modules.
12 :    
13 :     =cut
14 :    
15 : sh002i 412 use strict;
16 :     use warnings;
17 : sh002i 1257 #use Apache::DB;
18 : sh002i 412 use Date::Format;
19 :     use Date::Parse;
20 : sh002i 1145 use Errno;
21 : sh002i 1150 use File::Path qw(rmtree);
22 : sh002i 412
23 : sh002i 1145 use constant MKDIR_ATTEMPTS => 10;
24 :    
25 : sh002i 410 our @EXPORT = ();
26 : sh002i 424 our @EXPORT_OK = qw(
27 :     runtime_use
28 :     readFile
29 : sh002i 1150 readDirectory
30 : sh002i 424 formatDateTime
31 :     parseDateTime
32 : sh002i 562 writeLog
33 : gage 1387 writeCourseLog
34 : sh002i 562 writeTimingLogEntry
35 : malsyned 970 list2hash
36 :     max
37 : sh002i 427 dbDecode
38 :     dbEncode
39 : sh002i 429 decodeAnswers
40 :     encodeAnswers
41 : sh002i 424 ref2string
42 : sh002i 1111 sortByName
43 : sh002i 1145 makeTempDirectory
44 : sh002i 1150 removeTempDirectory
45 : sh002i 1145 pretty_print_rh
46 : malsyned 1287 cryptPassword
47 : sh002i 424 );
48 : sh002i 410
49 :     sub runtime_use($) {
50 :     return unless @_;
51 : sh002i 424 eval "package Main; require $_[0]; import $_[0]";
52 : sh002i 410 die $@ if $@;
53 :     }
54 :    
55 : sh002i 1257 #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 : malsyned 1045
71 : sh002i 410 sub readFile($) {
72 :     my $fileName = shift;
73 : sh002i 1150 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 : sh002i 410 return $result;
79 :     }
80 : sh002i 412
81 : malsyned 974 sub readDirectory($) {
82 : sh002i 1150 my $dirName = shift;
83 :     opendir my $dh, $dirName
84 : sh002i 1529 or die "Failed to read directory $dirName: $!";
85 : sh002i 1150 my @result = readdir $dh;
86 :     close $dh;
87 :     return @result;
88 : malsyned 974 }
89 :    
90 : sh002i 412 sub formatDateTime($) {
91 :     my $dateTime = shift;
92 : sh002i 558 # "standard" WeBWorK date/time format (for set definition files):
93 : sh002i 412 # %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 : gage 1492 #return time2str("%m/%d/%y %I:%M%P", $dateTime);
100 : gage 1481 return time2str("%m/%d/%y at %I:%M%P", $dateTime);
101 : sh002i 412 }
102 :    
103 :     sub parseDateTime($) {
104 : sh002i 424 my $string = shift;
105 : gage 1492 # 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 : sh002i 737 return str2time($string);
108 : sh002i 412 }
109 : sh002i 422
110 : sh002i 562 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 : sh002i 558
126 : gage 1387 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 : sh002i 631 # $ce - a WeBWork::CourseEnvironment object
144 :     # $function - fully qualified function name
145 :     # $details - any information, do not use the characters '[' or ']'
146 : sh002i 692 # $beginEnd - the string "begin", "intermediate", or "end"
147 :     # use the intermediate step begun or completed for INTERMEDIATE
148 : sh002i 631 # use an empty string for $details when calling for END
149 : sh002i 562 sub writeTimingLogEntry($$$$) {
150 :     my ($ce, $function, $details, $beginEnd) = @_;
151 :     return unless defined $ce->{webworkFiles}->{logs}->{timing};
152 : sh002i 692 $beginEnd = ($beginEnd eq "begin") ? ">" : ($beginEnd eq "end") ? "<" : "-";
153 : sh002i 562 writeLog($ce, "timing", "$$ ".time." $beginEnd $function [$details]");
154 :     }
155 :    
156 : malsyned 970 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 : malsyned 979 return defined $soFar ? $soFar : 0;
169 : malsyned 970 }
170 :    
171 : sh002i 429 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 : sh002i 445 push @array, "" if @array%2;
177 : sh002i 429 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 : sh002i 1095 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 : sh002i 429 $string .= "$name##$value##"; # this is also not my fault
198 :     }
199 :     $string =~ s/##$//; # remove last pair of hashs
200 :     return $string;
201 :     }
202 :    
203 : sh002i 424 sub ref2string($;$);
204 :     sub ref2string($;$) {
205 :     my $ref = shift;
206 :     my $dontExpand = shift || {};
207 :     my $refType = ref $ref;
208 : sh002i 422 my $result;
209 : sh002i 424 if ($refType and not $dontExpand->{$refType}) {
210 :     my $baseType = refBaseType($ref);
211 :     $result .= '<font size="1" color="grey">' . $refType;
212 : sh002i 425 $result .= " ($baseType)" if $baseType and $refType ne $baseType;
213 : sh002i 424 $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 : sh002i 429 # 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 : sh002i 424 foreach (0 .. $#array) {
232 :     $result .= '<tr valign="top">';
233 :     $result .= "<td>$_</td>";
234 : sh002i 429 $result .= "<td>".$FIELDS[$_]."</td>" if @FIELDS;
235 : sh002i 424 $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 : sh002i 422 } else {
244 : sh002i 424 # 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 : sh002i 422 }
249 : sh002i 424 $result .= "</table>"
250 : sh002i 422 } else {
251 : sh002i 424 $result .= defined $ref ? $ref : '<font color="red">undef</font>';
252 :     }
253 : sh002i 422 }
254 :    
255 : sh002i 424 sub refBaseType($) {
256 :     my $ref = shift;
257 : sh002i 984 $ref =~ m/(\w+)\(/; # this might not be robust...
258 :     return $1;
259 : sh002i 422 }
260 :    
261 : sh002i 1111 # 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 : sh002i 1145 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 : sh002i 1150 sub removeTempDirectory($) {
312 :     my ($dir) = @_;
313 :     rmtree($dir, 0, 0);
314 :     }
315 :    
316 : gage 1137 sub pretty_print_rh {
317 :     my $rh = shift;
318 :     foreach my $key (sort keys %{$rh}) {
319 :     warn " $key => ",$rh->{$key},"\n";
320 :     }
321 :     }
322 : sh002i 1145
323 : malsyned 1287 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 : sh002i 422 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9