--- loncom/interface/loncoursedata.pm 2006/05/05 21:35:31 1.168 +++ loncom/interface/loncoursedata.pm 2008/11/17 14:16:55 1.189 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: loncoursedata.pm,v 1.168 2006/05/05 21:35:31 raeburn Exp $ +# $Id: loncoursedata.pm,v 1.189 2008/11/17 14:16:55 jms Exp $ # # Copyright Michigan State University Board of Trustees # @@ -30,7 +30,7 @@ =head1 NAME -loncoursedata +Apache::loncoursedata =head1 SYNOPSIS @@ -38,23 +38,24 @@ Set of functions that download and proce =head1 PACKAGES USED - Apache::Constants qw(:common :http) - Apache::lonnet() - Apache::lonhtmlcommon - HTML::TokeParser - GDBM_File - + Apache::lonnet + Apache::longroup + Time::HiRes + Apache::lonmysql + LONCAPA + Digest::MD5 + =cut package Apache::loncoursedata; use strict; use Apache::lonnet; -use Apache::lonhtmlcommon; -use Time::HiRes; -use Apache::lonmysql; -use HTML::TokeParser; -use GDBM_File; +use Apache::longroup(); +use Time::HiRes(); +use Apache::lonmysql(); +use LONCAPA; +use Digest::MD5(); =pod @@ -63,18 +64,11 @@ use GDBM_File; This section contains all the functions that get data from other servers and/or itself. -=cut - -################################################ -################################################ - -=pod - =item &make_into_hash($values); Returns a reference to a hash as described by $values. $values is assumed to be the result of - join(':',map {&Apache::lonnet::escape($_)} %orighash); + join(':',map {&escape($_)} %orighash); This is a helper function for get_current_state. @@ -84,8 +78,7 @@ This is a helper function for get_curren ################################################ sub make_into_hash { my $values = shift; - my %tmp = map { &Apache::lonnet::unescape($_); } - split(':',$values); + my %tmp = map { &unescape($_); } split(':',$values); return \%tmp; } @@ -367,8 +360,11 @@ sub init_dbs { { name => 'section', type => 'VARCHAR(100) BINARY', restrictions => 'NOT NULL'}, - { name => 'status', - type => 'VARCHAR(15) BINARY', + { name => 'start', + type => 'INT', + restrictions => 'NOT NULL'}, + { name => 'end', + type => 'INT', restrictions => 'NOT NULL'}, { name => 'classification', type => 'VARCHAR(100) BINARY', }, @@ -380,7 +376,8 @@ sub init_dbs { 'PRIMARY KEY' => ['student_id'], 'KEY' => [{ columns => ['student (100)', 'section (100)', - 'status (15)',]},], + 'start', + 'end']},], }; # my $groupnames_table_def = { @@ -904,18 +901,21 @@ sub populate_student_table { &init_dbs($courseid,0); my $dbh = &Apache::lonmysql::get_dbh(); my $request = 'INSERT IGNORE INTO '.$student_table. - "(student,section,status) VALUES "; + "(student,section,start,end) VALUES "; my $cdom = $env{'course.'.$courseid.'.domain'}; my $cnum = $env{'course.'.$courseid.'.num'}; my $classlist = &get_classlist($cdom,$cnum); my $student_count=0; while (my ($student,$data) = each %$classlist) { - my ($section,$status) = ($data->[&CL_SECTION()], - $data->[&CL_STATUS()]); + my ($section,$start,$end) = ($data->[&CL_SECTION()], + $data->[&CL_START()], + $data->[&CL_END()]); if ($section eq '' || $section =~ /^\s*$/) { $section = 'none'; } - $request .= "('".$student."','".$section."','".$status."'),"; + if (!defined($start)) { $start = 0; } + if (!defined($end)) { $end = 0; } + $request .= "('".$student."','".$section."','".$start."','".$end."'),"; $student_count++; } return if ($student_count == 0); @@ -923,7 +923,7 @@ sub populate_student_table { $dbh->do($request); if ($dbh->err()) { &Apache::lonnet::logthis("error ".$dbh->errstr(). - " occured executing \n". + " occurred executing \n". $request); } return; @@ -965,9 +965,8 @@ sub populate_groupnames_table { my $dbh = &Apache::lonmysql::get_dbh(); my $cdom = $env{'course.'.$courseid.'.domain'}; my $cnum = $env{'course.'.$courseid.'.num'}; - my %curr_groups; - my $numgrps = &Apache::loncommon::coursegroups(\%curr_groups,$cdom,$cnum); - return if (!$numgrps); + my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum); + return if (!%curr_groups); my $request = 'INSERT IGNORE INTO '.$groupnames_table. '(groupname) VALUES '; foreach my $groupname (sort(keys(%curr_groups)),'none') { @@ -977,7 +976,7 @@ sub populate_groupnames_table { $dbh->do($request); if ($dbh->err()) { &Apache::lonnet::logthis("error ".$dbh->errstr(). - " occured executing \n". + " occurred executing \n". $request); } return; @@ -1025,8 +1024,9 @@ sub populate_students_groups_table { "(student_id,group_id) VALUES "; my $cdom = $env{'course.'.$courseid.'.domain'}; my $cnum = $env{'course.'.$courseid.'.num'}; - my $classlist = &get_classlist($cdom,$cnum); + my ($classlist,$keylist) = &get_classlist($cdom,$cnum); my ($classgroups,$studentgroups) = &get_group_memberships($classlist, + $keylist, $cdom,$cnum); my $record_count = 0; foreach my $student (sort(keys(%{$classgroups}))) { @@ -1046,7 +1046,7 @@ sub populate_students_groups_table { $dbh->do($request); if ($dbh->err()) { &Apache::lonnet::logthis("error ".$dbh->errstr(). - " occured executing \n". + " occurred executing \n". $request); } return; @@ -1105,7 +1105,7 @@ Input: $sname, $sdom, $courseid Output: $returnstatus -$returnstatus is a string describing any errors that occured. 'okay' is the +$returnstatus is a string describing any errors that occurred. 'okay' is the default. This subroutine loads a students data using lonnet::dump and inserts @@ -1119,7 +1119,7 @@ a description of the error. Once the "fulldump" tables are updated, the tables used for chart and 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 occurred successfully, $student_table is updated to reflect the time of the update. Notice we do not insert the data and immediately query it. This means it @@ -1137,7 +1137,6 @@ sub update_full_student_data { &setup_table_names($courseid); # my $student_id = &get_student_id($sname,$sdom); - my @group_ids = &get_students_groupids($student_id); my $student = $sname.':'.$sdom; # my $returnstatus = 'okay'; @@ -1169,6 +1168,8 @@ sub update_full_student_data { while (my ($key,$value) = each(%studentdata)) { next if ($key =~ /^(\d+):(resource$|subnum$|keys:)/); my ($transaction,$symb,$parameter) = split(':',$key); + $symb = &unescape($symb); + $parameter = &unescape($parameter); my $symb_id = &get_symb_id($symb); if ($parameter eq 'timestamp') { # We can deal with 'timestamp' right away @@ -1308,7 +1309,7 @@ sub update_full_student_data { chop($store_command); $dbh->do($store_command); if ($dbh->err) { - $returnstatus = 'error storing part data'; + $returnstatus = 'error saving part data'; &Apache::lonnet::logthis('insert error '.$dbh->errstr()); &Apache::lonnet::logthis("While attempting\n".$store_command); } @@ -1348,7 +1349,7 @@ sub update_full_student_data { chop($store_command); $dbh->do($store_command); if ($dbh->err) { - $returnstatus = 'error storing response data'; + $returnstatus = 'error saving response data'; &Apache::lonnet::logthis('insert error '.$dbh->errstr()); &Apache::lonnet::logthis("While attempting\n".$store_command); } @@ -1360,9 +1361,9 @@ sub update_full_student_data { ($sname,$sdom,$courseid, &Apache::lonnet::convert_dump_to_currentdump(\%studentdata)); if ($returnstatus eq 'okay' && $status ne 'okay') { - $returnstatus = 'error storing current data:'.$status; + $returnstatus = 'error saving current data:'.$status; } elsif ($status ne 'okay') { - $returnstatus .= ' error storing current data:'.$status; + $returnstatus .= ' error saving current data:'.$status; } ## ## Update the students time...... @@ -1390,7 +1391,7 @@ Input: $sname, $sdom, $courseid Output: $returnstatus, \%student_data -$returnstatus is a string describing any errors that occured. 'okay' is the +$returnstatus is a string describing any errors that occurred. 'okay' is the default. \%student_data is the data returned by a call to lonnet::currentdump. @@ -1420,25 +1421,23 @@ sub update_student_data { &setup_table_names($courseid); # my $student_id = &get_student_id($sname,$sdom); - my @group_ids = &get_students_groupids($student_id); my $student = $sname.':'.$sdom; # my $returnstatus = 'okay'; # # Download students data my $time_of_retrieval = time; - my @tmp = &Apache::lonnet::currentdump($courseid,$sdom,$sname); - if ((scalar(@tmp) > 0) && ($tmp[0] =~ /^error:/)) { + my %student_data = &Apache::lonnet::currentdump($courseid,$sdom,$sname); + if (&Apache::lonnet::error(%student_data)) { &Apache::lonnet::logthis('error getting data for '. $sname.':'.$sdom.' in course '.$courseid. - ':'.$tmp[0]); - $returnstatus = 'error getting data'; + ':'.(%student_data)[0]); + $returnstatus =(%student_data)[0] ; return ($returnstatus,undef); } - if (scalar(@tmp) < 1) { + if (scalar(keys(%student_data)) < 1) { return ('no data',undef); } - my %student_data = @tmp; my @Results = &store_student_data($sname,$sdom,$courseid,\%student_data); # # Set the students update time @@ -1472,7 +1471,6 @@ sub store_student_data { my ($sname,$sdom,$courseid,$student_data) = @_; # my $student_id = &get_student_id($sname,$sdom); - my @group_ids = &get_students_groupids($student_id); my $student = $sname.':'.$sdom; # my $returnstatus = 'okay'; @@ -1664,17 +1662,20 @@ sub ensure_current_data { # # Get the update time for the user my $updatetime = 0; + my $getuserdir = 1; my $modifiedtime = &Apache::lonnet::GetFileTimestamp - ($sdom,$sname,$courseid.'.db', - $Apache::lonnet::perlvar{'lonUsersDir'}); + ($sdom,$sname,$courseid.'.db',$getuserdir); # + if ($modifiedtime == -1) { + return ('no data',undef); + } + my $student_id = &get_student_id($sname,$sdom); - my @group_ids = &get_students_groupids($student_id); my @Result = &Apache::lonmysql::get_rows($student_table, "student_id ='$student_id'"); my $data = undef; if (@Result) { - $updatetime = $Result[0]->[5]; # Ack! This is dumb! + $updatetime = $Result[0]->[6]; # Ack! This is dumb! } if ($modifiedtime > $updatetime) { ($status,$data) = &update_student_data($sname,$sdom,$courseid); @@ -1713,17 +1714,16 @@ sub ensure_current_full_data { &ensure_tables_are_set_up($courseid); # # Get the update time for the user + my $getuserdir = 1; my $modifiedtime = &Apache::lonnet::GetFileTimestamp - ($sdom,$sname,$courseid.'.db', - $Apache::lonnet::perlvar{'lonUsersDir'}); + ($sdom,$sname,$courseid.'.db',$getuserdir); # my $student_id = &get_student_id($sname,$sdom); - my @group_ids = &get_students_groupids($student_id); my @Result = &Apache::lonmysql::get_rows($student_table, "student_id ='$student_id'"); my $updatetime; if (@Result && ref($Result[0]) eq 'ARRAY') { - $updatetime = $Result[0]->[6]; + $updatetime = $Result[0]->[7]; } if (! defined($updatetime) || $modifiedtime > $updatetime) { $status = &update_full_student_data($sname,$sdom,$courseid); @@ -1913,7 +1913,7 @@ sub get_current_state { } else { if ($status ne 'okay' && $status ne '') { &Apache::lonnet::logthis('status = '.$status); - return (); + return ('error: '.$status,undef); } my $returnhash = &get_student_data_from_performance_cache($sname,$sdom, $symb,$courseid); @@ -1998,7 +1998,7 @@ sub get_problem_statistics { &setup_table_names($courseid); my $symb_id = &get_symb_id($symb); my $part_id = &get_part_id($part); - my $stats_table = $courseid.'_problem_stats'; + my $stats_table = &temp_table_name($courseid,'problem_stats'); # my $dbh = &Apache::lonmysql::get_dbh(); return undef if (! defined($dbh)); @@ -2022,14 +2022,8 @@ sub get_problem_statistics { $request .= ' WHERE a.symb_id='.$symb_id.' AND a.part_id='.$part_id; # # Limit the students included to those specified - if (defined($Sections) && lc($Sections->[0]) ne 'all') { - $request .= ' AND ('. - join(' OR ', map { "b.section='".$_."'" } @$Sections - ).')'; - } - if (defined($status) && lc($status) ne 'any') { - $request .= " AND b.status='".$status."'"; - } + my ($section_limits,$enrollment_limits)= + &limit_by_section_and_status($Sections,$status,'b'); # # Limit by starttime and endtime my $time_requirements = undef; @@ -2044,6 +2038,12 @@ sub get_problem_statistics { if (defined($time_requirements)) { $request .= ' AND '.$time_requirements; } + if (defined($section_limits)) { + $request .= ' AND '.$section_limits; + } + if (defined($enrollment_limits)) { + $request .= ' AND '.$enrollment_limits; + } # Limit by group, as required if (defined($group_limits)) { $request .= ' AND '.$group_limits; @@ -2202,7 +2202,7 @@ sub populate_weight_table { $dbh->do($request); if ($dbh->err()) { &Apache::lonnet::logthis("error ".$dbh->errstr(). - " occured executing \n". + " occurred executing \n". $request); } return; @@ -2270,7 +2270,20 @@ sub limit_by_section_and_status { } my $enrollment_requirements=undef; if (defined($enrollment) && $enrollment ne 'Any') { - $enrollment_requirements = $tablename.".status='".$enrollment."'"; + my $now = time(); + if ( $enrollment eq 'Future' ) { + $enrollment_requirements = + "( $tablename.start > $now AND ". + "( $tablename.end = 0 OR $tablename.end > $now))"; + } elsif ( $enrollment eq 'Active' ) { + $enrollment_requirements = + "(( $tablename.start = 0 OR $tablename.start < $now ) AND ". + " ( $tablename.end = 0 OR $tablename.end > $now ))"; + } elsif ( $enrollment eq 'Expired' ) { + $enrollment_requirements = + "(( $tablename.start < $now ) AND ". + " ( $tablename.end < $now ))"; + } } return ($student_requirements,$enrollment_requirements); } @@ -2498,7 +2511,7 @@ sub score_stats { my $time_limits = &limit_by_start_end_time($starttime,$endtime,'a'); my @Symbids = map { &get_symb_id($_); } @{$symbs}; # - my $stats_table = $courseid.'_problem_stats'; + my $stats_table = &temp_table_name($courseid,'problem_stats'); my $symb_restriction = join(' OR ',map {'a.symb_id='.$_;} @Symbids); my $request = 'DROP TABLE '.$stats_table; $dbh->do($request); @@ -2577,7 +2590,7 @@ sub count_stats { my $time_limits = &limit_by_start_end_time($starttime,$endtime,'a'); my @Symbids = map { &get_symb_id($_); } @{$symbs}; # - my $stats_table = $courseid.'_problem_stats'; + my $stats_table = &temp_table_name($courseid,'problem_stats'); my $symb_restriction = join(' OR ',map {'a.symb_id='.$_;} @Symbids); my $request = 'DROP TABLE '.$stats_table; $dbh->do($request); @@ -2726,7 +2739,7 @@ sub get_response_data { if (ref($dataset) eq 'ARRAY' && scalar(@$dataset)>0) { # Clear the \'s from around the submission for (my $i =0;$i[$i]->[3] =~ s/(\'$|^\')//g; + $dataset->[$i]->[&RD_submission()] =~ s/(\'$|^\')//g; } return $dataset; } @@ -2754,7 +2767,6 @@ sub get_response_data_by_student { # my $student_id = &get_student_id($student->{'username'}, $student->{'domain'}); - my @group_ids = &get_students_groupids($student_id); # my $dbh = &Apache::lonmysql::get_dbh(); return undef if (! defined($dbh)); @@ -2788,7 +2800,7 @@ sub get_response_data_by_student { if (ref($dataset) eq 'ARRAY' && scalar(@$dataset)>0) { # Clear the \'s from around the submission for (my $i =0;$i[$i]->[2] =~ s/(\'$|^\')//g; + $dataset->[$i]->[&RDs_submission] =~ s/(\'$|^\')//g; } return $dataset; } @@ -2876,7 +2888,7 @@ sub get_student_scores { &setup_table_names($courseid); my $dbh = &Apache::lonmysql::get_dbh(); return (undef) if (! defined($dbh)); - my $tmptable = $courseid.'_temp_'.time; + my $tmptable = &temp_table_name($courseid,'temp_'.time); my $request = 'DROP TABLE IF EXISTS '.$tmptable; # &Apache::lonnet::logthis('request = '.$/.$request); $dbh->do($request); @@ -2988,7 +3000,7 @@ sub setup_table_names { } # # Set up database names - my $base_id = $courseid; + my $base_id = 'md5_'.&Digest::MD5::md5_hex($courseid); $symb_table = $base_id.'_'.'symb'; $part_table = $base_id.'_'.'part'; $student_table = $base_id.'_'.'student'; @@ -3017,6 +3029,12 @@ sub setup_table_names { return; } +sub temp_table_name { + my ($courseid,$affix) = @_; + my $base_id = 'md5_'.&Digest::MD5::md5_hex($courseid); + return $base_id.'_'.$affix; +} + ################################################ ################################################ @@ -3074,6 +3092,12 @@ sub CL_FULLNAME { return 6; } sub CL_STATUS { return 7; } sub CL_TYPE { return 8; } sub CL_LOCKEDTYPE { return 9; } +sub CL_GROUP { return 10; } +sub CL_PERMANENTEMAIL { return 11; } +sub CL_ROLE { return 12; } +sub CL_EXTENT { return 13; } +sub CL_PHOTO { return 14; } +sub CL_THUMBNAIL { return 15; } sub get_classlist { my ($cdom,$cnum) = @_; @@ -3130,6 +3154,9 @@ sub get_classlist { if(((!$end) || $now < $end) && ((!$start) || ($now > $start))) { $status='Active'; } + if(($now < $start) && ((!$end) || $now < $end )) { + $status='Future'; + } $classlist{$student} = [$sdom,$sname,$end,$start,$id,$section,$fullname,$status,$type,$lockedtype]; } @@ -3142,7 +3169,10 @@ sub get_classlist { } sub get_group_memberships { - my ($classlist,$cdom,$cnum) = @_; + my ($classlist,$keylist,$cdom,$cnum) = @_; + + return ({},{}) if (!ref($classlist) || !ref($keylist)); + my $cid = $cdom.'_'.$cnum; if (!defined($cdom) || !defined($cnum)) { $cid = $env{'request.course.id'}; @@ -3152,11 +3182,11 @@ sub get_group_memberships { my (%classgroups,%studentgroups); my $now = time; my $access_end = $env{'course.'.$cid.'.default_enrollment_end_date'}; - my (%curr_groups,%groupmemberhash); - my $numgroups = &Apache::loncommon::coursegroups(\%curr_groups,$cdom, - $cnum); - if ($numgroups) { - %groupmemberhash = &Apache::lonnet::get_group_membership($cdom,$cnum); + my %curr_groups =&Apache::longroup::coursegroups($cdom,$cnum); + if (%curr_groups) { + my $grpindex = &CL_GROUP(); + my %groupmemberhash = + &Apache::lonnet::get_group_membership($cdom,$cnum); foreach my $student (keys(%{$classlist})) { %{$classgroups{$student}} = (); my $hasgroup = 0; @@ -3196,6 +3226,9 @@ sub get_group_memberships { } if (!$hasgroup) { $studentgroups{'none'} ++; + } else { + $classlist->{$student}->[$grpindex] = join(',', + sort(keys(%{$classgroups{$student}{'active'}}))); } } }