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