File:
[LON-CAPA] /
loncom /
interface /
loncommon.pm
Revision
1.31:
download - view:
text,
annotated -
select for diffs
Mon Apr 15 23:37:37 2002 UTC (23 years, 1 month ago) by
albertel
Branches:
MAIN
CVS tags:
HEAD
- moved much of the CSV handling code into loncommon in preparation for grades.pm to accept uploads
- addressed BUG#71 (both original and reverse mode now work)
- did some cleanup and common code removal
# The LearningOnline Network with CAPA
# a pile of common routines
#
# $Id: loncommon.pm,v 1.31 2002/04/15 23:37:37 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#
# YEAR=2001
# 2/13-12/7 Guy Albertelli
# 12/11,12/12,12/17 Scott Harrison
# 12/21 Gerd Kortemeyer
# 12/21 Scott Harrison
# 12/25,12/28 Gerd Kortemeyer
# YEAR=2002
# 1/4 Gerd Kortemeyer
# Makes a table out of the previous attempts
# Inputs result_from_symbread, user, domain, course_id
# Reads in non-network-related .tab files
package Apache::loncommon;
use strict;
use Apache::lonnet();
use POSIX qw(strftime);
use Apache::Constants qw(:common);
use Apache::lonmsg();
my $readit;
# ----------------------------------------------- Filetypes/Languages/Copyright
my %language;
my %cprtag;
my %fe; my %fd;
my %fc;
# -------------------------------------------------------------- Thesaurus data
my @therelated;
my @theword;
my @thecount;
my %theindex;
my $thetotalcount;
my $thefuzzy=2;
my $thethreshold=0.1/$thefuzzy;
my $theavecount;
# ----------------------------------------------------------------------- BEGIN
BEGIN {
unless ($readit) {
# ------------------------------------------------------------------- languages
{
my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
'/language.tab');
if ($fh) {
while (<$fh>) {
next if /^\#/;
chomp;
my ($key,$val)=(split(/\s+/,$_,2));
$language{$key}=$val;
}
}
}
# ------------------------------------------------------------------ copyrights
{
my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonIncludes'}.
'/copyright.tab');
if ($fh) {
while (<$fh>) {
next if /^\#/;
chomp;
my ($key,$val)=(split(/\s+/,$_,2));
$cprtag{$key}=$val;
}
}
}
# ------------------------------------------------------------- file categories
{
my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
'/filecategories.tab');
if ($fh) {
while (<$fh>) {
next if /^\#/;
chomp;
my ($key,$val)=(split(/\s+/,$_,2));
push @{$fc{$key}},$val;
}
}
}
# ------------------------------------------------------------------ file types
{
my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
'/filetypes.tab');
if ($fh) {
while (<$fh>) {
next if (/^\#/);
chomp;
my ($ending,$emb,$descr)=split(/\s+/,$_,3);
if ($descr ne '') {
$fe{$ending}=lc($emb);
$fd{$ending}=$descr;
}
}
}
}
# -------------------------------------------------------------- Thesaurus data
{
my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.
'/thesaurus.dat');
if ($fh) {
while (<$fh>) {
my ($tword,$tindex,$tcount,$trelated)=split(/\@/,$_);
$theindex{$tword}=$tindex;
$theword[$tindex]=$tword;
$thecount[$tindex]=$tcount;
$thetotalcount+=$tcount;
$therelated[$tindex]=$trelated;
}
}
$theavecount=$thetotalcount/$#thecount;
}
&Apache::lonnet::logthis(
"<font color=yellow>INFO: Read file types and thesaurus</font>");
$readit=1;
}
}
# ============================================================= END BEGIN BLOCK
# ---------------------------------------------------------- Is this a keyword?
sub keyword {
my $newword=shift;
$newword=~s/\W//g;
$newword=~tr/A-Z/a-z/;
my $tindex=$theindex{$newword};
if ($tindex) {
if ($thecount[$tindex]>$theavecount) {
return 1;
}
}
return 0;
}
# -------------------------------------------------------- Return related words
sub related {
my $newword=shift;
$newword=~s/\W//g;
$newword=~tr/A-Z/a-z/;
my $tindex=$theindex{$newword};
if ($tindex) {
my %found=();
foreach (split(/\,/,$therelated[$tindex])) {
# - Related word found
my ($ridx,$rcount)=split(/\:/,$_);
# - Direct relation index
my $directrel=$rcount/$thecount[$tindex];
if ($directrel>$thethreshold) {
foreach (split(/\,/,$therelated[$ridx])) {
my ($rridx,$rrcount)=split(/\:/,$_);
if ($rridx==$tindex) {
# - Determine reverse relation index
my $revrel=$rrcount/$thecount[$ridx];
# - Calculate full index
$found{$ridx}=$directrel*$revrel;
if ($found{$ridx}>$thethreshold) {
foreach (split(/\,/,$therelated[$ridx])) {
my ($rrridx,$rrrcount)=split(/\:/,$_);
unless ($found{$rrridx}) {
my $revrevrel=$rrrcount/$thecount[$ridx];
if (
$directrel*$revrel*$revrevrel>$thethreshold
) {
$found{$rrridx}=
$directrel*$revrel*$revrevrel;
}
}
}
}
}
}
}
}
}
return ();
}
# ---------------------------------------------------------------- Language IDs
sub languageids {
return sort(keys(%language));
}
# -------------------------------------------------------- Language Description
sub languagedescription {
return $language{shift(@_)};
}
# --------------------------------------------------------------- Copyright IDs
sub copyrightids {
return sort(keys(%cprtag));
}
# ------------------------------------------------------- Copyright Description
sub copyrightdescription {
return $cprtag{shift(@_)};
}
# ------------------------------------------------------------- File Categories
sub filecategories {
return sort(keys(%fc));
}
# -------------------------------------- File Types within a specified category
sub filecategorytypes {
return @{$fc{lc(shift(@_))}};
}
# ------------------------------------------------------------------ File Types
sub fileextensions {
return sort(keys(%fe));
}
# ------------------------------------------------------------- Embedding Style
sub fileembstyle {
return $fe{lc(shift(@_))};
}
# ------------------------------------------------------------ Description Text
sub filedescription {
return $fd{lc(shift(@_))};
}
# ------------------------------------------------------------ Description Text
sub filedescriptionex {
my $ex=shift;
return '.'.$ex.' '.$fd{lc($ex)};
}
sub get_previous_attempt {
my ($symb,$username,$domain,$course)=@_;
my $prevattempts='';
if ($symb) {
my (%returnhash)=
&Apache::lonnet::restore($symb,$course,$domain,$username);
if ($returnhash{'version'}) {
my %lasthash=();
my $version;
for ($version=1;$version<=$returnhash{'version'};$version++) {
foreach (sort(split(/\:/,$returnhash{$version.':keys'}))) {
$lasthash{$_}=$returnhash{$version.':'.$_};
}
}
$prevattempts='<table border=2></tr><th>History</th>';
foreach (sort(keys %lasthash)) {
my ($ign,@parts) = split(/\./,$_);
if (@parts) {
my $data=$parts[-1];
pop(@parts);
$prevattempts.='<th>Part '.join('.',@parts).'<br />'.$data.'</th>';
} else {
$prevattempts.='<th>'.$ign.'</th>';
}
}
for ($version=1;$version<=$returnhash{'version'};$version++) {
$prevattempts.='</tr><tr><th>Attempt '.$version.'</th>';
foreach (sort(keys %lasthash)) {
my $value;
if ($_ =~ /timestamp/) {
$value=scalar(localtime($returnhash{$version.':'.$_}));
} else {
$value=$returnhash{$version.':'.$_};
}
$prevattempts.='<td>'.$value.'</td>';
}
}
$prevattempts.='</tr><tr><th>Current</th>';
foreach (sort(keys %lasthash)) {
my $value;
if ($_ =~ /timestamp/) {
$value=scalar(localtime($lasthash{$_}));
} else {
$value=$lasthash{$_};
}
$prevattempts.='<td>'.$value.'</td>';
}
$prevattempts.='</tr></table>';
} else {
$prevattempts='Nothing submitted - no attempts.';
}
} else {
$prevattempts='No data.';
}
}
sub get_student_view {
my ($symb,$username,$domain,$courseid) = @_;
my ($map,$id,$feedurl) = split(/___/,$symb);
my (%old,%moreenv);
my @elements=('symb','courseid','domain','username');
foreach my $element (@elements) {
$old{$element}=$ENV{'form.grade_'.$element};
$moreenv{'form.grade_'.$element}=eval '$'.$element #'
}
&Apache::lonnet::appenv(%moreenv);
my $userview=&Apache::lonnet::ssi('/res/'.$feedurl);
&Apache::lonnet::delenv('form.grade_');
foreach my $element (@elements) {
$ENV{'form.grade_'.$element}=$old{$element};
}
$userview=~s/\<body[^\>]*\>//gi;
$userview=~s/\<\/body\>//gi;
$userview=~s/\<html\>//gi;
$userview=~s/\<\/html\>//gi;
$userview=~s/\<head\>//gi;
$userview=~s/\<\/head\>//gi;
$userview=~s/action\s*\=/would_be_action\=/gi;
return $userview;
}
sub get_student_answers {
my ($symb,$username,$domain,$courseid) = @_;
my ($map,$id,$feedurl) = split(/___/,$symb);
my (%old,%moreenv);
my @elements=('symb','courseid','domain','username');
foreach my $element (@elements) {
$old{$element}=$ENV{'form.grade_'.$element};
$moreenv{'form.grade_'.$element}=eval '$'.$element #'
}
$moreenv{'form.grade_target'}='answer';
&Apache::lonnet::appenv(%moreenv);
my $userview=&Apache::lonnet::ssi('/res/'.$feedurl);
&Apache::lonnet::delenv('form.grade_');
foreach my $element (@elements) {
$ENV{'form.grade_'.$element}=$old{$element};
}
$userview=~s/\<body[^\>]*\>//gi;
$userview=~s/\<\/body\>//gi;
$userview=~s/\<html\>//gi;
$userview=~s/\<\/html\>//gi;
$userview=~s/\<head\>//gi;
$userview=~s/\<\/head\>//gi;
$userview=~s/action\s*\=/would_be_action\=/gi;
return $userview;
}
sub get_unprocessed_cgi {
my ($query,$possible_names)= @_;
# $Apache::lonxml::debug=1;
foreach (split(/&/,$query)) {
my ($name, $value) = split(/=/,$_);
$name = &Apache::lonnet::unescape($name);
if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
&Apache::lonxml::debug("Seting :$name: to :$value:");
unless (defined($ENV{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
}
}
}
sub cacheheader {
unless ($ENV{'request.method'} eq 'GET') { return ''; }
my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
<meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
<meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
return $output;
}
sub no_cache {
my ($r) = @_;
unless ($ENV{'request.method'} eq 'GET') { return ''; }
#my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
$r->no_cache(1);
$r->header_out("Pragma" => "no-cache");
#$r->header_out("Expires" => $date);
}
sub add_to_env {
my ($name,$value)=@_;
if (defined($ENV{$name})) {
if (ref($ENV{$name})) {
#already have multiple values
push(@{ $ENV{$name} },$value);
} else {
#first time seeing multiple values, convert hash entry to an arrayref
my $first=$ENV{$name};
undef($ENV{$name});
push(@{ $ENV{$name} },$first,$value);
}
} else {
$ENV{$name}=$value;
}
}
#---CSV Upload/Handling functions
# ========================================================= Store uploaded file
# needs $ENV{'form.upfile'}
# return $datatoken to be put into hidden field
sub upfile_store {
my $r=shift;
$ENV{'form.upfile'}=~s/\r/\n/gs;
$ENV{'form.upfile'}=~s/\f/\n/gs;
$ENV{'form.upfile'}=~s/\n+/\n/gs;
$ENV{'form.upfile'}=~s/\n+$//gs;
my $datatoken=$ENV{'user.name'}.'_'.$ENV{'user.domain'}.
'_enroll_'.$ENV{'request.course.id'}.'_'.time.'_'.$$;
{
my $fh=Apache::File->new('>'.$r->dir_config('lonDaemons').
'/tmp/'.$datatoken.'.tmp');
print $fh $ENV{'form.upfile'};
}
return $datatoken;
}
# ================================================= Load uploaded file from tmp
# needs $ENV{'form.datatoken'}
# sets $ENV{'form.upfile'} to the contents of the file
sub load_tmp_file {
my $r=shift;
my @studentdata=();
{
my $fh;
if ($fh=Apache::File->new($r->dir_config('lonDaemons').
'/tmp/'.$ENV{'form.datatoken'}.'.tmp')) {
@studentdata=<$fh>;
}
}
$ENV{'form.upfile'}=join('',@studentdata);
}
# ========================================= Separate uploaded file into records
# returns array of records
# needs $ENV{'form.upfile'}
# needs $ENV{'form.upfiletype'}
sub upfile_record_sep {
if ($ENV{'form.upfiletype'} eq 'xml') {
} else {
return split(/\n/,$ENV{'form.upfile'});
}
}
# =============================================== Separate a record into fields
# needs $ENV{'form.upfiletype'}
# takes $record as arg
sub record_sep {
my $record=shift;
my %components=();
if ($ENV{'form.upfiletype'} eq 'xml') {
} elsif ($ENV{'form.upfiletype'} eq 'space') {
my $i=0;
foreach (split(/\s+/,$record)) {
my $field=$_;
$field=~s/^(\"|\')//;
$field=~s/(\"|\')$//;
$components{$i}=$field;
$i++;
}
} elsif ($ENV{'form.upfiletype'} eq 'tab') {
my $i=0;
foreach (split(/\t+/,$record)) {
my $field=$_;
$field=~s/^(\"|\')//;
$field=~s/(\"|\')$//;
$components{$i}=$field;
$i++;
}
} else {
my @allfields=split(/\,/,$record);
my $i=0;
my $j;
for ($j=0;$j<=$#allfields;$j++) {
my $field=$allfields[$j];
if ($field=~/^\s*(\"|\')/) {
my $delimiter=$1;
while (($field!~/$delimiter$/) && ($j<$#allfields)) {
$j++;
$field.=','.$allfields[$j];
}
$field=~s/^\s*$delimiter//;
$field=~s/$delimiter\s*$//;
}
$components{$i}=$field;
$i++;
}
}
return %components;
}
# =============================== HTML code to select file and specify its type
sub upfile_select_html {
return (<<'ENDUPFORM');
<input type="file" name="upfile" size="50">
<br />Type: <select name="upfiletype">
<option value="csv">CSV (comma separated values, spreadsheet)</option>
<option value="space">Space separated</option>
<option value="tab">Tabulator separated</option>
<option value="xml">HTML/XML</option>
</select>
ENDUPFORM
}
# ===================Prints a table of sample values from each column uploaded
# $r is an Apache Request ref
# $records is an arrayref from &Apache::loncommon::upfile_record_sep
sub csv_print_samples {
my ($r,$records) = @_;
my (%sone,%stwo,%sthree);
%sone=&record_sep($$records[0]);
if (defined($$records[1])) {%stwo=&record_sep($$records[1]);}
if (defined($$records[2])) {%sthree=&record_sep($$records[2]);}
$r->print('Samples<br /><table border="2"><tr>');
foreach (sort({$a <=> $b} keys(%sone))) { $r->print('<th>Column '.($_+1).'</th>'); }
$r->print('</tr>');
foreach my $hash (\%sone,\%stwo,\%sthree) {
$r->print('<tr>');
foreach (sort({$a <=> $b} keys(%sone))) {
$r->print('<td>');
if (defined($$hash{$_})) { $r->print($$hash{$_}); }
$r->print('</td>');
}
$r->print('</tr>');
}
$r->print('</tr></table><br />'."\n");
}
# ======Prints a table to create associations between values and table columns
# $r is an Apache Request ref
# $records is an arrayref from &Apache::loncommon::upfile_record_sep
# $d is an array of 2 element arrays (internal name, displayed name)
sub csv_print_select_table {
my ($r,$records,$d) = @_;
my $i=0;my %sone;
%sone=&record_sep($$records[0]);
$r->print('Associate columns with student attributes.'."\n".
'<table border="2"><tr><th>Attribute</th><th>Column</th></tr>'."\n");
foreach (@$d) {
my ($value,$display)=@{ $_ };
$r->print('<tr><td>'.$display.'</td>');
$r->print('<td><select name=f'.$i.
' onChange="flip(this.form,'.$i.');">');
$r->print('<option value="none"></option>');
foreach (sort({$a <=> $b} keys(%sone))) {
$r->print('<option value="'.$_.'">Column '.($_+1).'</option>');
}
$r->print('</select></td></tr>'."\n");
$i++;
}
$i--;
return $i;
}
# ===================Prints a table of sample values from the upload and
# can make associate samples to internal names
# $r is an Apache Request ref
# $records is an arrayref from &Apache::loncommon::upfile_record_sep
# $d is an array of 2 element arrays (internal name, displayed name)
sub csv_samples_select_table {
my ($r,$records,$d) = @_;
my %sone; my %stwo; my %sthree;
my $i=0;
$r->print('<table border=2><tr><th>Field</th><th>Samples</th></tr>');
%sone=&record_sep($$records[0]);
if (defined($$records[1])) {%stwo=&record_sep($$records[1]);}
if (defined($$records[2])) {%sthree=&record_sep($$records[2]);}
foreach (sort keys %sone) {
$r->print('<tr><td><select name=f'.$i.
' onChange="flip(this.form,'.$i.');">');
foreach (@$d) {
my ($value,$display)=@{ $_ };
$r->print('<option value='.$value.'>'.$display.'</option>');
}
$r->print('</select></td><td>');
if (defined($sone{$_})) { $r->print($sone{$_}."</br>\n"); }
if (defined($stwo{$_})) { $r->print($stwo{$_}."</br>\n"); }
if (defined($sthree{$_})) { $r->print($sthree{$_}."</br>\n"); }
$r->print('</td></tr>');
$i++;
}
$i--;
return($i);
}
1;
__END__;
=head1 NAME
Apache::loncommon - pile of common routines
=head1 SYNOPSIS
Referenced by other mod_perl Apache modules.
Invocation:
&Apache::loncommon::SUBROUTINENAME(ARGUMENTS);
=head1 INTRODUCTION
Common collection of used subroutines. This collection helps remove
redundancy from other modules and increase efficiency of memory usage.
Current things done:
Makes a table out of the previous homework attempts
Inputs result_from_symbread, user, domain, course_id
Reads in non-network-related .tab files
This is part of the LearningOnline Network with CAPA project
described at http://www.lon-capa.org.
=head1 HANDLER SUBROUTINE
There is no handler subroutine.
=head1 OTHER SUBROUTINES
=over 4
=item *
BEGIN() : initialize values from language.tab, copyright.tab, filetypes.tab,
and filecategories.tab.
=item *
languageids() : returns list of all language ids
=item *
languagedescription() : returns description of a specified language id
=item *
copyrightids() : returns list of all copyrights
=item *
copyrightdescription() : returns description of a specified copyright id
=item *
filecategories() : returns list of all file categories
=item *
filecategorytypes() : returns list of file types belonging to a given file
category
=item *
fileembstyle() : returns embedding style for a specified file type
=item *
filedescription() : returns description for a specified file type
=item *
filedescriptionex() : returns description for a specified file type with
extra formatting
=item *
get_previous_attempt() : return string with previous attempt on problem
=item *
get_student_view() : show a snapshot of what student was looking at
=item *
get_student_answers() : show a snapshot of how student was answering problem
=item *
get_unprocessed_cgi() : get unparsed CGI parameters
=item *
cacheheader() : returns cache-controlling header code
=item *
nocache() : specifies header code to not have cache
=item *
add_to_env($name,$value) : adds $name to the %ENV hash with value
$value, if $name already exists, the entry is converted to an array
reference and $value is added to the array.
=back
=cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>