[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 1387 - (view) (download) (as text)

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9