[system] / trunk / xmlrpc / daemon / Webwork.pm Repository:
ViewVC logotype

Annotation of /trunk/xmlrpc/daemon/Webwork.pm

Parent Directory Parent Directory | Revision Log Revision Log


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

1 : gage 279 #!/usr/local/bin/perl -w
2 : gage 497 use Safe;
3 : gage 279 # Copyright (C) 2001 Michael Gage
4 :    
5 :     ###############################################################################
6 :     # The initial code simply initializes variables, defines addresses
7 :     # for directories, defines some simple subroutines responders used in debugging
8 :     # and makes sure that the appropriate CPAN library modules
9 :     # are available. The main code begins below that with the initialization
10 :     # of the PGtranslator5 module.
11 :     ###############################################################################
12 : gage 497
13 : gage 279 package Webwork;
14 :    
15 :     use strict;
16 :     use sigtrap;
17 :     use Carp;
18 : gage 497 use WeBWorK::PG::Translator;
19 : gage 279 #BEGIN {
20 :     # local $^W=0;
21 :     # require '/usr/local/lib/perl5/5.6.1/Benchmark.pm';
22 :     #}
23 : gage 328 print STDERR "using the perl version of MIME::Base64\n";
24 : gage 279 use MIME::Base64 qw( encode_base64 decode_base64);
25 :    
26 :    
27 :    
28 : gage 386
29 : gage 279
30 : gage 375
31 : gage 279 ###############################################################################
32 : gage 497 # List and address of available problemlibraries
33 : gage 279 ###############################################################################
34 :    
35 :    
36 : gage 497 my $libraryPath = '/u/gage/webwork/ww_prob_lib/';
37 : gage 279
38 : gage 497 my %AVAILABLE_PROBLEM_LIBRARIES = ( ww_prob_lib => '/u/gage/webwork/rochester_problib/',
39 :     indiana_prob_lib => '/u/gage/webwork/Indiana_prob_lib/',
40 : gage 279 capaOK_lib => '/ww/webwork/courses1/capaOK/templates/',
41 :     capa_lib => '/ww/webwork/courses/capa/templates/',
42 :     prob_lib_cvs => '/ww/webwork/courses/WW_Prob_Lib_CVS/templates/',
43 :     maa_100 => '/ww/webwork/courses/maa100/templates/',
44 :     teitel_physics121 => '/ww/webwork/courses/teitel-phy121/templates/',
45 :     );
46 :    
47 :     ###############################################################################
48 :     # Configure daemon:
49 :     ###############################################################################
50 : gage 497 my $courseScriptsDirectory = '/u/gage/webwork/system/courseScripts/';
51 :     my $macroDirectory = '/ww/webwork/courses/gage_course/templates/macros/';
52 :     my $scriptDirectory = '/u/gage/webwork/system/scripts/';
53 :     my $templateDirectory = '/ww/webwork/courses/gage_course/templates/';
54 : gage 279
55 : gage 497 $Global::courseTempDirectory = '/ww/htdocs/tmp/gage_course/';
56 :     $Global::courseTempURL = 'http://webwork-db.math.rochester.edu/tmp/gage_course/';
57 : gage 279
58 :    
59 : gage 497 $Global::groupID = "webwork";
60 : gage 279 $Global::numericalGroupID = 1005;
61 :    
62 : gage 320 my $displayMode = 'HTML_tth';
63 : gage 279
64 :     my $PG_PL = "${courseScriptsDirectory}PG.pl";
65 :     my $DANGEROUS_MACROS_PL = "${courseScriptsDirectory}dangerousMacros.pl";
66 : gage 497 my @MODULE_LIST = ( "Exporter", "DynaLoader", "GD", "WWPlot", "Fun",
67 : gage 279 "Circle", "Label", "PGrandom", "Units", "Hermite",
68 :     "List", "Match","Multiple", "Select", "AlgParser",
69 :     "AnswerHash", "Fraction", "VectorField", "Complex1",
70 :     "Complex", "MatrixReal1", "Matrix","Distributions",
71 :     "Regression"
72 : gage 497 );
73 : gage 386 my @EXTRA_PACKAGES = ( "AlgParserWithImplicitExpand", "Expr",
74 : gage 279 "ExprWithImplicitExpand", "AnswerEvaluator",
75 : gage 497 # "AnswerEvaluatorMaker"
76 :     );
77 :     my $INITIAL_MACRO_PACKAGES = <<END_OF_TEXT;
78 :     DOCUMENT();
79 :     loadMacros(
80 :     "PGbasicmacros.pl",
81 :     "PGchoicemacros.pl",
82 :     "PGanswermacros.pl",
83 :     "PGnumericalmacros.pl",
84 :     "PGgraphmacros.pl",
85 :     "PGauxiliaryFunctions.pl",
86 :     "PGmatrixmacros.pl",
87 :     "PGstatisticsmacros.pl",
88 :     "PGcomplexmacros.pl",
89 : gage 328 );
90 : gage 497
91 :     ENDDOCUMENT();
92 : gage 386
93 : gage 279 END_OF_TEXT
94 :    
95 :     ###############################################################################
96 :     #
97 :     ###############################################################################
98 :    
99 :     ###############################################################################
100 :     ###############################################################################
101 :    
102 : gage 497 #print STDERR "ok so far reading file /u/gage/xmlrpc/daemon/Webwork.pm\n";
103 : gage 279
104 :    
105 :    
106 :     ###############################################################################
107 :     # The following code initializes an instantiation of PGtranslator5 in the
108 :     # parent process. This initialized object is then share with each of the
109 :     # children forked from this parent process by the daemon.
110 :     #
111 :     # As far as I can tell, the child processes don't share any variable values even
112 :     # though their namespaces are the same.
113 :     ###############################################################################
114 :    
115 : gage 497
116 : gage 279 my $dummy_envir = { courseScriptsDirectory => $courseScriptsDirectory,
117 :     displayMode => $displayMode,
118 : gage 497 macroDirectory => $macroDirectory};
119 :     my $pt = new WeBWorK::PG::Translator; #pt stands for problem translator;
120 : gage 279 $pt ->rh_directories( { courseScriptsDirectory => $courseScriptsDirectory,
121 :     macroDirectory => $macroDirectory,
122 :     scriptDirectory => $scriptDirectory ,
123 :     templateDirectory => $templateDirectory,
124 :     tempDirectory => $Global::courseTempDirectory,
125 :     }
126 :     );
127 :     $pt -> evaluate_modules( @MODULE_LIST);
128 : gage 497 #print STDERR "Completed loading of modules, now loading extra packages\n";
129 : gage 279 $pt -> load_extra_packages( @EXTRA_PACKAGES );
130 : gage 497 #print STDERR "Completed loading of packages, now loading environment\n";
131 : gage 279 $pt -> environment($dummy_envir);
132 : gage 497 #print STDERR "Completed loading environment, next initialize\n";
133 : gage 279 $pt->initialize();
134 : gage 497 #print STDERR "Initialized. \n";
135 :     $pt -> unrestricted_load($PG_PL );
136 :     $pt -> unrestricted_load($DANGEROUS_MACROS_PL);
137 : gage 279 $pt-> set_mask();
138 :     #
139 : gage 497 #print STDERR "Unrestricted loads completed.\n";
140 : gage 279
141 : gage 497 $INITIAL_MACRO_PACKAGES =~ tr /\r/\n/;
142 : gage 279 $pt->source_string( $INITIAL_MACRO_PACKAGES );
143 : gage 497 #print STDERR "source strings read in\n";
144 : gage 279 $pt ->rf_safety_filter( \&safetyFilter); # install blank safety filter
145 :     $pt ->translate();
146 : gage 320
147 :     print STDERR "New PGtranslator object inititialization completed.\n";
148 : gage 279 ################################################################################
149 :     ## This ends the initialization of the PGtranslator object
150 :     ################################################################################
151 :    
152 :     ###############################################################################
153 :     # This subroutine is called by the child process. It reinitializes its copy of the
154 :     # PGtranslator5 object. The unrestricted_load and loadMacros subroutines of PGtranslator5
155 :     # have been modified so that if &_PG_init is already defined then nothing
156 :     # is read in but the initialization subroutine is run instead.
157 :     ###############################################################################
158 :    
159 :     sub renderProblem {
160 :     my $rh = shift;
161 :     my $beginTime = new Benchmark;
162 :     $Global::WARNINGS = "";
163 :     $pt->environment($rh->{envir});
164 : gage 497 #$pt->{safe_cache} = $safe_cmpt_cache;
165 : gage 279 $pt->initialize();
166 :     $pt -> unrestricted_load($PG_PL);
167 :     $pt -> unrestricted_load($DANGEROUS_MACROS_PL);
168 :     $pt-> set_mask();
169 :    
170 :     my $string = decode_base64( $rh ->{source} );
171 :     $string =~ tr /\r/\n/;
172 :    
173 :     $pt->source_string( $string );
174 :     $pt ->rf_safety_filter( \&safetyFilter); # install blank safety filter
175 :     $pt ->translate();
176 :    
177 :    
178 :     # Determine which problem grader to use
179 :     #$pt->rf_problem_grader($pt->rf_std_problem_grader); #this is the default
180 :     my $problem_grader_to_use = $pt->rh_flags->{PROBLEM_GRADER_TO_USE};
181 :    
182 :     if ( defined($problem_grader_to_use) and $problem_grader_to_use ) { # if defined and non-empty
183 :     if ($problem_grader_to_use eq 'std_problem_grader') {
184 :     # Reset problem grader to standard problem grader.
185 :     $pt->rf_problem_grader($pt->rf_std_problem_grader);
186 :     } elsif ($problem_grader_to_use eq 'avg_problem_grader') {
187 :     # Reset problem grader to average problem grader.
188 :     $pt->rf_problem_grader($pt->rf_avg_problem_grader);
189 :     } elsif (ref($problem_grader_to_use) eq 'CODE') {
190 :     # Set problem grader to instructor defined problem grader -- use cautiously.
191 :     $pt->rf_problem_grader($problem_grader_to_use)
192 :     } else {
193 :     warn "Error: Could not understand problem grader flag $problem_grader_to_use";
194 :     #this is the default set by the translator and used if the flag is not understood
195 :     #$pt->rf_problem_grader($pt->rf_std_problem_grader);
196 :     }
197 :    
198 :     } else {#this is the default set by the translator and used if no flag is set.
199 :     $pt->rf_problem_grader($pt->rf_std_problem_grader);
200 :     }
201 :    
202 :     # creates and stores a hash of answer results: $rh_answer_results
203 :     $pt -> process_answers($rh->{envir}->{inputs_ref});
204 :    
205 :    
206 :     $pt->rh_problem_state({ recorded_score => $rh->{problem_state}->{recorded_score},
207 :     num_of_correct_ans => $rh->{problem_state}->{num_of_correct_ans} ,
208 :     num_of_incorrect_ans => $rh->{problem_state}->{num_of_incorrect_ans}
209 :     } );
210 :     my %PG_FLAGS = $pt->h_flags;
211 :     my $ra_answer_entry_order = ( defined($PG_FLAGS{ANSWER_ENTRY_ORDER}) ) ?
212 :     $PG_FLAGS{ANSWER_ENTRY_ORDER} : [ keys %{$pt->rh_evaluated_answers} ] ;
213 :     my $answers_submitted = 0;
214 :     $answers_submitted = 1 if defined( $rh->{answer_form_submitted} ) and 1 == $rh->{answer_form_submitted};
215 :    
216 :     my ($rh_problem_result,$rh_problem_state) = $pt->grade_problem( answers_submitted => $answers_submitted,
217 :     ANSWER_ENTRY_ORDER => $ra_answer_entry_order
218 :     ); # grades the problem.
219 :     # protect image data for delivery via XML-RPC.
220 :     # Don't send code data.
221 :     my %PG_flag=();
222 :     # foreach my $key (keys %PG_FLAGS) {
223 :     # if ($key eq 'dynamic_images' ) {
224 :     # foreach my $ikey (keys %{$PG_FLAGS{'dynamic_images'} }) {
225 :     # $PG_flag{'dynamic_images'}->{$ikey} =
226 :     # encode_base64($PG_FLAGS{'dynamic_images'}->{$ikey});
227 :     # }
228 :     # } elsif (ref($PG_FLAGS{$key}) eq '' or ref($PG_FLAGS{$key}) =~ /SCALAR|HASH/) {
229 :     # $PG_flag{$key} = $PG_FLAGS{$key} ;
230 :     # }
231 :     # }
232 :    
233 :    
234 :     my $out = {
235 :     text => encode_base64( ${$pt ->r_text()} ),
236 :     header_text => encode_base64( ${ $pt->r_header } ),
237 :     answers => $pt->rh_evaluated_answers,
238 :     errors => $pt-> errors(),
239 :     WARNINGS => encode_base64($Global::WARNINGS ),
240 :     problem_result => $rh_problem_result,
241 :     problem_state => $rh_problem_state,
242 :     PG_flag => \%PG_flag
243 :     };
244 :     my $endTime = new Benchmark;
245 :     $out->{compute_time} = logTimingInfo($beginTime, $endTime);
246 :     $out;
247 :    
248 :     }
249 :    
250 :     ###############################################################################
251 :     # This ends the main subroutine executed by the child process in responding to
252 :     # a request. The other subroutines are auxiliary.
253 :     ###############################################################################
254 :    
255 :    
256 :     sub safetyFilter {
257 :     my $answer = shift; # accepts one answer and checks it
258 :     my $submittedAnswer = $answer;
259 :     $answer = '' unless defined $answer;
260 :     my ($errorno, $answerIsCorrectQ);
261 :     $answer =~ tr/\000-\037/ /;
262 :     #### Return if answer field is empty ########
263 :     unless ($answer =~ /\S/) {
264 :     # $errorno = "<BR>No answer was submitted.";
265 :     $errorno = 0; ## don't report blank answer as error
266 :    
267 :     return ($answer,$errorno);
268 :     }
269 :     ######### replace ^ with ** (for exponentiation)
270 :     # $answer =~ s/\^/**/g;
271 :     ######### Return if forbidden characters are found
272 :     unless ($answer =~ /^[a-zA-Z0-9_\-\+ \t\/@%\*\.\n^\(\)]+$/ ) {
273 :     $answer =~ tr/a-zA-Z0-9_\-\+ \t\/@%\*\.\n^\(\)/#/c;
274 :     $errorno = "<BR>There are forbidden characters in your answer: $submittedAnswer<BR>";
275 :    
276 :     return ($answer,$errorno);
277 :     }
278 :    
279 :     $errorno = 0;
280 :     return($answer, $errorno);
281 :     }
282 :    
283 :    
284 :     sub logTimingInfo{
285 :     my ($beginTime,$endTime,) = @_;
286 :     my $out = "";
287 :     $out .= Benchmark::timestr( Benchmark::timediff($endTime , $beginTime) );
288 :     $out;
289 :     }
290 :    
291 :     ###############
292 :    
293 :     sub echo {
294 :     my $in= shift;
295 :     return(ref($in));
296 :     }
297 :     sub hello {
298 :     print "Receiving request for hello world\n";
299 :     return "Hello world";
300 :     }
301 :     sub pretty_print_rh {
302 :     my $rh = shift;
303 :     my $out = "";
304 :     my $type = ref($rh);
305 :     if ( ref($rh) =~/HASH/ ) {
306 :     foreach my $key (sort keys %{$rh}) {
307 :     $out .= " $key => " . pretty_print_rh( $rh->{$key} ) . "\n";
308 :     }
309 :     } elsif ( ref($rh) =~ /SCALAR/ ) {
310 :     $out = "scalar reference ". ${$rh};
311 :     } elsif ( ref($rh) =~/Base64/ ) {
312 :     $out .= "base64 reference " .$$rh;
313 :     } else {
314 :     $out = $rh;
315 :     }
316 :     if (defined($type) ) {
317 : gage 497 $out .= "type = $type \n";
318 : gage 279 }
319 :     return $out;
320 :     }
321 :    
322 :     #sub xmlquit {
323 :     # print "exiting daemon\n";
324 :     # return "";
325 :     #}
326 :    
327 :     ###############################################################################
328 :     #OTHER SERVICES
329 :     ###############################################################################
330 :    
331 : gage 497 my $PASSWORD = 'geometry';
332 : gage 279
333 :     use File::stat;
334 :     sub readFile {
335 :     my $rh = shift;
336 :     local($|)=1;
337 :     my $out = {};
338 :     my $filePath = $rh->{filePath};
339 :     unless ($rh->{pw} eq $PASSWORD ) {
340 :     $out->{error}=404;
341 :     return($out);
342 :     }
343 :     if ( defined($AVAILABLE_PROBLEM_LIBRARIES{$rh->{library_name}} ) ) {
344 :     $filePath = $AVAILABLE_PROBLEM_LIBRARIES{$rh->{library_name}} . $filePath;
345 :     } else {
346 :     $out->{error} = "Could not find library:".$rh->{library_name}.":";
347 :     return($out);
348 :     }
349 :    
350 :     if (-r $filePath) {
351 :     open IN, "<$filePath";
352 :     local($/)=undef;
353 :     my $text = <IN>;
354 :     $out->{text}= encode_base64($text);
355 :     my $sb=stat($filePath);
356 :     $out->{size}=$sb->size;
357 :     $out->{path}=$filePath;
358 :     $out->{permissions}=$sb->mode&07777;
359 :     $out->{modTime}=scalar localtime $sb->mtime;
360 :     close(IN);
361 :     } else {
362 :     $out->{error} = "Could not read file at |$filePath|";
363 :     }
364 :     return($out);
365 :     }
366 :    
367 :    
368 :    
369 :     use File::Find;
370 :     sub listLib {
371 :     my $rh = shift;
372 :     my $out = {};
373 :     my $dirPath;
374 :     unless ($rh->{pw} eq $PASSWORD ) {
375 :     $out->{error}=404;
376 :     return($out);
377 :     }
378 :    
379 :     if ( defined($AVAILABLE_PROBLEM_LIBRARIES{$rh->{library_name}} ) ) {
380 :     $dirPath = $AVAILABLE_PROBLEM_LIBRARIES{$rh->{library_name}} ;
381 :     } else {
382 :     $out->{error} = "Could not find library:".$rh->{library_name}.":";
383 :     return($out);
384 :     }
385 :    
386 :     my @outListLib;
387 :     my $wanted = sub {
388 :     my $name = $File::Find::name;
389 :     my @out=();
390 :     if ($name =~/\S/ ) {
391 :     $name =~ s|^$dirPath||o; # cut the first directory
392 :     push(@outListLib, "$name\n") if $name =~/\.pg/;
393 :     }
394 :     };
395 :     my $command = $rh->{command};
396 :     $command = 'all' unless defined($command);
397 :     $command eq 'all' && do {print "$dirPath\n\n";
398 :     find($wanted, $dirPath);
399 :     @outListLib = sort @outListLib;
400 :     $out->{ra_out} = \@outListLib;
401 :     $out->{text} = join("", sort @outListLib);
402 :     return($out);
403 :     };
404 :     $command eq 'setsOnly' && do {
405 :     if ( opendir(DIR, $dirPath) ) {
406 :     my @fileList=();
407 :     while (defined(my $file = readdir(DIR))) {
408 :     push(@fileList,$file) if -d "$dirPath/$file";
409 :    
410 :     }
411 :     $out->{text} = join("\n",sort @fileList);
412 :     closedir(DIR);
413 :     } else {
414 :     $out->{error}= "Can't open directory $dirPath";
415 :     }
416 :     return($out);
417 :     };
418 :     $command eq 'listSet' && do { my $dirPath2 = $dirPath . $rh->{set};
419 :    
420 :     if ( opendir(DIR, $dirPath2) ) {
421 :     my @fileList =();
422 :     while (defined(my $file = readdir(DIR))) {
423 :     if (-d "$dirPath2/$file") {
424 :     push(@fileList, "$file/${file}.pg");
425 :    
426 :     } elsif ($file =~ /.pg$/ ) { # file ends in .pg
427 :     push(@fileList, $file);
428 :    
429 :     }
430 :    
431 :    
432 :     }
433 :     $out->{text} = join("\n",sort @fileList);
434 :     closedir(DIR);
435 :     } else {
436 :     $out->{error}= "Can't open directory $dirPath2";
437 :     }
438 :    
439 :     return($out);
440 :     };
441 :     # else
442 :     $out->{error}="Unrecognized command $command";
443 :     $out;
444 :     }
445 :    
446 : gage 497 my $TEMPDIRECTORY = '/ww/htdocs/tmp/daemon/';
447 :     my $TEMP_BASE_URL = 'http://webwork-db.math.rochester.edu/tmp/daemon/';
448 :     my $externalLatexPath = "/usr/local/bin/latex";
449 :     my $externalDvipsPath = "/usr/local/bin/dvips";
450 :     my $externalGsPath = "/usr/local/bin/gs";
451 :    
452 :    
453 :     sub tex2pdf {
454 :    
455 :     my $rh = shift;
456 :     local($|)=1;
457 :     my $out = {};
458 :     my $filePath = $rh->{filePath};
459 :     unless ($rh->{pw} eq $PASSWORD ) {
460 :     $out->{error}=404;
461 :     return($out);
462 :     }
463 :     my $fileName = $rh->{fileName};
464 :     my $texString = decode_base64( $rh->{'texString'});
465 :     $fileName =~ s/\.pg$//;
466 :     my $url = "${TEMP_BASE_URL}$fileName.pdf";
467 :     my $texFileBaseName = $TEMPDIRECTORY.$fileName;
468 :    
469 :     surePathToTmpFile($texFileBaseName);
470 :     local(*TEX);
471 :     open(TEX,"> $texFileBaseName.tex") or die "Can't open $texFileBaseName.tex to store tex code";
472 :     local($/)=undef;
473 :     print TEX $texString;
474 :     close(TEX);
475 :     my $dviCommandLine = "$externalLatexPath $texFileBaseName.tex >/dev/null 2>/dev/null";
476 :     my $psCommandLine = "$externalDvipsPath -o $texFileBaseName.ps $texFileBaseName.dvi >/dev/null 2>/dev/null";
477 :     my $pdfCommandLine = "$externalGsPath -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=$texFileBaseName.pdf -c save pop -f $texFileBaseName.ps";
478 :     print "$dviCommandLine\n";
479 :     print "$psCommandLine\n";
480 :     print "$pdfCommandLine\n";
481 :    
482 :     system($dviCommandLine); -e "$texFileBaseName.dvi" or die "tex generation failed";
483 :     # system($psCommandLine); -e "$texFileBaseName.ps" or die "ps generation failed.";
484 :     # system($pdfCommandLine); -e "$texFileBaseName.pdf" or die "pdf generation failed.";
485 :     return({pdfURL => $url});
486 :    
487 :    
488 :    
489 :    
490 :     }
491 :    
492 :     sub surePathToTmpFile { # constructs intermediate directories if needed beginning at ${Global::htmlDirectory}tmp/
493 :     # the input path must be either the full path, or the path relative to this tmp sub directory
494 :     my $path = shift;
495 :     my $delim = getDirDelim();
496 :     my $tmpDirectory = $TEMPDIRECTORY;
497 :     # if the path starts with $tmpDirectory (which is permitted but optional) remove this initial segment
498 :     print "Original path $path\n";
499 :     $path =~ s|^$tmpDirectory|| if $path =~ m|^$tmpDirectory|;
500 :     $path = convertPath($path);
501 :     print "Creating path to $path using $delim\n";
502 :     # find the nodes on the given path
503 :     my @nodes = split("$delim",$path);
504 :     # create new path
505 :     $path = convertPath("$tmpDirectory");
506 :    
507 :     while (@nodes>1 ) {
508 :     print "Creating path: $path\n ";
509 :     $path = convertPath($path . shift (@nodes) ."/");
510 :     unless (-e $path) {
511 :     # system("mkdir $path");
512 :     createDirectory($path,$Global::tmp_directory_permission, $Global::numericalGroupID) ||
513 :     die "Failed to create directory $path";
514 :    
515 :     }
516 :    
517 :     }
518 :     $path = convertPath($path . shift(@nodes));
519 :    
520 :     # system(qq!echo "" > $path! );
521 :    
522 :     $path;
523 :    
524 :     }
525 :    
526 :    
527 :    
528 :    
529 :    
530 :    
531 :    
532 :    
533 :    
534 :    
535 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9