Diff for /loncom/interface/loncoursedata.pm between versions 1.90 and 1.117

version 1.90, 2003/09/26 19:23:14 version 1.117, 2004/02/17 19:56:30
Line 73  and/or itself. Line 73  and/or itself.
   
 =item &get_sequence_assessment_data()  =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   Use lonnavmaps to build a data structure describing the order and 
 assessment contents of each sequence in the current course.  assessment contents of each sequence in the current course.
   
Line 190  sub get_sequence_assessment_data { Line 188  sub get_sequence_assessment_data {
         $title =~ s/\:/\&\#058;/g;          $title =~ s/\:/\&\#058;/g;
         $symb  = $curRes->symb();          $symb  = $curRes->symb();
         $src   = $curRes->src();          $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 $parts = $curRes->parts();
         my %partdata;          my %partdata;
         foreach my $part (@$parts) {          foreach my $part (@$parts) {
Line 197  sub get_sequence_assessment_data { Line 199  sub get_sequence_assessment_data {
             my @Ids       = $curRes->responseIds($part);              my @Ids       = $curRes->responseIds($part);
             $partdata{$part}->{'ResponseTypes'}= \@Responses;              $partdata{$part}->{'ResponseTypes'}= \@Responses;
             $partdata{$part}->{'ResponseIds'}  = \@Ids;              $partdata{$part}->{'ResponseIds'}  = \@Ids;
               # Count how many responses of each type there are in this part
               foreach (@Responses) {
                   $partdata{$part}->{$_}++;
               }
         }          }
         my $assessment = { title => $title,          my $assessment = { title => $title,
                            src   => $src,                             src   => $src,
Line 395  characters) and a KEY on 'part_id'. Line 401  characters) and a KEY on 'part_id'.
   
 =item $student_table  =item $student_table
   
 The student_table has two columns.  The first is a 'student_id' and the second  The student_table has 7 columns.  The first is a 'student_id' assigned by 
 is the text description of the 'student' (typically username:domain) (less  MySQL.  The second is 'student' which is username:domain.  The third through
 than 100 characters).  The 'student_id' is automatically generated by MySQL.  fifth are 'section', 'status' (enrollment status), and 'classification' 
 The use of the name 'student_id' is loaded, I know, but this ID is used ONLY   (to be used in the future).  The sixth and seventh ('updatetime' and 
 internally to the MySQL database and is not the same as the students ID   'fullupdatetime') contain the time of last update and full update of student
 (stored in the students environment).  This table has its PRIMARY KEY on the  data.  This table has its PRIMARY KEY on the 'student_id' column and is indexed
 'student' (100 characters).  on 'student', 'section', and 'status'.
   
 =item $studentdata_table  
   
 The studentdata_table has four columns:  'student_id' (the unique id of   
 the student), 'updatetime' (the time the students data was last updated),  
 'fullupdatetime' (the time the students full data was last updated),  
 'section', and 'classification'( the students current classification).  
 This table has its PRIMARY KEY on 'student_id'.  
   
 =back   =back 
   
Line 456  The response table holds data (documente Line 454  The response table holds data (documente
 associated with a particular response id which is stored when a student   associated with a particular response id which is stored when a student 
 attempts a problem.  The following are the columns of the table, in order:  attempts a problem.  The following are the columns of the table, in order:
 'symb_id','part_id','response_id','student_id','transaction','tries',  'symb_id','part_id','response_id','student_id','transaction','tries',
 'awarddetail', 'awarded','response_specific' (data particular to the response  'awarddetail', 'response_specific' (data particular to the response
 type), 'response_specific_value', and 'submission (the text of the students  type), 'response_specific_value', and 'submission (the text of the students
 submission).  The primary key is based on the first five columns listed above.  submission).  The primary key is based on the first five columns listed above.
   
Line 515  my $current_course =''; Line 513  my $current_course ='';
 my $symb_table;  my $symb_table;
 my $part_table;  my $part_table;
 my $student_table;  my $student_table;
 my $studentdata_table;  
 my $performance_table;  my $performance_table;
 my $parameters_table;  my $parameters_table;
 my $fulldump_response_table;  my $fulldump_response_table;
Line 591  sub init_dbs { Line 588  sub init_dbs {
                       auto_inc     => 'yes', },                        auto_inc     => 'yes', },
                     { name => 'student',                      { name => 'student',
                       type => 'VARCHAR(100)',                        type => 'VARCHAR(100)',
                         restrictions => 'NOT NULL UNIQUE'},
                       { name => 'section',
                         type => 'VARCHAR(100)',
                         restrictions => 'NOT NULL'},
                       { name => 'status',
                         type => 'VARCHAR(15)',
                       restrictions => 'NOT NULL'},                        restrictions => 'NOT NULL'},
                     { name => 'classification',                      { name => 'classification',
                       type => 'varchar(100)', },                        type => 'varchar(100)', },
                     ],  
         'PRIMARY KEY' => ['student (100)'],  
         'KEY' => [{ columns => ['student_id']},],  
     };  
     #  
     my $studentdata_table_def = {  
         id => $studentdata_table,  
         permanent => 'no',  
         columns => [{ name => 'student_id',  
                       type => 'MEDIUMINT UNSIGNED',  
                       restrictions => 'NOT NULL UNIQUE',},  
                     { name => 'updatetime',                      { name => 'updatetime',
                       type => 'INT UNSIGNED'},                        type => 'INT UNSIGNED'},
                     { name => 'fullupdatetime',                      { name => 'fullupdatetime',
                       type => 'INT UNSIGNED'},                        type => 'INT UNSIGNED'},
                     { name => 'section',  
                       type => 'VARCHAR(100)'},  
                     { name => 'classification',  
                       type => 'VARCHAR(100)', },  
                     ],                      ],
         'PRIMARY KEY' => ['student_id'],          'PRIMARY KEY' => ['student_id'],
           'KEY' => [{ columns => ['student (100)',
                                   'section (100)',
                                   'status (15)',]},],
     };      };
     #      #
     my $performance_table_def = {      my $performance_table_def = {
Line 707  sub init_dbs { Line 698  sub init_dbs {
                     { name => 'transaction',                      { name => 'transaction',
                       type => 'MEDIUMINT UNSIGNED',                        type => 'MEDIUMINT UNSIGNED',
                       restrictions => 'NOT NULL' },                        restrictions => 'NOT NULL' },
                     { name => 'tries',  
                       type => 'SMALLINT UNSIGNED',  
                       restrictions => 'NOT NULL' },  
                     { name => 'awarddetail',                      { name => 'awarddetail',
                       type => 'TINYTEXT' },                        type => 'TINYTEXT' },
                     { name => 'awarded',  
                       type => 'TINYTEXT' },  
 #                    { name => 'message',  #                    { name => 'message',
 #                      type => 'CHAR' },  #                      type => 'CHAR' },
                     { name => 'response_specific',                      { name => 'response_specific',
Line 797  sub init_dbs { Line 783  sub init_dbs {
         return 3;          return 3;
     }      }
     #      #
     $tableid = &Apache::lonmysql::create_table($studentdata_table_def);  
     if (! defined($tableid)) {  
         &Apache::lonnet::logthis("error creating studentdata_table: ".  
                                  &Apache::lonmysql::get_error());  
         return 4;  
     }  
     #  
     $tableid = &Apache::lonmysql::create_table($performance_table_def);      $tableid = &Apache::lonmysql::create_table($performance_table_def);
     if (! defined($tableid)) {      if (! defined($tableid)) {
         &Apache::lonnet::logthis("error creating preformance_table: ".          &Apache::lonnet::logthis("error creating preformance_table: ".
Line 1040  sub get_student_id { Line 1019  sub get_student_id {
         $have_read_student_table = 1;          $have_read_student_table = 1;
     }      }
     if (! exists($ids_by_student{$student})) {      if (! exists($ids_by_student{$student})) {
         &Apache::lonmysql::store_row($student_table,[undef,$student,undef]);          &populate_student_table();
         undef(%ids_by_student);          undef(%ids_by_student);
           undef(%students_by_id);
         my @Result = &Apache::lonmysql::get_rows($student_table);          my @Result = &Apache::lonmysql::get_rows($student_table);
         foreach (@Result) {          foreach (@Result) {
             $ids_by_student{$_->[1]}=$_->[0];              $ids_by_student{$_->[1]}=$_->[0];
Line 1065  sub get_student { Line 1045  sub get_student {
     return undef; # error      return undef; # error
 }  }
   
   sub populate_student_table {
       my ($courseid) = @_;
       if (! defined($courseid)) {
           $courseid = $ENV{'request.course.id'};
       }
       #
       &setup_table_names($courseid);
       my $dbh = &Apache::lonmysql::get_dbh();
       my $request = 'INSERT IGNORE INTO '.$student_table.
           "(student,section,status) VALUES ";
       my $classlist = &get_classlist($courseid);
       my $student_count=0;
       while (my ($student,$data) = each %$classlist) {
           my ($section,$status) = ($data->[&CL_SECTION()],
                                    $data->[&CL_STATUS()]);
           if ($section eq '' || $section =~ /^\s*$/) {
               $section = 'none';
           }
           $request .= "('".$student."','".$section."','".$status."'),";
           $student_count++;
       }
       return if ($student_count == 0);
       chop($request);
       $dbh->do($request);
       if ($dbh->err()) {
           &Apache::lonnet::logthis("error ".$dbh->errstr().
                                    " occured executing \n".
                                    $request);
       }
       return;
   }
   
   
   ################################################
   ################################################
   
   =pod
   
   =item &clear_internal_caches()
   
   Causes the internal caches used in get_student_id, get_student,
   get_symb_id, get_symb, get_part_id, and get_part to be undef'd.
   
   Needs to be called before the first operation with the MySQL database
   for a given Apache request.
   
   =cut
   
   ################################################
   ################################################
   sub clear_internal_caches {
       $have_read_part_table = 0;
       undef(%ids_by_part);
       undef(%parts_by_id);
       $have_read_symb_table = 0;
       undef(%ids_by_symb);
       undef(%symbs_by_id);
       $have_read_student_table = 0;
       undef(%ids_by_student);
       undef(%students_by_id);
   }
   
   
 ################################################  ################################################
 ################################################  ################################################
   
Line 1093  a description of the error. Line 1136  a description of the error.
 Once the "fulldump" tables are updated, the tables used for chart and  Once the "fulldump" tables are updated, the tables used for chart and
 spreadsheet (which hold only the current state of the student on their  spreadsheet (which hold only the current state of the student on their
 homework, not historical data) are updated.  If all updates have occured   homework, not historical data) are updated.  If all updates have occured 
 successfully, the studentdata table is updated to reflect the time of the  successfully, $student_table is updated to reflect the time of the update.
 update.  
   
 Notice we do not insert the data and immediately query it.  This means it  Notice we do not insert the data and immediately query it.  This means it
 is possible for there to be data returned this first time that is not   is possible for there to be data returned this first time that is not 
Line 1147  sub update_full_student_data { Line 1189  sub update_full_student_data {
             # We can deal with 'timestamp' right away              # We can deal with 'timestamp' right away
             my @timestamp_storage = ($symb_id,$student_id,              my @timestamp_storage = ($symb_id,$student_id,
                                      $transaction,$value);                                       $transaction,$value);
             my $store_command = 'INSERT INTO '.$fulldump_timestamp_table.              my $store_command = 'INSERT IGNORE INTO '.$fulldump_timestamp_table.
                 " VALUES ('".join("','",@timestamp_storage)."');";                  " VALUES ('".join("','",@timestamp_storage)."');";
             $dbh->do($store_command);              $dbh->do($store_command);
             if ($dbh->err()) {              if ($dbh->err()) {
Line 1183  sub update_full_student_data { Line 1225  sub update_full_student_data {
             }              }
             # deal with response specific data              # deal with response specific data
             if (defined($resp_id) &&              if (defined($resp_id) &&
                 $field =~ /^(tries|                  $field =~ /^(awarddetail|
                              awarddetail|  
                              awarded|  
                              submission|                               submission|
                              submissiongrading|                               submissiongrading|
                              molecule)$/x) {                               molecule)$/x) {
Line 1195  sub update_full_student_data { Line 1235  sub update_full_student_data {
                     # However, there is one wrinkle: submissions which end in                      # However, there is one wrinkle: submissions which end in
                     # and odd number of '\' cause insert errors to occur.                        # and odd number of '\' cause insert errors to occur.  
                     # Best trap this somehow...                      # Best trap this somehow...
                     my ($offensive_string) = ($value =~ /(\\+)$/);                      $value = $dbh->quote($value);
                     if (length($offensive_string) % 2) {  
                         $value =~ s/\\$/\\\\/;  
                     }  
                 }                  }
                 if ($field eq 'submissiongrading' ||                   if ($field eq 'submissiongrading' || 
                     $field eq 'molecule') {                      $field eq 'molecule') {
Line 1212  sub update_full_student_data { Line 1249  sub update_full_student_data {
     }      }
     ##      ##
     ## Store the part data      ## Store the part data
     my $store_command = 'INSERT INTO '.$fulldump_part_table.      my $store_command = 'INSERT IGNORE INTO '.$fulldump_part_table.
         ' VALUES '."\n";          ' VALUES '."\n";
     my $store_rows = 0;      my $store_rows = 0;
     while (my ($symb_id,$hash1) = each (%$partdata)) {      while (my ($symb_id,$hash1) = each (%$partdata)) {
Line 1240  sub update_full_student_data { Line 1277  sub update_full_student_data {
     }      }
     ##      ##
     ## Store the response data      ## Store the response data
     $store_command = 'INSERT INTO '.$fulldump_response_table.      $store_command = 'INSERT IGNORE INTO '.$fulldump_response_table.
         ' VALUES '."\n";          ' VALUES '."\n";
     $store_rows = 0;      $store_rows = 0;
     while (my ($symb_id,$hash1) = each (%$respdata)) {      while (my ($symb_id,$hash1) = each (%$respdata)) {
         while (my ($part_id,$hash2) = each (%$hash1)) {          while (my ($part_id,$hash2) = each (%$hash1)) {
             while (my ($resp_id,$hash3) = each (%$hash2)) {              while (my ($resp_id,$hash3) = each (%$hash2)) {
                 while (my ($transaction,$data) = each (%$hash3)) {                  while (my ($transaction,$data) = each (%$hash3)) {
                     $store_command .= "('".join("','",$symb_id,$part_id,                      my $submission = $data->{'submission'};
                                                 $resp_id,$student_id,                      # We have to be careful with user supplied input.
                                                 $transaction,                      # most of the time we are okay because it is escaped.
                                                 $data->{'tries'},                      # However, there is one wrinkle: submissions which end in
                                                 $data->{'awarddetail'},                      # and odd number of '\' cause insert errors to occur.  
                                                 $data->{'awarded'},                      # Best trap this somehow...
                                                 $data->{'response_specific'},                      $submission = $dbh->quote($submission);
                                                 $data->{'response_specific_value'},                      $store_command .= "('".
                                                 $data->{'submission'})."'),";                          join("','",$symb_id,$part_id,
                                $resp_id,$student_id,
                                $transaction,
                                $data->{'awarddetail'},
                                $data->{'response_specific'},
                                $data->{'response_specific_value'}).
                                "',".$submission."),";
                     $store_rows++;                      $store_rows++;
                 }                  }
             }              }
Line 1284  sub update_full_student_data { Line 1327  sub update_full_student_data {
     ##      ##
     ## Update the students time......      ## Update the students time......
     if ($returnstatus eq 'okay') {      if ($returnstatus eq 'okay') {
         &Apache::lonmysql::replace_row          &store_updatetime($student_id,$time_of_retrieval,$time_of_retrieval);
             ($studentdata_table,          if ($dbh->err) {
              [$student_id,$time_of_retrieval,$time_of_retrieval,undef,undef]);              if ($returnstatus eq 'okay') {
                   $returnstatus = 'error updating student time';
               } else {
                   $returnstatus = 'error updating student time';
               }
           }
     }      }
     return $returnstatus;      return $returnstatus;
 }  }
Line 1353  sub update_student_data { Line 1401  sub update_student_data {
     my @Results = &store_student_data($sname,$sdom,$courseid,\%student_data);      my @Results = &store_student_data($sname,$sdom,$courseid,\%student_data);
     #      #
     # Set the students update time      # Set the students update time
     &Apache::lonmysql::replace_row($studentdata_table,      if ($Results[0] eq 'okay') {
                          [$student_id,$time_of_retrieval,undef,undef,undef]);          &store_updatetime($student_id,$time_of_retrieval,$time_of_retrieval);
       }
     #      #
     return @Results;      return @Results;
 }  }
   
   sub store_updatetime {
       my ($student_id,$updatetime,$fullupdatetime)=@_;
       my $values = '';
       if (defined($updatetime)) {
           $values = 'updatetime='.$updatetime.' ';
       }
       if (defined($fullupdatetime)) {
           if ($values ne '') {
               $values .= ',';
           }
           $values .= 'fullupdatetime='.$fullupdatetime.' ';
       }
       return if ($values eq '');
       my $dbh = &Apache::lonmysql::get_dbh();
       my $request = 'UPDATE '.$student_table.' SET '.$values.
           ' WHERE student_id='.$student_id.' LIMIT 1';
       $dbh->do($request);
   }
   
 sub store_student_data {  sub store_student_data {
     my ($sname,$sdom,$courseid,$student_data) = @_;      my ($sname,$sdom,$courseid,$student_data) = @_;
     #      #
Line 1379  sub store_student_data { Line 1447  sub store_student_data {
     my $starttime = Time::HiRes::time;      my $starttime = Time::HiRes::time;
     my $elapsed = 0;      my $elapsed = 0;
     my $rows_stored;      my $rows_stored;
     my $store_parameters_command  = 'INSERT INTO '.$parameters_table.      my $store_parameters_command  = 'INSERT IGNORE INTO '.$parameters_table.
         ' VALUES '."\n";          ' VALUES '."\n";
     my $num_parameters = 0;      my $num_parameters = 0;
     my $store_performance_command = 'INSERT INTO '.$performance_table.      my $store_performance_command = 'INSERT IGNORE INTO '.$performance_table.
         ' VALUES '."\n";          ' VALUES '."\n";
     return ('error',undef) if (! defined($dbh));      return ('error',undef) if (! defined($dbh));
     while (my ($current_symb,$param_hash) = each(%{$student_data})) {      while (my ($current_symb,$param_hash) = each(%{$student_data})) {
Line 1433  sub store_student_data { Line 1501  sub store_student_data {
     chop $store_performance_command;      chop $store_performance_command;
     chop $store_performance_command;      chop $store_performance_command;
     my $start = Time::HiRes::time;      my $start = Time::HiRes::time;
       $dbh->do($store_performance_command);
       if ($dbh->err()) {
           &Apache::lonnet::logthis(' bigass insert error:'.$dbh->errstr());
           &Apache::lonnet::logthis('command = '.$store_performance_command);
           $returnstatus = 'error: unable to insert performance into database';
           return ($returnstatus,$student_data);
       }
     $dbh->do($store_parameters_command) if ($num_parameters>0);      $dbh->do($store_parameters_command) if ($num_parameters>0);
     if ($dbh->err()) {      if ($dbh->err()) {
         &Apache::lonnet::logthis(' bigass insert error:'.$dbh->errstr());          &Apache::lonnet::logthis(' bigass insert error:'.$dbh->errstr());
Line 1442  sub store_student_data { Line 1517  sub store_student_data {
         $returnstatus = 'error: unable to insert parameters into database';          $returnstatus = 'error: unable to insert parameters into database';
         return ($returnstatus,$student_data);          return ($returnstatus,$student_data);
     }      }
     $dbh->do($store_performance_command);  
     if ($dbh->err()) {  
         &Apache::lonnet::logthis(' bigass insert error:'.$dbh->errstr());  
         &Apache::lonnet::logthis('command = '.$store_performance_command);  
         $returnstatus = 'error: unable to insert performance into database';  
         return ($returnstatus,$student_data);  
     }  
     $elapsed += Time::HiRes::time - $start;      $elapsed += Time::HiRes::time - $start;
     return ($returnstatus,$student_data);      return ($returnstatus,$student_data);
 }  }
Line 1478  sub ensure_tables_are_set_up { Line 1546  sub ensure_tables_are_set_up {
     #      #
     # if the tables do not exist, make them      # if the tables do not exist, make them
     my @CurrentTable = &Apache::lonmysql::tables_in_db();      my @CurrentTable = &Apache::lonmysql::tables_in_db();
     my ($found_symb,$found_student,$found_part,$found_studentdata,      my ($found_symb,$found_student,$found_part,
         $found_performance,$found_parameters,$found_fulldump_part,          $found_performance,$found_parameters,$found_fulldump_part,
         $found_fulldump_response,$found_fulldump_timestamp);          $found_fulldump_response,$found_fulldump_timestamp);
     foreach (@CurrentTable) {      foreach (@CurrentTable) {
         $found_symb        = 1 if ($_ eq $symb_table);          $found_symb        = 1 if ($_ eq $symb_table);
         $found_student     = 1 if ($_ eq $student_table);          $found_student     = 1 if ($_ eq $student_table);
         $found_part        = 1 if ($_ eq $part_table);          $found_part        = 1 if ($_ eq $part_table);
         $found_studentdata = 1 if ($_ eq $studentdata_table);  
         $found_performance = 1 if ($_ eq $performance_table);          $found_performance = 1 if ($_ eq $performance_table);
         $found_parameters  = 1 if ($_ eq $parameters_table);          $found_parameters  = 1 if ($_ eq $parameters_table);
         $found_fulldump_part      = 1 if ($_ eq $fulldump_part_table);          $found_fulldump_part      = 1 if ($_ eq $fulldump_part_table);
         $found_fulldump_response  = 1 if ($_ eq $fulldump_response_table);          $found_fulldump_response  = 1 if ($_ eq $fulldump_response_table);
         $found_fulldump_timestamp = 1 if ($_ eq $fulldump_timestamp_table);          $found_fulldump_timestamp = 1 if ($_ eq $fulldump_timestamp_table);
     }      }
     if (!$found_symb        || !$found_studentdata ||       if (!$found_symb        || 
         !$found_student     || !$found_part   ||          !$found_student     || !$found_part   ||
         !$found_performance || !$found_parameters ||          !$found_performance || !$found_parameters ||
         !$found_fulldump_part || !$found_fulldump_response ||          !$found_fulldump_part || !$found_fulldump_response ||
Line 1515  Input: $sname, $sdom, $courseid Line 1582  Input: $sname, $sdom, $courseid
 Output: $status, $data  Output: $status, $data
   
 This routine ensures the data for a given student is up to date.  This routine ensures the data for a given student is up to date.
 The $studentdata_table is queried to determine the time of the last update.    The $student_table is queried to determine the time of the last update.  
 If the students data is out of date, &update_student_data() is called.    If the students data is out of date, &update_student_data() is called.  
 The return values from the call to &update_student_data() are returned.  The return values from the call to &update_student_data() are returned.
   
Line 1537  sub ensure_current_data { Line 1604  sub ensure_current_data {
          $Apache::lonnet::perlvar{'lonUsersDir'});           $Apache::lonnet::perlvar{'lonUsersDir'});
     #      #
     my $student_id = &get_student_id($sname,$sdom);      my $student_id = &get_student_id($sname,$sdom);
     my @Result = &Apache::lonmysql::get_rows($studentdata_table,      my @Result = &Apache::lonmysql::get_rows($student_table,
                                              "student_id ='$student_id'");                                               "student_id ='$student_id'");
     my $data = undef;      my $data = undef;
     if (@Result) {      if (@Result) {
         $updatetime = $Result[0]->[1];          $updatetime = $Result[0]->[5];  # Ack!  This is dumb!
     }      }
     if ($modifiedtime > $updatetime) {      if ($modifiedtime > $updatetime) {
         ($status,$data) = &update_student_data($sname,$sdom,$courseid);          ($status,$data) = &update_student_data($sname,$sdom,$courseid);
Line 1562  Output: $status Line 1629  Output: $status
   
 This routine ensures the fulldata (the data from a lonnet::dump, not a  This routine ensures the fulldata (the data from a lonnet::dump, not a
 lonnet::currentdump) for a given student is up to date.  lonnet::currentdump) for a given student is up to date.
 The $studentdata_table is queried to determine the time of the last update.    The $student_table is queried to determine the time of the last update.  
 If the students fulldata is out of date, &update_full_student_data() is  If the students fulldata is out of date, &update_full_student_data() is
 called.    called.  
   
Line 1585  sub ensure_current_full_data { Line 1652  sub ensure_current_full_data {
          $Apache::lonnet::perlvar{'lonUsersDir'});           $Apache::lonnet::perlvar{'lonUsersDir'});
     #      #
     my $student_id = &get_student_id($sname,$sdom);      my $student_id = &get_student_id($sname,$sdom);
     my @Result = &Apache::lonmysql::get_rows($studentdata_table,      my @Result = &Apache::lonmysql::get_rows($student_table,
                                              "student_id ='$student_id'");                                               "student_id ='$student_id'");
     my $updatetime;      my $updatetime;
     if (@Result && ref($Result[0]) eq 'ARRAY') {      if (@Result && ref($Result[0]) eq 'ARRAY') {
         $updatetime = $Result[0]->[2];          $updatetime = $Result[0]->[6];
     }      }
     if (! defined($updatetime) || $modifiedtime > $updatetime) {      if (! defined($updatetime) || $modifiedtime > $updatetime) {
         $status = &update_full_student_data($sname,$sdom,$courseid);          $status = &update_full_student_data($sname,$sdom,$courseid);
Line 1678  sub get_student_data_from_performance_ca Line 1745  sub get_student_data_from_performance_ca
         $studentdata->{$symb}->{$base.'.awarddetail'} = $awarddetail;          $studentdata->{$symb}->{$base.'.awarddetail'} = $awarddetail;
         $studentdata->{$symb}->{'timestamp'} = $time if (defined($time) && $time ne '');          $studentdata->{$symb}->{'timestamp'} = $time if (defined($time) && $time ne '');
     }      }
       ## Get misc parameters
       $request = 'SELECT c.symb,a.parameter,a.value '.
           "FROM $student_table AS b ".
           "LEFT JOIN $parameters_table AS a ON b.student_id=a.student_id ".
           "LEFT JOIN $symb_table AS c ON c.symb_id = a.symb_id ".
           "WHERE student='$student'";
       if (defined($symb) && $symb ne '') {
           $request .= " AND c.symb=".$dbh->quote($symb);
       }
       $sth = $dbh->prepare($request);
       $sth->execute();
       if ($sth->err()) {
           &Apache::lonnet::logthis("Unable to execute MySQL request:");
           &Apache::lonnet::logthis("\n".$request."\n");
           &Apache::lonnet::logthis("error is:".$sth->errstr());
           if (defined($symb) && $symb ne '') {
               $studentdata = $studentdata->{$symb};
           }
           return $studentdata;
       }
       #
       foreach my $row (@{$sth->fetchall_arrayref}) {
           $rows_retrieved++;
           my ($symb,$parameter,$value) = (@$row);
           $studentdata->{$symb}->{$parameter}  = $value;
       }
       #
     if (defined($symb) && $symb ne '') {      if (defined($symb) && $symb ne '') {
         $studentdata = $studentdata->{$symb};          $studentdata = $studentdata->{$symb};
     }      }
Line 1821  able to answer it correctly. Line 1915  able to answer it correctly.
 ################################################  ################################################
 ################################################  ################################################
 sub get_problem_statistics {  sub get_problem_statistics {
     my ($students,$symb,$part,$courseid) = @_;      my ($Sections,$status,$symb,$part,$courseid) = @_;
     return if (! defined($symb) || ! defined($part));      return if (! defined($symb) || ! defined($part));
     $courseid = $ENV{'request.course.id'} if (! defined($courseid));      $courseid = $ENV{'request.course.id'} if (! defined($courseid));
     #      #
       &setup_table_names($courseid);
     my $symb_id = &get_symb_id($symb);      my $symb_id = &get_symb_id($symb);
     my $part_id = &get_part_id($part);      my $part_id = &get_part_id($part);
     my $stats_table = $courseid.'_problem_stats';      my $stats_table = $courseid.'_problem_stats';
Line 1832  sub get_problem_statistics { Line 1927  sub get_problem_statistics {
     my $dbh = &Apache::lonmysql::get_dbh();      my $dbh = &Apache::lonmysql::get_dbh();
     return undef if (! defined($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      $dbh->do('DROP TABLE '.$stats_table);  # May return an error
     my $request =       my $request = 
         'CREATE TEMPORARY TABLE '.$stats_table.          'CREATE TEMPORARY TABLE '.$stats_table.' '.
             ' SELECT student_id,solved,award,tries FROM '.$performance_table.          'SELECT a.student_id,a.solved,a.award,a.awarded,a.tries '.
                 ' WHERE symb_id='.$symb_id.' AND part_id='.$part_id;          'FROM '.$performance_table.' AS a ';
     if (defined($students)) {      if ((defined($Sections) && lc($Sections->[0]) ne 'all') || 
           (defined($status)   && lc($status)        ne 'any')) {
           $request .= 'NATURAL LEFT JOIN '.$student_table.' AS b ';
       }
       $request .= ' WHERE a.symb_id='.$symb_id.' AND a.part_id='.$part_id;
       if (defined($Sections) && lc($Sections->[0]) ne 'all') {
         $request .= ' AND ('.          $request .= ' AND ('.
             join(' OR ', map {'student_id='.              join(' OR ', map { "b.section='".$_."'" } @$Sections
                                   &get_student_id($_->{'username'},  
                                                   $_->{'domain'})  
                                   } @$students  
                  ).')';                   ).')';
     }      }
 #    &Apache::lonnet::logthis($request);      if (defined($status) && lc($status) ne 'any') {
           $request .= " AND b.status='".$status."'";
       }
     $dbh->do($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      my ($num,$tries,$mod,$mean,$STD) = &execute_SQL_request
         ($dbh,          ($dbh,$request);
          'SELECT COUNT(*),SUM(tries),MAX(tries),AVG(tries),STD(tries) FROM '.  #    &Apache::lonnet::logthis('request = '.$/.$request);
          $stats_table);      $request = 'SELECT SUM(awarded) FROM '.$stats_table;
     my ($Solved) = &execute_SQL_request($dbh,'SELECT COUNT(tries) FROM '.      my ($Solved) = &execute_SQL_request($dbh,$request);
                                         $stats_table.  #    &Apache::lonnet::logthis('request = '.$/.$request);
                                         " WHERE solved='correct_by_student'");      $request = 'SELECT SUM(awarded) FROM '.$stats_table.
     my ($solved) = &execute_SQL_request($dbh,'SELECT COUNT(tries) FROM '.          " WHERE solved='correct_by_override'";
                                         $stats_table.  #    &Apache::lonnet::logthis('request = '.$/.$request);
                                         " WHERE solved='correct_by_override'");      my ($solved) = &execute_SQL_request($dbh,$request);
   #    $Solved = int($Solved);
   #    $solved = int($solved);
       #
     $num    = 0 if (! defined($num));      $num    = 0 if (! defined($num));
     $tries  = 0 if (! defined($tries));      $tries  = 0 if (! defined($tries));
     $mod    = 0 if (! defined($mod));      $mod    = 0 if (! defined($mod));
Line 1887  sub get_problem_statistics { Line 1981  sub get_problem_statistics {
         $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;          $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      # Store in metadata
     #      #
Line 1931  sub execute_SQL_request { Line 2025  sub execute_SQL_request {
     return ();      return ();
 }  }
   
   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 undef if (! defined($symb) || 
                  ! defined($response));
       $courseid = $ENV{'request.course.id'} if (! defined($courseid));
       #
       &setup_table_names($courseid);
       my $symb_id = &get_symb_id($symb);
       my $response_id = &get_part_id($response);
       #
       my $dbh = &Apache::lonmysql::get_dbh();
       return undef if (! defined($dbh));
       my $request = 'SELECT '.
           '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 '.
           'a.transaction = b.transaction '.
           '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 {'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) {
           # Clear the \'s from around the submission
           for (my $i =0;$i<scalar(@$dataset);$i++) {
               $dataset->[$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;
       }
   
   }
   
   ################################################
   ################################################
   
   =pod
   
   =item &get_student_scores($Sections,$Symbs,$enrollment,$courseid)
   
   =cut
   
   ################################################
   ################################################
   sub get_student_scores {
       my ($Sections,$Symbs,$enrollment,$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 $tmptable = $courseid.'_temp_'.time;
       #
       my $symb_requirements;
       if (defined($Symbs)  && @$Symbs) {
           $symb_requirements = '('.
               join(' OR ', map{ "(a.symb_id='".&get_symb_id($_->{'symb'}).
                                     "' AND a.part_id='".&get_part_id($_->{'part'}).
                                     "')"
                                 } @$Symbs).')';
       }
       #
       my $student_requirements;
       if ( (defined($Sections) && $Sections->[0] ne 'all')) {
           $student_requirements = '('.
               join(' OR ', map { "b.section='".$_."'" } @$Sections
                    ).')';
       }
       #
       my $enrollment_requirements=undef;
       if (defined($enrollment) && $enrollment ne 'Any') {
           $enrollment_requirements = "b.status='".$enrollment."'";
       }
       ##
       ##
       my $request = 'CREATE TEMPORARY TABLE IF NOT EXISTS '.$tmptable.
           ' SELECT a.student_id,SUM(a.awarded) AS score FROM '.
           $performance_table.' AS a ';
       if (defined($student_requirements) || defined($enrollment_requirements)) {
           $request .= ' NATURAL LEFT JOIN '.$student_table.' AS b ';
       }
       if (defined($symb_requirements)      || 
           defined($student_requirements)   ||
           defined($enrollment_requirements) ) {
           $request .= ' WHERE ';
       }
       if (defined($symb_requirements)) {
           $request .= $symb_requirements.' AND ';
       }
       if (defined($student_requirements)) {
           $request .= $student_requirements.' AND ';
       }
       if (defined($enrollment_requirements)) {
           $request .= $enrollment_requirements.' AND ';
       }
       $request =~ s/ AND $//;
       $request .= ' GROUP BY a.student_id';
   #    &Apache::lonnet::logthis("request = \n".$request);
       my $sth = $dbh->prepare($request);
       $sth->execute();
       if ($dbh->err) {
           &Apache::lonnet::logthis('error = '.$dbh->errstr());
           return undef;
       }
       $request = 'SELECT score,COUNT(*) FROM '.$tmptable.' GROUP BY score';
   #    &Apache::lonnet::logthis("request = \n".$request);
       $sth = $dbh->prepare($request);
       $sth->execute();
       if ($dbh->err) {
           &Apache::lonnet::logthis('error = '.$dbh->errstr());
           return undef;
       }
       my $dataset = $sth->fetchall_arrayref();
       return $dataset;
   }
   
 ################################################  ################################################
 ################################################  ################################################
Line 1975  sub setup_table_names { Line 2288  sub setup_table_names {
     $symb_table        = $base_id.'_'.'symb';      $symb_table        = $base_id.'_'.'symb';
     $part_table        = $base_id.'_'.'part';      $part_table        = $base_id.'_'.'part';
     $student_table     = $base_id.'_'.'student';      $student_table     = $base_id.'_'.'student';
     $studentdata_table = $base_id.'_'.'studentdata';  
     $performance_table = $base_id.'_'.'performance';      $performance_table = $base_id.'_'.'performance';
     $parameters_table  = $base_id.'_'.'parameters';      $parameters_table  = $base_id.'_'.'parameters';
     $fulldump_part_table      = $base_id.'_'.'partdata';      $fulldump_part_table      = $base_id.'_'.'partdata';
Line 1986  sub setup_table_names { Line 2298  sub setup_table_names {
                $symb_table,                 $symb_table,
                $part_table,                 $part_table,
                $student_table,                 $student_table,
                $studentdata_table,  
                $performance_table,                 $performance_table,
                $parameters_table,                 $parameters_table,
                $fulldump_part_table,                 $fulldump_part_table,
Line 2032  $ENV{'course.'.$cid.'.domain'}, and $ENV Line 2343  $ENV{'course.'.$cid.'.domain'}, and $ENV
   
 Returns a reference to a hash which contains:  Returns a reference to a hash which contains:
  keys    '$sname:$sdom'   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  The constant values CL_SDOM, CL_SNAME, CL_END, etc. can be used
 as indices into the returned list to future-proof clients against  as indices into the returned list to future-proof clients against
Line 2051  sub CL_ID       { return 4; } Line 2362  sub CL_ID       { return 4; }
 sub CL_SECTION  { return 5; }  sub CL_SECTION  { return 5; }
 sub CL_FULLNAME { return 6; }  sub CL_FULLNAME { return 6; }
 sub CL_STATUS   { return 7; }  sub CL_STATUS   { return 7; }
   sub CL_TYPE     { return 8; }
   
 sub get_classlist {  sub get_classlist {
     my ($cid,$cdom,$cnum) = @_;      my ($cid,$cdom,$cnum) = @_;
Line 2067  sub get_classlist { Line 2379  sub get_classlist {
         }          }
         my ($sname,$sdom) = split(/:/,$student);          my ($sname,$sdom) = split(/:/,$student);
         my @Values = split(/:/,$info);          my @Values = split(/:/,$info);
         my ($end,$start,$id,$section,$fullname);          my ($end,$start,$id,$section,$fullname,$type);
         if (@Values > 2) {          if (@Values > 2) {
             ($end,$start,$id,$section,$fullname) = @Values;              ($end,$start,$id,$section,$fullname,$type) = @Values;
         } else { # We have to get the data ourselves          } else { # We have to get the data ourselves
             ($end,$start) = @Values;              ($end,$start) = @Values;
             $section = &Apache::lonnet::getsection($sdom,$sname,$cid);              $section = &Apache::lonnet::getsection($sdom,$sname,$cid);
Line 2106  sub get_classlist { Line 2418  sub get_classlist {
             $status='Active';              $status='Active';
         }          }
         $classlist{$student} =           $classlist{$student} = 
             [$sdom,$sname,$end,$start,$id,$section,$fullname,$status];              [$sdom,$sname,$end,$start,$id,$section,$fullname,$status,$type];
     }      }
     if (wantarray()) {      if (wantarray()) {
         return (\%classlist,['domain','username','end','start','id',          return (\%classlist,['domain','username','end','start','id',
                              'section','fullname','status']);                               'section','fullname','status','type']);
     } else {      } else {
         return \%classlist;          return \%classlist;
     }      }

Removed from v.1.90  
changed lines
  Added in v.1.117


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>