Diff for /loncom/interface/loncoursedata.pm between versions 1.171 and 1.185

version 1.171, 2006/05/18 01:08:51 version 1.185, 2008/01/05 18:36:26
Line 38  Set of functions that download and proce Line 38  Set of functions that download and proce
   
 =head1 PACKAGES USED  =head1 PACKAGES USED
   
  Apache::Constants qw(:common :http)    Apache::lonnet
  Apache::lonnet()    Apache::longroup
  Apache::lonhtmlcommon    Time::HiRes
  HTML::TokeParser    Apache::lonmysql
  GDBM_File    LONCAPA
     Digest::MD5
    
 =cut  =cut
   
 package Apache::loncoursedata;  package Apache::loncoursedata;
   
 use strict;  use strict;
 use Apache::lonnet;  use Apache::lonnet;
 use Apache::lonhtmlcommon;  use Apache::longroup();
 use Apache::longroup;  use Time::HiRes();
 use Time::HiRes;  use Apache::lonmysql();
 use Apache::lonmysql;  use LONCAPA;
 use HTML::TokeParser;  use Digest::MD5();
 use GDBM_File;  
   
 =pod  =pod
   
Line 75  and/or itself. Line 75  and/or itself.
   
 Returns a reference to a hash as described by $values.  $values is  Returns a reference to a hash as described by $values.  $values is
 assumed to be the result of   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.  This is a helper function for get_current_state.
   
Line 85  This is a helper function for get_curren Line 85  This is a helper function for get_curren
 ################################################  ################################################
 sub make_into_hash {  sub make_into_hash {
     my $values = shift;      my $values = shift;
     my %tmp = map { &Apache::lonnet::unescape($_); }      my %tmp = map { &unescape($_); } split(':',$values);
                                            split(':',$values);  
     return \%tmp;      return \%tmp;
 }  }
   
Line 368  sub init_dbs { Line 367  sub init_dbs {
                     { name => 'section',                      { name => 'section',
                       type => 'VARCHAR(100) BINARY',                        type => 'VARCHAR(100) BINARY',
                       restrictions => 'NOT NULL'},                        restrictions => 'NOT NULL'},
                     { name => 'status',                      { name => 'start',
                       type => 'VARCHAR(15) BINARY',                        type => 'INT',
                         restrictions => 'NOT NULL'},
                       { name => 'end',
                         type => 'INT',
                       restrictions => 'NOT NULL'},                        restrictions => 'NOT NULL'},
                     { name => 'classification',                      { name => 'classification',
                       type => 'VARCHAR(100) BINARY', },                        type => 'VARCHAR(100) BINARY', },
Line 381  sub init_dbs { Line 383  sub init_dbs {
         'PRIMARY KEY' => ['student_id'],          'PRIMARY KEY' => ['student_id'],
         'KEY' => [{ columns => ['student (100)',          'KEY' => [{ columns => ['student (100)',
                                 'section (100)',                                  'section (100)',
                                 'status (15)',]},],                                  'start',
    'end']},],
     };      };
     #      #
     my $groupnames_table_def = {      my $groupnames_table_def = {
Line 905  sub populate_student_table { Line 908  sub populate_student_table {
     &init_dbs($courseid,0);      &init_dbs($courseid,0);
     my $dbh = &Apache::lonmysql::get_dbh();      my $dbh = &Apache::lonmysql::get_dbh();
     my $request = 'INSERT IGNORE INTO '.$student_table.      my $request = 'INSERT IGNORE INTO '.$student_table.
         "(student,section,status) VALUES ";          "(student,section,start,end) VALUES ";
     my $cdom = $env{'course.'.$courseid.'.domain'};      my $cdom = $env{'course.'.$courseid.'.domain'};
     my $cnum = $env{'course.'.$courseid.'.num'};      my $cnum = $env{'course.'.$courseid.'.num'};
     my $classlist = &get_classlist($cdom,$cnum);      my $classlist = &get_classlist($cdom,$cnum);
     my $student_count=0;      my $student_count=0;
     while (my ($student,$data) = each %$classlist) {      while (my ($student,$data) = each %$classlist) {
         my ($section,$status) = ($data->[&CL_SECTION()],          my ($section,$start,$end) = ($data->[&CL_SECTION()],
                                  $data->[&CL_STATUS()]);       $data->[&CL_START()],
        $data->[&CL_END()]);
         if ($section eq '' || $section =~ /^\s*$/) {          if ($section eq '' || $section =~ /^\s*$/) {
             $section = 'none';              $section = 'none';
         }          }
         $request .= "('".$student."','".$section."','".$status."'),";   if (!defined($start)) { $start = 0; }
    if (!defined($end))   { $end   = 0; }
           $request .= "('".$student."','".$section."','".$start."','".$end."'),";
         $student_count++;          $student_count++;
     }      }
     return if ($student_count == 0);      return if ($student_count == 0);
Line 1138  sub update_full_student_data { Line 1144  sub update_full_student_data {
     &setup_table_names($courseid);      &setup_table_names($courseid);
     #      #
     my $student_id = &get_student_id($sname,$sdom);      my $student_id = &get_student_id($sname,$sdom);
     my @group_ids = &get_students_groupids($student_id);  
     my $student = $sname.':'.$sdom;      my $student = $sname.':'.$sdom;
     #      #
     my $returnstatus = 'okay';      my $returnstatus = 'okay';
Line 1170  sub update_full_student_data { Line 1175  sub update_full_student_data {
     while (my ($key,$value) = each(%studentdata)) {      while (my ($key,$value) = each(%studentdata)) {
         next if ($key =~ /^(\d+):(resource$|subnum$|keys:)/);          next if ($key =~ /^(\d+):(resource$|subnum$|keys:)/);
         my ($transaction,$symb,$parameter) = split(':',$key);          my ($transaction,$symb,$parameter) = split(':',$key);
    $symb = &unescape($symb);
    $parameter = &unescape($parameter);
         my $symb_id = &get_symb_id($symb);          my $symb_id = &get_symb_id($symb);
         if ($parameter eq 'timestamp') {          if ($parameter eq 'timestamp') {
             # We can deal with 'timestamp' right away              # We can deal with 'timestamp' right away
Line 1309  sub update_full_student_data { Line 1316  sub update_full_student_data {
         chop($store_command);          chop($store_command);
         $dbh->do($store_command);          $dbh->do($store_command);
         if ($dbh->err) {          if ($dbh->err) {
             $returnstatus = 'error storing part data';              $returnstatus = 'error saving part data';
             &Apache::lonnet::logthis('insert error '.$dbh->errstr());              &Apache::lonnet::logthis('insert error '.$dbh->errstr());
             &Apache::lonnet::logthis("While attempting\n".$store_command);              &Apache::lonnet::logthis("While attempting\n".$store_command);
         }          }
Line 1349  sub update_full_student_data { Line 1356  sub update_full_student_data {
         chop($store_command);          chop($store_command);
         $dbh->do($store_command);          $dbh->do($store_command);
         if ($dbh->err) {          if ($dbh->err) {
             $returnstatus = 'error storing response data';              $returnstatus = 'error saving response data';
             &Apache::lonnet::logthis('insert error '.$dbh->errstr());              &Apache::lonnet::logthis('insert error '.$dbh->errstr());
             &Apache::lonnet::logthis("While attempting\n".$store_command);              &Apache::lonnet::logthis("While attempting\n".$store_command);
         }          }
Line 1361  sub update_full_student_data { Line 1368  sub update_full_student_data {
         ($sname,$sdom,$courseid,          ($sname,$sdom,$courseid,
          &Apache::lonnet::convert_dump_to_currentdump(\%studentdata));           &Apache::lonnet::convert_dump_to_currentdump(\%studentdata));
     if ($returnstatus eq 'okay' && $status ne 'okay') {      if ($returnstatus eq 'okay' && $status ne 'okay') {
         $returnstatus = 'error storing current data:'.$status;          $returnstatus = 'error saving current data:'.$status;
     } elsif ($status ne 'okay') {      } elsif ($status ne 'okay') {
         $returnstatus .= ' error storing current data:'.$status;          $returnstatus .= ' error saving current data:'.$status;
     }              }        
     ##      ##
     ## Update the students time......      ## Update the students time......
Line 1421  sub update_student_data { Line 1428  sub update_student_data {
     &setup_table_names($courseid);      &setup_table_names($courseid);
     #      #
     my $student_id = &get_student_id($sname,$sdom);      my $student_id = &get_student_id($sname,$sdom);
     my @group_ids = &get_students_groupids($student_id);  
     my $student = $sname.':'.$sdom;      my $student = $sname.':'.$sdom;
     #      #
     my $returnstatus = 'okay';      my $returnstatus = 'okay';
     #      #
     # Download students data      # Download students data
     my $time_of_retrieval = time;      my $time_of_retrieval = time;
     my @tmp = &Apache::lonnet::currentdump($courseid,$sdom,$sname);      my %student_data = &Apache::lonnet::currentdump($courseid,$sdom,$sname);
     if ((scalar(@tmp) > 0) && ($tmp[0] =~ /^error:/)) {      if (&Apache::lonnet::error(%student_data)) {
         &Apache::lonnet::logthis('error getting data for '.          &Apache::lonnet::logthis('error getting data for '.
                                  $sname.':'.$sdom.' in course '.$courseid.                                   $sname.':'.$sdom.' in course '.$courseid.
                                  ':'.$tmp[0]);                                   ':'.(%student_data)[0]);
         $returnstatus = 'error getting data';          $returnstatus =(%student_data)[0] ;
         return ($returnstatus,undef);          return ($returnstatus,undef);
     }      }
     if (scalar(@tmp) < 1) {      if (scalar(keys(%student_data)) < 1) {
         return ('no data',undef);          return ('no data',undef);
     }      }
     my %student_data = @tmp;  
     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
Line 1473  sub store_student_data { Line 1478  sub store_student_data {
     my ($sname,$sdom,$courseid,$student_data) = @_;      my ($sname,$sdom,$courseid,$student_data) = @_;
     #      #
     my $student_id = &get_student_id($sname,$sdom);      my $student_id = &get_student_id($sname,$sdom);
     my @group_ids = &get_students_groupids($student_id);  
     my $student = $sname.':'.$sdom;      my $student = $sname.':'.$sdom;
     #      #
     my $returnstatus = 'okay';      my $returnstatus = 'okay';
Line 1669  sub ensure_current_data { Line 1673  sub ensure_current_data {
         ($sdom,$sname,$courseid.'.db',          ($sdom,$sname,$courseid.'.db',
          $Apache::lonnet::perlvar{'lonUsersDir'});           $Apache::lonnet::perlvar{'lonUsersDir'});
     #      #
       if ($modifiedtime == -1) {
    return ('no data',undef);
       }
   
     my $student_id = &get_student_id($sname,$sdom);      my $student_id = &get_student_id($sname,$sdom);
     my @group_ids = &get_students_groupids($student_id);  
     my @Result = &Apache::lonmysql::get_rows($student_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]->[5];  # Ack!  This is dumb!          $updatetime = $Result[0]->[6];  # 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 1719  sub ensure_current_full_data { Line 1726  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 @group_ids = &get_students_groupids($student_id);  
     my @Result = &Apache::lonmysql::get_rows($student_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]->[6];          $updatetime = $Result[0]->[7];
     }      }
     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 1914  sub get_current_state { Line 1920  sub get_current_state {
     } else {      } else {
         if ($status ne 'okay' && $status ne '') {          if ($status ne 'okay' && $status ne '') {
             &Apache::lonnet::logthis('status = '.$status);              &Apache::lonnet::logthis('status = '.$status);
             return ();              return ('error: '.$status,undef);
         }          }
         my $returnhash = &get_student_data_from_performance_cache($sname,$sdom,          my $returnhash = &get_student_data_from_performance_cache($sname,$sdom,
                                                       $symb,$courseid);                                                        $symb,$courseid);
Line 1999  sub get_problem_statistics { Line 2005  sub get_problem_statistics {
     &setup_table_names($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 = &temp_table_name($courseid,'problem_stats');
     #      #
     my $dbh = &Apache::lonmysql::get_dbh();      my $dbh = &Apache::lonmysql::get_dbh();
     return undef if (! defined($dbh));      return undef if (! defined($dbh));
Line 2023  sub get_problem_statistics { Line 2029  sub get_problem_statistics {
     $request .= ' WHERE a.symb_id='.$symb_id.' AND a.part_id='.$part_id;      $request .= ' WHERE a.symb_id='.$symb_id.' AND a.part_id='.$part_id;
     #      #
     # Limit the students included to those specified      # Limit the students included to those specified
     if (defined($Sections) && lc($Sections->[0]) ne 'all') {      my ($section_limits,$enrollment_limits)=
         $request .= ' AND ('.          &limit_by_section_and_status($Sections,$status,'b');
             join(' OR ', map { "b.section='".$_."'" } @$Sections  
                  ).')';  
     }  
     if (defined($status) && lc($status) ne 'any') {  
         $request .= " AND b.status='".$status."'";  
     }  
     #      #
     # Limit by starttime and endtime      # Limit by starttime and endtime
     my $time_requirements = undef;      my $time_requirements = undef;
Line 2045  sub get_problem_statistics { Line 2045  sub get_problem_statistics {
     if (defined($time_requirements)) {      if (defined($time_requirements)) {
         $request .= ' AND '.$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      # Limit by group, as required
     if (defined($group_limits)) {      if (defined($group_limits)) {
         $request .= ' AND '.$group_limits;          $request .= ' AND '.$group_limits;
Line 2271  sub limit_by_section_and_status { Line 2277  sub limit_by_section_and_status {
     }      }
     my $enrollment_requirements=undef;      my $enrollment_requirements=undef;
     if (defined($enrollment) && $enrollment ne 'Any') {      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);      return ($student_requirements,$enrollment_requirements);
 }  }
Line 2499  sub score_stats { Line 2518  sub score_stats {
     my $time_limits = &limit_by_start_end_time($starttime,$endtime,'a');      my $time_limits = &limit_by_start_end_time($starttime,$endtime,'a');
     my @Symbids = map { &get_symb_id($_); } @{$symbs};      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 $symb_restriction = join(' OR ',map {'a.symb_id='.$_;} @Symbids);
     my $request = 'DROP TABLE '.$stats_table;      my $request = 'DROP TABLE '.$stats_table;
     $dbh->do($request);      $dbh->do($request);
Line 2578  sub count_stats { Line 2597  sub count_stats {
     my $time_limits = &limit_by_start_end_time($starttime,$endtime,'a');      my $time_limits = &limit_by_start_end_time($starttime,$endtime,'a');
     my @Symbids = map { &get_symb_id($_); } @{$symbs};      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 $symb_restriction = join(' OR ',map {'a.symb_id='.$_;} @Symbids);
     my $request = 'DROP TABLE '.$stats_table;      my $request = 'DROP TABLE '.$stats_table;
     $dbh->do($request);      $dbh->do($request);
Line 2727  sub get_response_data { Line 2746  sub get_response_data {
     if (ref($dataset) eq 'ARRAY' && scalar(@$dataset)>0) {      if (ref($dataset) eq 'ARRAY' && scalar(@$dataset)>0) {
         # Clear the \'s from around the submission          # Clear the \'s from around the submission
         for (my $i =0;$i<scalar(@$dataset);$i++) {          for (my $i =0;$i<scalar(@$dataset);$i++) {
             $dataset->[$i]->[3] =~ s/(\'$|^\')//g;              $dataset->[$i]->[&RD_submission()] =~ s/(\'$|^\')//g;
         }          }
         return $dataset;          return $dataset;
     }      }
Line 2755  sub get_response_data_by_student { Line 2774  sub get_response_data_by_student {
     #      #
     my $student_id = &get_student_id($student->{'username'},      my $student_id = &get_student_id($student->{'username'},
                                      $student->{'domain'});                                       $student->{'domain'});
     my @group_ids = &get_students_groupids($student_id);  
     #      #
     my $dbh = &Apache::lonmysql::get_dbh();      my $dbh = &Apache::lonmysql::get_dbh();
     return undef if (! defined($dbh));      return undef if (! defined($dbh));
Line 2789  sub get_response_data_by_student { Line 2807  sub get_response_data_by_student {
     if (ref($dataset) eq 'ARRAY' && scalar(@$dataset)>0) {      if (ref($dataset) eq 'ARRAY' && scalar(@$dataset)>0) {
         # Clear the \'s from around the submission          # Clear the \'s from around the submission
         for (my $i =0;$i<scalar(@$dataset);$i++) {          for (my $i =0;$i<scalar(@$dataset);$i++) {
             $dataset->[$i]->[2] =~ s/(\'$|^\')//g;              $dataset->[$i]->[&RDs_submission] =~ s/(\'$|^\')//g;
         }          }
         return $dataset;          return $dataset;
     }      }
Line 2877  sub get_student_scores { Line 2895  sub get_student_scores {
     &setup_table_names($courseid);      &setup_table_names($courseid);
     my $dbh = &Apache::lonmysql::get_dbh();      my $dbh = &Apache::lonmysql::get_dbh();
     return (undef) if (! defined($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;      my $request = 'DROP TABLE IF EXISTS '.$tmptable;
 #    &Apache::lonnet::logthis('request = '.$/.$request);  #    &Apache::lonnet::logthis('request = '.$/.$request);
     $dbh->do($request);      $dbh->do($request);
Line 2989  sub setup_table_names { Line 3007  sub setup_table_names {
     }      }
     #      #
     # Set up database names      # Set up database names
     my $base_id = $courseid;      my $base_id = 'md5_'.&Digest::MD5::md5_hex($courseid);
     $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';
Line 3018  sub setup_table_names { Line 3036  sub setup_table_names {
     return;      return;
 }  }
   
   sub temp_table_name {
       my ($courseid,$affix) = @_;
       my $base_id = 'md5_'.&Digest::MD5::md5_hex($courseid);
       return $base_id.'_'.$affix;
   }
   
 ################################################  ################################################
 ################################################  ################################################
   
Line 3075  sub CL_FULLNAME { return 6; } Line 3099  sub CL_FULLNAME { return 6; }
 sub CL_STATUS   { return 7; }  sub CL_STATUS   { return 7; }
 sub CL_TYPE     { return 8; }  sub CL_TYPE     { return 8; }
 sub CL_LOCKEDTYPE   { return 9; }  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 get_classlist {  sub get_classlist {
     my ($cdom,$cnum) = @_;      my ($cdom,$cnum) = @_;
Line 3131  sub get_classlist { Line 3160  sub get_classlist {
         if(((!$end) || $now < $end) && ((!$start) || ($now > $start))) {          if(((!$end) || $now < $end) && ((!$start) || ($now > $start))) {
             $status='Active';              $status='Active';
         }          }
           if(($now < $start) && ((!$end) || $now < $end )) {
               $status='Future';
           }
         $classlist{$student} =           $classlist{$student} = 
             [$sdom,$sname,$end,$start,$id,$section,$fullname,$status,$type,$lockedtype];              [$sdom,$sname,$end,$start,$id,$section,$fullname,$status,$type,$lockedtype];
     }      }
Line 3144  sub get_classlist { Line 3176  sub get_classlist {
   
 sub get_group_memberships {  sub get_group_memberships {
     my ($classlist,$keylist,$cdom,$cnum) = @_;      my ($classlist,$keylist,$cdom,$cnum) = @_;
   
       return ({},{}) if (!ref($classlist) || !ref($keylist));
   
     my $cid = $cdom.'_'.$cnum;      my $cid = $cdom.'_'.$cnum;
     if (!defined($cdom) || !defined($cnum)) {      if (!defined($cdom) || !defined($cnum)) {
         $cid =  $env{'request.course.id'};          $cid =  $env{'request.course.id'};
Line 3155  sub get_group_memberships { Line 3190  sub get_group_memberships {
     my $access_end = $env{'course.'.$cid.'.default_enrollment_end_date'};      my $access_end = $env{'course.'.$cid.'.default_enrollment_end_date'};
     my %curr_groups =&Apache::longroup::coursegroups($cdom,$cnum);      my %curr_groups =&Apache::longroup::coursegroups($cdom,$cnum);
     if (%curr_groups) {      if (%curr_groups) {
         my $grpindex = scalar(@{$keylist});          my $grpindex = &CL_GROUP();
         my %groupmemberhash =           my %groupmemberhash = 
     &Apache::lonnet::get_group_membership($cdom,$cnum);      &Apache::lonnet::get_group_membership($cdom,$cnum);
         foreach my $student (keys(%{$classlist})) {          foreach my $student (keys(%{$classlist})) {

Removed from v.1.171  
changed lines
  Added in v.1.185


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