--- loncom/interface/loncoursedata.pm 2004/06/04 17:46:10 1.134 +++ loncom/interface/loncoursedata.pm 2005/03/03 18:57:36 1.144 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: loncoursedata.pm,v 1.134 2004/06/04 17:46:10 matthew Exp $ +# $Id: loncoursedata.pm,v 1.144 2005/03/03 18:57:36 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -66,161 +66,6 @@ and/or itself. =cut -#################################################### -#################################################### - -=pod - -=item &get_sequence_assessment_data() - -Use lonnavmaps to build a data structure describing the order and -assessment contents of each sequence in the current course. - -The returned structure is a hash reference. - -{ title => 'title', - symb => 'symb', - src => '/s/o/u/r/c/e', - type => (container|assessment), - num_assess => 2, # only for container - parts => [11,13,15], # only for assessment - response_ids => [12,14,16], # only for assessment - contents => [........] # only for container -} - -$hash->{'contents'} is a reference to an array of hashes of the same structure. - -Also returned are array references to the sequences and assessments contained -in the course. - - -=cut - -#################################################### -#################################################### -sub get_sequence_assessment_data { - my $fn=$ENV{'request.course.fn'}; - ## - ## use navmaps - my $navmap = Apache::lonnavmaps::navmap->new(); - if (!defined($navmap)) { - return 'Can not open Coursemap'; - } - # We explicity grab the top level map because I am not sure we - # are pulling it from the iterator. - my $top_level_map = $navmap->getById('0.0'); - # - my $iterator = $navmap->getIterator(undef, undef, undef, 1); - my $curRes = $iterator->next(); # Top level sequence - ## - ## Prime the pump - ## - ## We are going to loop until we run out of sequences/pages to explore for - ## resources. This means we have to start out with something to look - ## at. - my $title = $ENV{'course.'.$ENV{'request.course.id'}.'.description'}; - my $symb = $top_level_map->symb(); - my $src = $top_level_map->src(); - my $randompick = $top_level_map->randompick(); - # - my @Sequences; - my @Assessments; - my @Nested_Sequences = (); # Stack of sequences, keeps track of depth - my $top = { title => $title, - src => $src, - symb => $symb, - type => 'container', - num_assess => 0, - num_assess_parts => 0, - contents => [], - randompick => $randompick, - }; - push (@Sequences,$top); - push (@Nested_Sequences, $top); - # - # We need to keep track of which sequences contain homework problems - # - my $previous_too; - my $previous; - while (scalar(@Nested_Sequences)) { - $previous_too = $previous; - $previous = $curRes; - $curRes = $iterator->next(); - my $currentmap = $Nested_Sequences[-1]; # Last one on the stack - if ($curRes == $iterator->BEGIN_MAP()) { - if (! ref($previous)) { - $previous = $previous_too; - } - if (! ref($previous)) { - next; - } - # get the map itself, instead of BEGIN_MAP - $title = $previous->title(); - $title =~ s/\:/\&\#058;/g; - $symb = $previous->symb(); - $src = $previous->src(); - # pick up the filename if there is no title available - if (! defined($title) || $title eq '') { - ($title) = ($src=~/\/([^\/]*)$/); - } - $randompick = $previous->randompick(); - my $newmap = { title => $title, - src => $src, - symb => $symb, - type => 'container', - num_assess => 0, - randompick => $randompick, - contents => [], - }; - push (@{$currentmap->{'contents'}},$newmap); # this is permanent - push (@Sequences,$newmap); - push (@Nested_Sequences, $newmap); # this is a stack - next; - } - if ($curRes == $iterator->END_MAP()) { - pop(@Nested_Sequences); - next; - } - next if (! ref($curRes)); - next if (! $curRes->is_problem() && $curRes->src() !~ /\.survey$/); - # Okay, from here on out we only deal with assessments - $title = $curRes->title(); - $title =~ s/\:/\&\#058;/g; - $symb = $curRes->symb(); - $src = $curRes->src(); - # Grab the filename if there is not title available - if (! defined($title) || $title eq '') { - ($title) = ($src=~ m:/([^/]*)$:); - } - my $parts = $curRes->parts(); - my %partdata; - foreach my $part (@$parts) { - my @Responses = $curRes->responseType($part); - my @Ids = $curRes->responseIds($part); - $partdata{$part}->{'ResponseTypes'}= \@Responses; - $partdata{$part}->{'ResponseIds'} = \@Ids; - # Count how many responses of each type there are in this part - foreach (@Responses) { - $partdata{$part}->{$_}++; - } - } - my $assessment = { title => $title, - src => $src, - symb => $symb, - type => 'assessment', - parts => $parts, - num_parts => scalar(@$parts), - partdata => \%partdata, - }; - push(@Assessments,$assessment); - push(@{$currentmap->{'contents'}},$assessment); - $currentmap->{'num_assess'}++; - $currentmap->{'num_assess_parts'}+= scalar(@$parts); - } - $navmap->untieHashes(); - return ($top,\@Sequences,\@Assessments); -} - sub LoadDiscussion { my ($courseID)=@_; my %Discuss=(); @@ -250,76 +95,6 @@ sub LoadDiscussion { =pod -=item &GetUserName(username,userdomain) - -Returns a hash with the following entries: - 'firstname', 'middlename', 'lastname', 'generation', and 'fullname' - - 'fullname' is the result of &Apache::loncoursedata::ProcessFullName. - -=cut - -################################################ -################################################ -sub GetUserName { - my ($username,$userdomain) = @_; - $username = $ENV{'user.name'} if (! defined($username)); - $userdomain = $ENV{'user.domain'} if (! defined($username)); - my %userenv = &Apache::lonnet::get('environment', - ['firstname','middlename','lastname','generation'], - $userdomain,$username); - $userenv{'fullname'} = &ProcessFullName($userenv{'lastname'}, - $userenv{'generation'}, - $userenv{'firstname'}, - $userenv{'middlename'}); - return %userenv; -} - -################################################ -################################################ - -=pod - -=item &ProcessFullName() - -Takes lastname, generation, firstname, and middlename (or some partial -set of this data) and returns the full name version as a string. Format -is Lastname generation, firstname middlename or a subset of this. - -=cut - -################################################ -################################################ -sub ProcessFullName { - my ($lastname, $generation, $firstname, $middlename)=@_; - my $Str = ''; - - # Strip whitespace preceeding & following name components. - $lastname =~ s/(\s+$|^\s+)//g; - $generation =~ s/(\s+$|^\s+)//g; - $firstname =~ s/(\s+$|^\s+)//g; - $middlename =~ s/(\s+$|^\s+)//g; - - if($lastname ne '') { - $Str .= $lastname; - $Str .= ' '.$generation if ($generation ne ''); - $Str .= ','; - $Str .= ' '.$firstname if ($firstname ne ''); - $Str .= ' '.$middlename if ($middlename ne ''); - } else { - $Str .= $firstname if ($firstname ne ''); - $Str .= ' '.$middlename if ($middlename ne ''); - $Str .= ' '.$generation if ($generation ne ''); - } - - return $Str; -} - -################################################ -################################################ - -=pod - =item &make_into_hash($values); Returns a reference to a hash as described by $values. $values is @@ -857,6 +632,13 @@ sub init_dbs { =item &delete_caches() +This routine drops all the tables associated with a course from the +MySQL database. + +Input: course id (optional, determined by environment if omitted) + +Returns: nothing + =cut ################################################ @@ -1138,7 +920,6 @@ sub clear_internal_caches { undef(%students_by_id); } - ################################################ ################################################ @@ -2108,10 +1889,24 @@ sub populate_weight_table { } # &setup_table_names($courseid); - my ($top,$sequences,$assessments) = get_sequence_assessment_data(); - if (! defined($top) || ! ref($top)) { - # There has been an error, better report it - &Apache::lonnet::logthis('top is undefined'); + my $navmap = Apache::lonnavmaps::navmap->new(); + if (!defined($navmap)) { + &Apache::lonnet::logthis('loncoursedata::populate_weight_table:'.$/. + ' unable to get navmaps resource'.$/. + ' '.join(' ',(caller))); + return; + } + my @sequences = $navmap->retrieveResources(undef, + sub { shift->is_map(); },1,0,1); + my @resources; + foreach my $seq (@sequences) { + push(@resources,$navmap->retrieveResources($seq, + sub {shift->is_problem();}, + 0,0,0)); + } + if (! scalar(@resources)) { + &Apache::lonnet::logthis('loncoursedata::populate_weight_table:'.$/. + ' no resources returned for '.$courseid); return; } # Since we use lonnet::EXT to retrieve problem weights, @@ -2121,12 +1916,12 @@ sub populate_weight_table { my $request = 'INSERT IGNORE INTO '.$weight_table. "(symb_id,part_id,weight) VALUES "; my $weight; - foreach my $res (@$assessments) { - my $symb_id = &get_symb_id($res->{'symb'}); - foreach my $part (@{$res->{'parts'}}) { + foreach my $res (@resources) { + my $symb_id = &get_symb_id($res->symb); + foreach my $part (@{$res->parts}) { my $part_id = &get_part_id($part); $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight', - $res->{'symb'}, + $res->symb, undef,undef,undef); if (!defined($weight) || ($weight eq '')) { $weight=1; @@ -2298,10 +2093,18 @@ Returns: the sum of the score on the pro ######################################################## ######################################################## sub get_sum_of_scores { - my ($resource,$part,$students,$courseid,$starttime,$endtime) = @_; + my ($symb,$part,$students,$courseid,$starttime,$endtime) = @_; if (! defined($courseid)) { $courseid = $ENV{'request.course.id'}; } + if (defined($students) && + ((@$students == 0) || + (@$students == 1 && (! defined($students->[0]) || + $students->[0] eq '')) + ) + ){ + undef($students); + } # &setup_table_names($courseid); my $dbh = &Apache::lonmysql::get_dbh(); @@ -2309,7 +2112,7 @@ sub get_sum_of_scores { my $request = 'SELECT SUM(a.awarded*w.weight),SUM(w.weight) FROM '. $performance_table.' AS a '. 'NATURAL LEFT JOIN '.$weight_table.' AS w '; - $request .= 'WHERE a.symb_id='.&get_symb_id($resource->{'symb'}). + $request .= 'WHERE a.symb_id='.&get_symb_id($symb). ' AND a.part_id='.&get_part_id($part); if (defined($time_limits)) { $request .= ' AND '.$time_limits; @@ -2324,7 +2127,9 @@ sub get_sum_of_scores { $sth->execute(); my $rows = $sth->fetchrow_arrayref(); if ($dbh->err) { - &Apache::lonnet::logthis('error = '.$dbh->errstr()); + &Apache::lonnet::logthis('error 1 = '.$dbh->errstr()); + &Apache::lonnet::logthis('prepared then executed, fetchrow_arrayrefed'. + $/.$request); return (undef,undef); } return ($rows->[0],$rows->[1]); @@ -2504,7 +2309,8 @@ sub get_student_data { my $sth = $dbh->prepare($request); $sth->execute(); if ($dbh->err) { - &Apache::lonnet::logthis('error = '.$dbh->errstr()); + &Apache::lonnet::logthis('error 2 = '.$dbh->errstr()); + &Apache::lonnet::logthis('prepared then executed '.$/.$request); return undef; } my $dataset = $sth->fetchall_arrayref(); @@ -2529,7 +2335,15 @@ sub get_response_data { # &setup_table_names($courseid); my $symb_id = &get_symb_id($symb); + if (! defined($symb_id)) { + &Apache::lonnet::logthis('Unable to find symb for '.$symb.' in '.$courseid); + return undef; + } my $response_id = &get_part_id($response); + if (! defined($response_id)) { + &Apache::lonnet::logthis('Unable to find id for '.$response.' in '.$courseid); + return undef; + } # my $dbh = &Apache::lonmysql::get_dbh(); return undef if (! defined($dbh)); @@ -2565,7 +2379,8 @@ sub get_response_data { my $sth = $dbh->prepare($request); $sth->execute(); if ($dbh->err) { - &Apache::lonnet::logthis('error = '.$dbh->errstr()); + &Apache::lonnet::logthis('error 3 = '.$dbh->errstr()); + &Apache::lonnet::logthis('prepared then executed '.$/.$request); return undef; } my $dataset = $sth->fetchall_arrayref(); @@ -2621,7 +2436,8 @@ sub get_response_data_by_student { my $sth = $dbh->prepare($request); $sth->execute(); if ($dbh->err) { - &Apache::lonnet::logthis('error = '.$dbh->errstr()); + &Apache::lonnet::logthis('error 4 = '.$dbh->errstr()); + &Apache::lonnet::logthis('prepared then executed '.$/.$request); return undef; } my $dataset = $sth->fetchall_arrayref(); @@ -2641,39 +2457,54 @@ sub RT_tries { return 2; } sub RT_timestamp { return 3; } sub get_response_time_data { - my ($students,$symb,$part,$courseid) = @_; + my ($sections,$enrollment,$symb,$part,$courseid) = @_; return undef if (! defined($symb) || ! defined($part)); $courseid = $ENV{'request.course.id'} if (! defined($courseid)); # &setup_table_names($courseid); my $symb_id = &get_symb_id($symb); + if (! defined($symb_id)) { + &Apache::lonnet::logthis('Unable to find symb for '.$symb.' in '.$courseid); + return undef; + } my $part_id = &get_part_id($part); + if (! defined($part_id)) { + &Apache::lonnet::logthis('Unable to find id for '.$part.' in '.$courseid); + return undef; + } # my $dbh = &Apache::lonmysql::get_dbh(); return undef if (! defined($dbh)); + my ($student_requirements,$enrollment_requirements) = + &limit_by_section_and_status($sections,$enrollment,'d'); my $request = 'SELECT '. 'a.student_id, a.awarded, a.tries, b.timestamp '. 'FROM '.$fulldump_part_table.' AS a '. - 'NATURAL LEFT JOIN '.$fulldump_timestamp_table.' AS b '. -# 'ON a.symb_id=b.symb_id AND a.student_id=b.student_id AND '. -# 'a.transaction = b.transaction '. + 'LEFT JOIN '.$fulldump_timestamp_table.' AS b '. + 'ON a.symb_id=b.symb_id AND a.student_id=b.student_id AND '. + 'a.transaction = b.transaction '. + 'LEFT JOIN '.$student_table.' as d '. + 'ON a.student_id=d.student_id '. 'WHERE '. 'a.symb_id='.$symb_id.' AND a.part_id='.$part_id; - if (defined($students)) { - $request .= ' AND ('. - join(' OR ', map {'a.student_id='. - &get_student_id($_->{'username'}, - $_->{'domain'}) - } @$students - ).')'; + if (defined($student_requirements) || defined($enrollment_requirements)) { + $request .= ' AND '; + if (defined($student_requirements)) { + $request .= $student_requirements.' AND '; + } + if (defined($enrollment_requirements)) { + $request .= $enrollment_requirements.' AND '; + } + $request =~ s/( AND )$//; } $request .= ' ORDER BY b.timestamp'; # &Apache::lonnet::logthis("request =\n".$request); my $sth = $dbh->prepare($request); $sth->execute(); if ($dbh->err) { - &Apache::lonnet::logthis('error = '.$dbh->errstr()); + &Apache::lonnet::logthis('error 5 = '.$dbh->errstr()); + &Apache::lonnet::logthis('prepared then executed '.$/.$request); return undef; } my $dataset = $sth->fetchall_arrayref(); @@ -2763,7 +2594,8 @@ sub get_student_scores { my $sth = $dbh->prepare($request); $sth->execute(); if ($dbh->err) { - &Apache::lonnet::logthis('error = '.$dbh->errstr()); + &Apache::lonnet::logthis('error 6 = '.$dbh->errstr()); + &Apache::lonnet::logthis('prepared then executed '.$/.$request); return undef; } $request = 'SELECT score,COUNT(*) FROM '.$tmptable.' GROUP BY score'; @@ -2771,7 +2603,8 @@ sub get_student_scores { $sth = $dbh->prepare($request); $sth->execute(); if ($dbh->err) { - &Apache::lonnet::logthis('error = '.$dbh->errstr()); + &Apache::lonnet::logthis('error 7 = '.$dbh->errstr()); + &Apache::lonnet::logthis('prepared then executed '.$/.$request); return undef; } my $dataset = $sth->fetchall_arrayref(); @@ -2878,7 +2711,7 @@ $ENV{'course.'.$cid.'.domain'}, and $ENV Returns a reference to a hash which contains: keys '$sname:$sdom' - values [$sdom,$sname,$end,$start,$id,$section,$fullname,$status,$type] + values [$sdom,$sname,$end,$start,$id,$section,$fullname,$status,$type,$lockedtype] The constant values CL_SDOM, CL_SNAME, CL_END, etc. can be used as indices into the returned list to future-proof clients against @@ -2898,6 +2731,7 @@ sub CL_SECTION { return 5; } sub CL_FULLNAME { return 6; } sub CL_STATUS { return 7; } sub CL_TYPE { return 8; } +sub CL_LOCKEDTYPE { return 9; } sub get_classlist { my ($cid,$cdom,$cnum) = @_; @@ -2914,9 +2748,9 @@ sub get_classlist { } my ($sname,$sdom) = split(/:/,$student); my @Values = split(/:/,$info); - my ($end,$start,$id,$section,$fullname,$type); + my ($end,$start,$id,$section,$fullname,$type,$lockedtype); if (@Values > 2) { - ($end,$start,$id,$section,$fullname,$type) = @Values; + ($end,$start,$id,$section,$fullname,$type,$lockedtype) = @Values; } else { # We have to get the data ourselves ($end,$start) = @Values; $section = &Apache::lonnet::getsection($sdom,$sname,$cid); @@ -2931,14 +2765,13 @@ sub get_classlist { &Apache::lonnet::logthis('unable to retrieve environment '. 'for '.$sname.':'.$sdom); } else { - $fullname = &ProcessFullName(@info{qw/lastname generation - firstname middlename/}); + $fullname = &Apache::lonnet::format_name(@info{qw/firstname middlename lastname generation/},'lastname'); $id = $info{'id'}; } # Update the classlist with this students information if ($fullname ne 'not available') { - my $enrolldata = join(':',$end,$start,$id,$section,$fullname); - my $reply=&Apache::lonnet::cput('classlist', + my $enrolldata = join(':',$end,$start,$id,$section,$fullname); + my $reply=&Apache::lonnet::cput('classlist', {$student => $enrolldata}, $cdom,$cnum); if ($reply !~ /^(ok|delayed)/) { @@ -2953,11 +2786,11 @@ sub get_classlist { $status='Active'; } $classlist{$student} = - [$sdom,$sname,$end,$start,$id,$section,$fullname,$status,$type]; + [$sdom,$sname,$end,$start,$id,$section,$fullname,$status,$type,$lockedtype]; } if (wantarray()) { return (\%classlist,['domain','username','end','start','id', - 'section','fullname','status','type']); + 'section','fullname','status','type','lockedtype']); } else { return \%classlist; }