--- loncom/interface/loncoursedata.pm 2003/10/02 20:48:57 1.100 +++ loncom/interface/loncoursedata.pm 2004/04/30 20:09:18 1.112.2.2 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: loncoursedata.pm,v 1.100 2003/10/02 20:48:57 matthew Exp $ +# $Id: loncoursedata.pm,v 1.112.2.2 2004/04/30 20:09:18 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -73,8 +73,6 @@ and/or itself. =item &get_sequence_assessment_data() -AT THIS TIME THE USE OF THIS FUNCTION IS *NOT* RECOMMENDED - Use lonnavmaps to build a data structure describing the order and assessment contents of each sequence in the current course. @@ -190,6 +188,10 @@ sub get_sequence_assessment_data { $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) { @@ -579,7 +581,7 @@ sub init_dbs { restrictions => 'NOT NULL', auto_inc => 'yes', }, { name => 'part', - type => 'VARCHAR(100)', + type => 'VARCHAR(100) BINARY', restrictions => 'NOT NULL'}, ], 'PRIMARY KEY' => ['part (100)'], @@ -594,10 +596,10 @@ sub init_dbs { restrictions => 'NOT NULL', auto_inc => 'yes', }, { name => 'student', - type => 'VARCHAR(100)', + type => 'VARCHAR(100) BINARY', restrictions => 'NOT NULL'}, { name => 'classification', - type => 'varchar(100)', }, + type => 'varchar(100) BINARY', }, ], 'PRIMARY KEY' => ['student (100)'], 'KEY' => [{ columns => ['student_id']},], @@ -614,9 +616,9 @@ sub init_dbs { { name => 'fullupdatetime', type => 'INT UNSIGNED'}, { name => 'section', - type => 'VARCHAR(100)'}, + type => 'VARCHAR(100) BINARY'}, { name => 'classification', - type => 'VARCHAR(100)', }, + type => 'VARCHAR(100) BINARY', }, ], 'PRIMARY KEY' => ['student_id'], }; @@ -634,7 +636,7 @@ sub init_dbs { type => 'MEDIUMINT UNSIGNED', restrictions => 'NOT NULL' }, { name => 'part', - type => 'VARCHAR(100)', + type => 'VARCHAR(100) BINARY', restrictions => 'NOT NULL'}, { name => 'solved', type => 'TINYTEXT' }, @@ -714,7 +716,7 @@ sub init_dbs { { name => 'awarddetail', type => 'TINYTEXT' }, # { name => 'message', -# type => 'CHAR' }, +# type => 'CHAR BINARY' }, { name => 'response_specific', type => 'TINYTEXT' }, { name => 'response_specific_value', @@ -1039,7 +1041,8 @@ sub get_student_id { $have_read_student_table = 1; } if (! exists($ids_by_student{$student})) { - &Apache::lonmysql::store_row($student_table,[undef,$student,undef]); + &Apache::lonmysql::store_row($student_table, + [undef,$student,undef]); undef(%ids_by_student); my @Result = &Apache::lonmysql::get_rows($student_table); foreach (@Result) { @@ -1222,10 +1225,7 @@ sub update_full_student_data { # However, there is one wrinkle: submissions which end in # and odd number of '\' cause insert errors to occur. # Best trap this somehow... - my ($offensive_string) = ($value =~ /(\\+)$/); - if (length($offensive_string) % 2) { - $value =~ s/\\$/\\\\/; - } + $value = $dbh->quote($value); } if ($field eq 'submissiongrading' || $field eq 'molecule') { @@ -1248,6 +1248,7 @@ sub update_full_student_data { $store_command .= "('".join("','",$symb_id,$part_id, $student_id, $transaction, + $data->{'tries'}, $data->{'award'}, $data->{'awarded'}, $data->{'previous'})."'),"; @@ -1273,14 +1274,21 @@ sub update_full_student_data { while (my ($part_id,$hash2) = each (%$hash1)) { while (my ($resp_id,$hash3) = each (%$hash2)) { while (my ($transaction,$data) = each (%$hash3)) { - $store_command .= "('".join("','",$symb_id,$part_id, - $resp_id,$student_id, - $transaction, - $data->{'tries'}, - $data->{'awarddetail'}, - $data->{'response_specific'}, - $data->{'response_specific_value'}, - $data->{'submission'})."'),"; + my $submission = $data->{'submission'}; + # We have to be careful with user supplied input. + # most of the time we are okay because it is escaped. + # However, there is one wrinkle: submissions which end in + # and odd number of '\' cause insert errors to occur. + # Best trap this somehow... + $submission = $dbh->quote($submission); + $store_command .= "('". + join("','",$symb_id,$part_id, + $resp_id,$student_id, + $transaction, + $data->{'awarddetail'}, + $data->{'response_specific'}, + $data->{'response_specific_value'}). + "',".$submission."),"; $store_rows++; } } @@ -1887,21 +1895,10 @@ sub get_problem_statistics { my $dbh = &Apache::lonmysql::get_dbh(); return undef if (! defined($dbh)); # - # A) Number of Students attempting problem - # B) Total number of tries of students attempting problem - # C) Mod (largest number of tries for solving the problem) - # D) Mean (average number of tries for solving the problem) - # E) Number of students to solve the problem - # F) Number of students to solve the problem by override - # G) Number of students unable to solve the problem - # H) Degree of difficulty : 1-(E+F)/B - # I) Standard deviation of number of tries - # J) Skew of tries: sqrt(sum(Xi-D)^3)/A - # $dbh->do('DROP TABLE '.$stats_table); # May return an error my $request = 'CREATE TEMPORARY TABLE '.$stats_table. - ' SELECT student_id,solved,award,tries FROM '.$performance_table. + ' SELECT student_id,solved,award,awarded,tries FROM '.$performance_table. ' WHERE symb_id='.$symb_id.' AND part_id='.$part_id; if (defined($students)) { $request .= ' AND ('. @@ -1913,16 +1910,22 @@ sub get_problem_statistics { } # &Apache::lonnet::logthis($request); $dbh->do($request); +# &Apache::lonnet::logthis('request = '.$/.$request); + $request = 'SELECT COUNT(*),SUM(tries),MAX(tries),AVG(tries),STD(tries) '. + 'FROM '.$stats_table; my ($num,$tries,$mod,$mean,$STD) = &execute_SQL_request - ($dbh, - 'SELECT COUNT(*),SUM(tries),MAX(tries),AVG(tries),STD(tries) FROM '. - $stats_table); - my ($Solved) = &execute_SQL_request($dbh,'SELECT COUNT(tries) FROM '. - $stats_table. - " WHERE solved='correct_by_student' OR solved='correct_by_scantron'"); - my ($solved) = &execute_SQL_request($dbh,'SELECT COUNT(tries) FROM '. - $stats_table. - " WHERE solved='correct_by_override'"); + ($dbh,$request); +# &Apache::lonnet::logthis('request = '.$/.$request); + $request = 'SELECT SUM(awarded) FROM '.$stats_table; + my ($Solved) = &execute_SQL_request($dbh,$request); +# &Apache::lonnet::logthis('request = '.$/.$request); + $request = 'SELECT SUM(awarded) FROM '.$stats_table. + " WHERE solved='correct_by_override'"; +# &Apache::lonnet::logthis('request = '.$/.$request); + my ($solved) = &execute_SQL_request($dbh,$request); +# $Solved = int($Solved); +# $solved = int($solved); + # $num = 0 if (! defined($num)); $tries = 0 if (! defined($tries)); $mod = 0 if (! defined($mod)); @@ -1942,7 +1945,7 @@ sub get_problem_statistics { $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10; } # - $dbh->do('DROP TABLE '.$stats_table); # May return an error +# $dbh->do('DROP TABLE '.$stats_table); # May return an error # # Store in metadata # @@ -1986,9 +1989,47 @@ sub execute_SQL_request { return (); } -sub get_optionresponse_data { +sub get_student_data { + my ($students,$courseid) = @_; + $courseid = $ENV{'request.course.id'} if (! defined($courseid)); + &setup_table_names($courseid); + my $dbh = &Apache::lonmysql::get_dbh(); + return undef if (! defined($dbh)); + my $request = 'SELECT '. + 'student_id, student '. + 'FROM '.$student_table; + if (defined($students)) { + $request .= ' WHERE ('. + join(' OR ', map {'student_id='. + &get_student_id($_->{'username'}, + $_->{'domain'}) + } @$students + ).')'; + } + $request.= ' ORDER BY student_id'; + my $sth = $dbh->prepare($request); + $sth->execute(); + if ($dbh->err) { + &Apache::lonnet::logthis('error = '.$dbh->errstr()); + return undef; + } + my $dataset = $sth->fetchall_arrayref(); + if (ref($dataset) eq 'ARRAY' && scalar(@$dataset)>0) { + return $dataset; + } +} + +sub RD_student_id { return 0; } +sub RD_awarddetail { return 1; } +sub RD_response_eval { return 2; } +sub RD_submission { return 3; } +sub RD_timestamp { return 4; } +sub RD_tries { return 5; } +sub RD_sname { return 6; } + +sub get_response_data { my ($students,$symb,$response,$courseid) = @_; - return if (! defined($symb) || + return undef if (! defined($symb) || ! defined($response)); $courseid = $ENV{'request.course.id'} if (! defined($courseid)); # @@ -1999,7 +2040,8 @@ sub get_optionresponse_data { my $dbh = &Apache::lonmysql::get_dbh(); return undef if (! defined($dbh)); my $request = 'SELECT '. - 'a.response_specific_value, a.submission, b.timestamp, c.tries '. + 'a.student_id, a.awarddetail, a.response_specific_value, '. + 'a.submission, b.timestamp, c.tries, d.student '. 'FROM '.$fulldump_response_table.' AS a '. 'LEFT JOIN '.$fulldump_timestamp_table.' AS b '. 'ON a.symb_id=b.symb_id AND a.student_id=b.student_id AND '. @@ -2007,26 +2049,84 @@ sub get_optionresponse_data { 'LEFT JOIN '.$fulldump_part_table.' AS c '. 'ON a.symb_id=c.symb_id AND a.student_id=c.student_id AND '. 'a.part_id=c.part_id AND a.transaction = c.transaction '. + 'LEFT JOIN '.$student_table.' AS d '. + 'ON a.student_id=d.student_id '. 'WHERE '. 'a.symb_id='.$symb_id.' AND a.response_id='.$response_id; if (defined($students)) { $request .= ' AND ('. - join(' OR ', map {'student_id='. + join(' OR ', map {'a.student_id='. &get_student_id($_->{'username'}, $_->{'domain'}) } @$students ).')'; } $request .= ' ORDER BY b.timestamp'; - &Apache::lonnet::logthis("request =\n".$request); +# &Apache::lonnet::logthis("request =\n".$request); my $sth = $dbh->prepare($request); $sth->execute(); + if ($dbh->err) { + &Apache::lonnet::logthis('error = '.$dbh->errstr()); + return undef; + } my $dataset = $sth->fetchall_arrayref(); if (ref($dataset) eq 'ARRAY' && scalar(@$dataset)>0) { - return @$dataset; + # Clear the \'s from around the submission + for (my $i =0;$i[$i]->[3] =~ s/(\'$|^\')//g; + } + return $dataset; } } +sub RT_student_id { return 0; } +sub RT_awarded { return 1; } +sub RT_tries { return 2; } +sub RT_timestamp { return 3; } + +sub get_response_time_data { + my ($students,$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); + my $part_id = &get_part_id($part); + # + my $dbh = &Apache::lonmysql::get_dbh(); + return undef if (! defined($dbh)); + 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 '. + '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 + ).')'; + } + $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()); + return undef; + } + my $dataset = $sth->fetchall_arrayref(); + if (ref($dataset) eq 'ARRAY' && scalar(@$dataset)>0) { + return $dataset; + } + +} + ################################################ ################################################ @@ -2127,7 +2227,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] + values [$sdom,$sname,$end,$start,$id,$section,$fullname,$status,$type] The constant values CL_SDOM, CL_SNAME, CL_END, etc. can be used as indices into the returned list to future-proof clients against @@ -2146,6 +2246,7 @@ sub CL_ID { return 4; } sub CL_SECTION { return 5; } sub CL_FULLNAME { return 6; } sub CL_STATUS { return 7; } +sub CL_TYPE { return 8; } sub get_classlist { my ($cid,$cdom,$cnum) = @_; @@ -2162,9 +2263,9 @@ sub get_classlist { } my ($sname,$sdom) = split(/:/,$student); my @Values = split(/:/,$info); - my ($end,$start,$id,$section,$fullname); + my ($end,$start,$id,$section,$fullname,$type); if (@Values > 2) { - ($end,$start,$id,$section,$fullname) = @Values; + ($end,$start,$id,$section,$fullname,$type) = @Values; } else { # We have to get the data ourselves ($end,$start) = @Values; $section = &Apache::lonnet::getsection($sdom,$sname,$cid); @@ -2201,11 +2302,11 @@ sub get_classlist { $status='Active'; } $classlist{$student} = - [$sdom,$sname,$end,$start,$id,$section,$fullname,$status]; + [$sdom,$sname,$end,$start,$id,$section,$fullname,$status,$type]; } if (wantarray()) { return (\%classlist,['domain','username','end','start','id', - 'section','fullname','status']); + 'section','fullname','status','type']); } else { return \%classlist; }