Annotation of loncom/interface/lonnavmaps.pm, revision 1.18
1.2 www 1: # The LearningOnline Network with CAPA
2: # Navigate Maps Handler
1.1 www 3: #
1.2 www 4: # (Page Handler
1.1 www 5: #
1.2 www 6: # (TeX Content Handler
1.1 www 7: #
1.2 www 8: # 05/29/00,05/30 Gerd Kortemeyer)
9: # 08/30,08/31,09/06,09/14,09/15,09/16,09/19,09/20,09/21,09/23,
10: # 10/02,10/10,10/14,10/16,10/18,10/19,10/31,11/6,11/14,11/16 Gerd Kortemeyer)
1.1 www 11: #
1.17 www 12: # 3/1/1,6/1,17/1,29/1,30/1,2/8,9/21,9/24,9/25 Gerd Kortemeyer
1.2 www 13:
1.1 www 14: package Apache::lonnavmaps;
15:
16: use strict;
1.2 www 17: use Apache::Constants qw(:common :http);
18: use Apache::lonnet();
1.18 ! albertel 19: use Apache::loncommon();
1.2 www 20: use HTML::TokeParser;
21: use GDBM_File;
22:
23: # -------------------------------------------------------------- Module Globals
24: my %hash;
25: my @rows;
26:
1.10 www 27: #
28: # These cache hashes need to be independent of user, resource and course
29: # (user and course can/should be in the keys)
30: #
31:
32: my %courserdatas;
33: my %userrdatas;
34:
35: #
36: # These global hashes are dependent on user, course and resource,
37: # and need to be initialized every time when a sheet is calculated
38: #
39: my %courseopt;
40: my %useropt;
41: my %parmhash;
42:
43:
1.2 www 44: # ------------------------------------------------------------------ Euclid gcd
45:
46: sub euclid {
47: my ($e,$f)=@_;
48: my $a; my $b; my $r;
49: if ($e>$f) { $b=$e; $r=$f; } else { $r=$e; $b=$f; }
50: while ($r!=0) {
51: $a=$b; $b=$r;
52: $r=$a%$b;
53: }
54: return $b;
55: }
56:
1.10 www 57: # --------------------------------------------------------------------- Parmval
58:
59: # -------------------------------------------- Figure out a cascading parameter
60: #
61: # For this function to work
62: #
63: # * parmhash needs to be tied
64: # * courseopt and useropt need to be initialized for this user and course
65: #
66:
67: sub parmval {
68: my ($what,$symb)=@_;
69: my $cid=$ENV{'request.course.id'};
70: my $csec=$ENV{'request.course.sec'};
71: my $uname=$ENV{'user.name'};
72: my $udom=$ENV{'user.domain'};
73:
74: unless ($symb) { return ''; }
75: my $result='';
76:
77: my ($mapname,$id,$fn)=split(/\_\_\_/,$symb);
78:
79: # ----------------------------------------------------- Cascading lookup scheme
80: my $rwhat=$what;
81: $what=~s/^parameter\_//;
82: $what=~s/\_/\./;
83:
84: my $symbparm=$symb.'.'.$what;
85: my $mapparm=$mapname.'___(all).'.$what;
86: my $usercourseprefix=$uname.'_'.$udom.'_'.$cid;
87:
88: my $seclevel=
89: $usercourseprefix.'.['.
90: $csec.'].'.$what;
91: my $seclevelr=
92: $usercourseprefix.'.['.
93: $csec.'].'.$symbparm;
94: my $seclevelm=
95: $usercourseprefix.'.['.
96: $csec.'].'.$mapparm;
97:
98: my $courselevel=
99: $usercourseprefix.'.'.$what;
100: my $courselevelr=
101: $usercourseprefix.'.'.$symbparm;
102: my $courselevelm=
103: $usercourseprefix.'.'.$mapparm;
104:
105: # ---------------------------------------------------------- fourth, check user
106:
107: if ($uname) {
108:
109: if ($useropt{$courselevelr}) { return $useropt{$courselevelr}; }
110:
111: if ($useropt{$courselevelm}) { return $useropt{$courselevelm}; }
112:
113: if ($useropt{$courselevel}) { return $useropt{$courselevel}; }
114:
115: }
116:
117: # --------------------------------------------------------- third, check course
118:
119: if ($csec) {
120:
121: if ($courseopt{$seclevelr}) { return $courseopt{$seclevelr}; }
122:
123: if ($courseopt{$seclevelm}) { return $courseopt{$seclevelm}; }
124:
125: if ($courseopt{$seclevel}) { return $courseopt{$seclevel}; }
126:
127: }
128:
129: if ($courseopt{$courselevelr}) { return $courseopt{$courselevelr}; }
130:
131: if ($courseopt{$courselevelm}) { return $courseopt{$courselevelm}; }
132:
133: if ($courseopt{$courselevel}) { return $courseopt{$courselevel}; }
134:
135: # ----------------------------------------------------- second, check map parms
136:
137: my $thisparm=$parmhash{$symbparm};
138: if ($thisparm) { return $thisparm; }
139:
140: # -------------------------------------------------------- first, check default
141:
142: return &Apache::lonnet::metadata($fn,$rwhat.'.default');
143:
144: }
145:
146:
147:
1.9 www 148: # ------------------------------------------------------------- Find out status
149:
150: sub astatus {
151: my $rid=shift;
152: my $code=1;
153: my $ctext='';
154: $rid=~/(\d+)\.(\d+)/;
1.10 www 155: my $symb=&Apache::lonnet::declutter($hash{'map_id_'.$1}).'___'.$2.'___'.
156: &Apache::lonnet::declutter($hash{'src_'.$rid});
1.15 www 157:
158: my %duedate=();
159: my %opendate=();
160: my %answerdate=();
161: map {
162: if ($_=~/^parameter\_(.*)\_opendate$/) {
163: my $part=$1;
164: $duedate{$part}=&parmval($part.'.duedate',$symb);
165: $opendate{$part}=&parmval($part.'.opendate',$symb);
166: $answerdate{$part}=&parmval($part.'.answerdate',$symb);
167: }
168: } sort split(/\,/,&Apache::lonnet::metadata($hash{'src_'.$rid},'keys'));
169:
1.11 www 170: my $now=time;
171: my $tcode=0;
1.16 www 172:
173: my %returnhash=&Apache::lonnet::restore($symb);
174:
175: map {
176:
177: my $duedate=$duedate{$_};
178: my $opendate=$opendate{$_};
179: my $answerdate=$answerdate{$_};
180: my $preface='';
181: unless ($_ eq '0') { $preface=' Part: '.$_.' '; }
1.12 www 182: if ($opendate) {
1.11 www 183: if ($now<$duedate) {
1.16 www 184: unless ($tcode==4) { $tcode=2; }
185: $ctext.=$preface.'Due: '.localtime($duedate);
1.11 www 186: if ($now<$opendate) {
1.16 www 187: unless ($tcode) { $tcode=1; }
188: $ctext.=$preface.'Open: '.localtime($opendate);
1.11 www 189: }
190: if ($duedate-$now<86400) {
191: $tcode=4;
1.16 www 192: $ctext.=$preface.'Due: '.localtime($duedate);
1.11 www 193: }
194: } else {
1.16 www 195: unless (($tcode==4) || ($tcode eq 2)) { $tcode=3; }
1.11 www 196: if ($now<$answerdate) {
1.16 www 197: $ctext.='Answer: '.localtime($duedate);
1.11 www 198: }
1.10 www 199: }
1.12 www 200: } else {
1.16 www 201: unless (($tcode==2) || ($tcode==4)) { $tcode=1; }
1.12 www 202: }
1.16 www 203:
204: my $status=$returnhash{'resource.'.$_.'.solved'};
205:
206: if ($status eq 'correct_by_student') {
1.9 www 207: unless ($code==2) { $code=3; }
1.16 www 208: $ctext.=' solved';
209: } elsif ($status eq 'correct_by_override') {
1.9 www 210: unless ($code==2) { $code=3; }
1.16 www 211: $ctext.=' override';
212: } elsif ($status eq 'incorrect_attempted') {
1.9 www 213: $code=2;
1.16 www 214: $ctext.=' ('.
1.17 www 215: ($returnhash{'resource.'.$_.'.tries'}?
216: $returnhash{'resource.'.$_.'.tries'}:'0').'/'.
1.16 www 217: &parmval($_.'.maxtries',$symb).' tries)';
218: } elsif ($status eq 'incorrect_by_override') {
1.9 www 219: $code=2;
1.16 www 220: $ctext.=' override';
221: } elsif ($status eq 'excused') {
1.9 www 222: unless ($code==2) { $code=3; }
1.16 www 223: $ctext.=' excused';
1.9 www 224: }
1.16 www 225:
1.17 www 226: } sort keys %opendate;
1.16 www 227:
1.11 www 228: return 'p'.$code.$tcode.'"'.$ctext.'"';
1.9 www 229: }
230:
1.2 www 231: # ------------------------------------------------------------ Build page table
232:
233: sub tracetable {
234: my ($sofar,$rid,$beenhere)=@_;
235: my $further=$sofar;
236: unless ($beenhere=~/\&$rid\&/) {
237: $beenhere.=$rid.'&';
238:
239: if (defined($hash{'is_map_'.$rid})) {
1.7 www 240: $sofar++;
241: my $tprefix='';
242: if ($hash{'map_type_'.$hash{'map_pc_'.$hash{'src_'.$rid}}}
243: eq 'sequence') {
244: $tprefix='h';
245: }
1.6 www 246: if (defined($rows[$sofar])) {
1.7 www 247: $rows[$sofar].='&'.$tprefix.$rid;
1.6 www 248: } else {
1.7 www 249: $rows[$sofar]=$tprefix.$rid;
1.6 www 250: }
1.2 www 251: if ((defined($hash{'map_start_'.$hash{'src_'.$rid}})) &&
1.7 www 252: (defined($hash{'map_finish_'.$hash{'src_'.$rid}})) &&
253: ($tprefix eq 'h')) {
1.2 www 254: my $frid=$hash{'map_finish_'.$hash{'src_'.$rid}};
255: $sofar=
256: &tracetable($sofar,$hash{'map_start_'.$hash{'src_'.$rid}},
257: '&'.$frid.'&');
258: $sofar++;
259: if ($hash{'src_'.$frid}) {
260: my $brepriv=&Apache::lonnet::allowed('bre',$hash{'src_'.$frid});
261: if (($brepriv eq '2') || ($brepriv eq 'F')) {
1.7 www 262: my $pprefix='';
1.9 www 263: if ($hash{'src_'.$frid}=~
264: /\.(problem|exam|quiz|assess|survey|form)$/) {
265: $pprefix=&astatus($frid);
266:
1.7 www 267: }
1.2 www 268: if (defined($rows[$sofar])) {
1.7 www 269: $rows[$sofar].='&'.$pprefix.$frid;
1.2 www 270: } else {
1.7 www 271: $rows[$sofar]=$pprefix.$frid;
1.2 www 272: }
273: }
274: }
275: }
276: } else {
277: $sofar++;
278: if ($hash{'src_'.$rid}) {
279: my $brepriv=&Apache::lonnet::allowed('bre',$hash{'src_'.$rid});
280: if (($brepriv eq '2') || ($brepriv eq 'F')) {
1.7 www 281: my $pprefix='';
1.9 www 282: if ($hash{'src_'.$rid}=~
283: /\.(problem|exam|quiz|assess|survey|form)$/) {
284: $pprefix=&astatus($rid);
1.7 www 285: }
1.2 www 286: if (defined($rows[$sofar])) {
1.7 www 287: $rows[$sofar].='&'.$pprefix.$rid;
1.2 www 288: } else {
1.7 www 289: $rows[$sofar]=$pprefix.$rid;
1.2 www 290: }
291: }
292: }
293: }
294:
295: if (defined($hash{'to_'.$rid})) {
296: my $mincond=1;
297: my $next='';
298: map {
299: my $thiscond=
300: &Apache::lonnet::directcondval($hash{'condid_'.$hash{'undercond_'.$_}});
301: if ($thiscond>=$mincond) {
302: if ($next) {
303: $next.=','.$_.':'.$thiscond;
304: } else {
305: $next=$_.':'.$thiscond;
306: }
307: if ($thiscond>$mincond) { $mincond=$thiscond; }
308: }
309: } split(/\,/,$hash{'to_'.$rid});
310: map {
311: my ($linkid,$condval)=split(/\:/,$_);
312: if ($condval>=$mincond) {
313: my $now=&tracetable($sofar,$hash{'goesto_'.$linkid},$beenhere);
314: if ($now>$further) { $further=$now; }
315: }
316: } split(/\,/,$next);
317:
318: }
319: }
320: return $further;
321: }
322:
323: # ================================================================ Main Handler
1.1 www 324:
325: sub handler {
1.2 www 326: my $r=shift;
327:
328:
329: # ------------------------------------------- Set document type for header only
330:
331: if ($r->header_only) {
332: if ($ENV{'browser.mathml'}) {
333: $r->content_type('text/xml');
334: } else {
335: $r->content_type('text/html');
336: }
337: $r->send_http_header;
338: return OK;
339: }
340:
341: my $requrl=$r->uri;
342: # ----------------------------------------------------------------- Tie db file
343: if ($ENV{'request.course.fn'}) {
344: my $fn=$ENV{'request.course.fn'};
345: if (-e "$fn.db") {
1.10 www 346: if ((tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER,0640)) &&
347: (tie(%parmhash,'GDBM_File',
348: $ENV{'request.course.fn'}.'_parms.db',&GDBM_READER,0640))) {
1.2 www 349: # ------------------------------------------------------------------- Hash tied
350: my $firstres=$hash{'map_start_/res/'.$ENV{'request.course.uri'}};
351: my $lastres=$hash{'map_finish_/res/'.$ENV{'request.course.uri'}};
352: if (($firstres) && ($lastres)) {
353: # ----------------------------------------------------------------- Render page
1.10 www 354: # -------------------------------------------------------------- Set parameters
355:
356:
357: # ---------------------------- initialize coursedata and userdata for this user
358: undef %courseopt;
359: undef %useropt;
360:
361: my $uname=$ENV{'user.name'};
362: my $udom=$ENV{'user.domain'};
363: my $uhome=$ENV{'user.home'};
364: my $cid=$ENV{'request.course.id'};
365: my $chome=$ENV{'course.'.$cid.'.home'};
366: my ($cdom,$cnum)=split(/\_/,$cid);
367:
368: my $userprefix=$uname.'_'.$udom.'_';
369:
370: unless ($uhome eq 'no_host') {
371: # -------------------------------------------------------------- Get coursedata
372: unless
373: ((time-$courserdatas{$cid.'.last_cache'})<240) {
374: my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.
375: ':resourcedata',$chome);
376: if ($reply!~/^error\:/) {
377: $courserdatas{$cid}=$reply;
378: $courserdatas{$cid.'.last_cache'}=time;
379: }
380: }
381: map {
382: my ($name,$value)=split(/\=/,$_);
383: $courseopt{$userprefix.&Apache::lonnet::unescape($name)}=
384: &Apache::lonnet::unescape($value);
385: } split(/\&/,$courserdatas{$cid});
386: # --------------------------------------------------- Get userdata (if present)
387: unless
388: ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) {
389: my $reply=
390: &Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome);
391: if ($reply!~/^error\:/) {
392: $userrdatas{$uname.'___'.$udom}=$reply;
393: $userrdatas{$uname.'___'.$udom.'.last_cache'}=time;
394: }
395: }
396: map {
397: my ($name,$value)=split(/\=/,$_);
398: $useropt{$userprefix.&Apache::lonnet::unescape($name)}=
399: &Apache::lonnet::unescape($value);
400: } split(/\&/,$userrdatas{$uname.'___'.$udom});
401: }
1.2 www 402:
403: @rows=();
404:
405: &tracetable(0,$firstres,'&'.$lastres.'&');
406: if ($hash{'src_'.$lastres}) {
407: my $brepriv=
408: &Apache::lonnet::allowed('bre',$hash{'src_'.$lastres});
409: if (($brepriv eq '2') || ($brepriv eq 'F')) {
410: $rows[$#rows+1]=''.$lastres;
411: }
412: }
413:
414: # ------------------------------------------------------------------ Page parms
415:
416: my $j;
1.5 www 417: my $i;
1.2 www 418: my $lcm=1;
419: my $contents=0;
420:
421: # ---------------------------------------------- Go through table to get layout
422:
423: for ($i=0;$i<=$#rows;$i++) {
424: if ($rows[$i]) {
425: $contents++;
426: my @colcont=split(/\&/,$rows[$i]);
427: $lcm*=($#colcont+1)/euclid($lcm,($#colcont+1));
428: }
429: }
1.5 www 430:
1.2 www 431:
432: unless ($contents) {
433: $r->content_type('text/html');
434: $r->send_http_header;
435: $r->print('<html><body>Empty Map.</body></html>');
436: } else {
1.10 www 437:
1.2 www 438: # ------------------------------------------------------------------ Build page
439:
1.13 www 440: my $currenturl=$ENV{'form.postdata'};
441: $currenturl=~s/^http\:\/\///;
442: $currenturl=~s/^[^\/]+//;
443:
1.2 www 444: # ---------------------------------------------------------------- Send headers
445:
446: $r->content_type('text/html');
447: $r->send_http_header;
1.18 ! albertel 448: my $date=localtime;
1.2 www 449: $r->print(
1.18 ! albertel 450: '<html>'.&Apache::loncommon::cacheheader().
! 451: '<head><title>Navigate LON-CAPA Maps</title></head>');
1.13 www 452: $r->print('<body bgcolor="#FFFFFF"');
1.14 www 453: if (($currenturl=~/^\/res/) &&
454: ($currenturl!~/^\/res\/adm/)) {
1.13 www 455: $r->print(' onLoad="window.location.hash='.
456: "'curloc'".'"');
457: }
458: $r->print('><script>window.focus();</script>'.
1.6 www 459: '<img align=right src=/adm/lonIcons/lonlogos.gif>'.
1.2 www 460: '<h1>Navigate Course Map</h1>');
1.13 www 461: $r->rflush();
1.14 www 462: if (($currenturl=~/^\/res/) &&
463: ($currenturl!~/^\/res\/adm/)) {
1.13 www 464: $r->print('<a href="#curloc">Current Location</a><p>');
465: }
466: # ----------------------------------------------------- The little content list
467: for ($i=0;$i<=$#rows;$i++) {
468: if ($rows[$i]) {
469: my @colcont=split(/\&/,$rows[$i]);
470: my $avespan=$lcm/($#colcont+1);
471: for ($j=0;$j<=$#colcont;$j++) {
472: my $rid=$colcont[$j];
473: if ($rid=~/^h(.+)/) {
474: $rid=$1;
475: $r->print(
476: ' <a href="#'.$rid.'">'.$hash{'title_'.$rid}.'</a><br>');
477: }
478: }
479: }
480: }
1.2 www 481: # ----------------------------------------------------------------- Start table
1.14 www 482: $r->print('<hr><table cols="'.$lcm.'" border="0">');
1.2 www 483: for ($i=0;$i<=$#rows;$i++) {
484: if ($rows[$i]) {
485: $r->print("\n<tr>");
486: my @colcont=split(/\&/,$rows[$i]);
487: my $avespan=$lcm/($#colcont+1);
488: for ($j=0;$j<=$#colcont;$j++) {
489: my $rid=$colcont[$j];
1.6 www 490: my $add='<td> ';
1.7 www 491: my $adde='</td>';
492: my $hwk='<font color="#223322">';
493: my $hwke='</font>';
1.6 www 494: if ($rid=~/^h(.+)/) {
495: $rid=$1;
1.13 www 496: $add=
497: '<th bgcolor="#AAFF55"><a name="'.$rid.'">';
1.7 www 498: $adde='</th>';
499: }
1.11 www 500: if ($rid=~/^p(\d)(\d)\"([\w\: \(\)\/\,]*)\"(.+)/) {
1.7 www 501: my $code=$1;
1.11 www 502: my $tcode=$2;
503: my $ctext=$3;
504: $rid=$4;
505: if ($tcode eq '1') {
506: $add='<td bgcolor="#AAAAAA">';
507: }
508: if ($code eq '3') {
509: $add='<td bgcolor="#AAFFAA">';
510: } else {
511: $add='<td bgcolor="#FFAAAA">';
512: if ($tcode eq '2') {
513: $add='<td bgcolor="#FFFFAA">';
514: }
515: if ($tcode eq '4') {
516: $add='<td bgcolor="#FFFF33"><blink>';
517: $adde='</blink></td>';
518: }
519: }
1.9 www 520: $hwk='<font color="#888811"><b>';
521: $hwke='</b></font>';
1.10 www 522: if ($code eq '1') {
523: $hwke='</b> ('.$ctext.')</font>';
524: }
1.7 www 525: if ($code eq '2') {
1.9 www 526: $hwk='<font color="#992222"><b>';
527: $hwke='</b> ('.$ctext.')</font>';
1.7 www 528: }
529: if ($code eq '3') {
1.9 www 530: $hwk='<font color="#229922"><b>';
531: $hwke='</b> ('.$ctext.')</font>';
1.7 www 532: }
1.6 www 533: }
1.13 www 534: if ($hash{'src_'.$rid} eq $currenturl) {
535: $add=$add.'<a name="curloc"></a>'.
536: '<font color=red><b>-> </b></font>';
537: $adde=
538: '<font color=red><b> <-</b></font>'.$adde;
539: }
1.7 www 540: $r->print($add.'<a href="'.$hash{'src_'.$rid}.
541: '">'.$hwk.
542: $hash{'title_'.$rid}.$hwke.'</a>'.$adde);
1.2 www 543: }
544: $r->print('</tr>');
545: }
546: }
547: $r->print("\n</table>");
548: $r->print('</body></html>');
549: # -------------------------------------------------------------------- End page
550: }
551: # ------------------------------------------------------------- End render page
552: } else {
553: $r->content_type('text/html');
554: $r->send_http_header;
555: $r->print('<html><body>Coursemap undefined.</body></html>');
556: }
557: # ------------------------------------------------------------------ Untie hash
558: unless (untie(%hash)) {
559: &Apache::lonnet::logthis("<font color=blue>WARNING: ".
560: "Could not untie coursemap $fn (browse).</font>");
1.10 www 561: }
562: unless (untie(%parmhash)) {
563: &Apache::lonnet::logthis("<font color=blue>WARNING: ".
564: "Could not untie parmhash (browse).</font>");
1.2 www 565: }
566: # -------------------------------------------------------------------- All done
567: return OK;
568: # ----------------------------------------------- Errors, hash could no be tied
569: }
570: }
571: }
1.3 www 572:
1.2 www 573: $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized";
574: return HTTP_NOT_ACCEPTABLE;
575: }
1.1 www 576:
577: 1;
578: __END__
1.2 www 579:
580:
581:
582:
583:
584:
585:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>