version 1.14, 2006/09/26 15:15:31
|
version 1.22, 2007/06/15 23:02:09
|
Line 30 package LONCAPA::lonmetadata;
|
Line 30 package LONCAPA::lonmetadata;
|
|
|
use strict; |
use strict; |
use DBI; |
use DBI; |
|
use HTML::TokeParser; |
use vars qw($Metadata_Table_Description $Portfolio_metadata_table_description |
use vars qw($Metadata_Table_Description $Portfolio_metadata_table_description |
$Portfolio_access_table_description $Fulltext_indicies $Portfolio_metadata_indices $Portfolio_access_indices $Portfolio_addedfields_table_description $Portfolio_addedfields_indices); |
$Portfolio_access_table_description $Fulltext_indicies $Portfolio_metadata_indices $Portfolio_access_indices $Portfolio_addedfields_table_description $Portfolio_addedfields_indices); |
|
|
Line 183 $Portfolio_metadata_table_description =
|
Line 184 $Portfolio_metadata_table_description =
|
{ name => 'domain', type=>'TEXT'}, |
{ name => 'domain', type=>'TEXT'}, |
{ name => 'groupname', type=>'TEXT'}, |
{ name => 'groupname', type=>'TEXT'}, |
{ name => 'courserestricted', type=>'TEXT'}, |
{ name => 'courserestricted', type=>'TEXT'}, |
{ name => 'addedfieldnames', type=>'TEXT'}, |
|
{ name => 'addedfieldvalues', type=>'TEXT'}, |
|
#-------------------------------------------------- |
#-------------------------------------------------- |
{ name => 'dependencies', type=>'TEXT'}, |
{ name => 'dependencies', type=>'TEXT'}, |
{ name => 'modifyinguser', type=>'TEXT'}, |
{ name => 'modifyinguser', type=>'TEXT'}, |
Line 502 sub lookup_metadata {
|
Line 501 sub lookup_metadata {
|
$error = $sth->errstr; |
$error = $sth->errstr; |
} |
} |
} |
} |
} |
} |
return ($error,$returnvalue); |
return ($error,$returnvalue); |
} |
} |
|
|
Line 796 sub process_dynamic_metadata {
|
Line 795 sub process_dynamic_metadata {
|
# Get the statistical data - Use a weighted average |
# Get the statistical data - Use a weighted average |
foreach my $type (qw/avetries difficulty disc/) { |
foreach my $type (qw/avetries difficulty disc/) { |
my $studentcount; |
my $studentcount; |
|
my %course_counted; |
my $sum; |
my $sum; |
my @Values; |
my @Values; |
my @Students; |
my @Students; |
# |
# |
# Old data |
# New data |
foreach my $coursedata (values(%{$resdata->{'statistics'}}), |
|
values(%{$resdata->{'stats'}})) { |
|
if (ref($coursedata) eq 'HASH' && exists($coursedata->{$type})) { |
|
$studentcount += $coursedata->{'stdno'}; |
|
$sum += ($coursedata->{$type}*$coursedata->{'stdno'}); |
|
push(@Values,$coursedata->{$type}); |
|
push(@Students,$coursedata->{'stdno'}); |
|
} |
|
} |
|
if (exists($resdata->{'stats'})) { |
if (exists($resdata->{'stats'})) { |
foreach my $identifier (sort(keys(%{$resdata->{'stats'}}))) { |
foreach my $identifier (sort(keys(%{$resdata->{'stats'}}))) { |
my $coursedata = $resdata->{'stats'}->{$identifier}; |
my $coursedata = $resdata->{'stats'}->{$identifier}; |
|
next if (lc($coursedata->{$type}) eq 'nan'); |
|
$course_counted{$coursedata->{'course'}}++; |
$studentcount += $coursedata->{'stdno'}; |
$studentcount += $coursedata->{'stdno'}; |
$sum += $coursedata->{$type}*$coursedata->{'stdno'}; |
$sum += $coursedata->{$type}*$coursedata->{'stdno'}; |
push(@Values,$coursedata->{$type}); |
push(@Values,$coursedata->{$type}); |
Line 820 sub process_dynamic_metadata {
|
Line 813 sub process_dynamic_metadata {
|
} |
} |
} |
} |
# |
# |
# New data |
# Old data |
|
foreach my $course (keys(%{$resdata->{'statistics'}})) { |
|
next if (exists($course_counted{$course})); |
|
my $coursedata = $resdata->{'statistics'}{$course}; |
|
if (ref($coursedata) eq 'HASH' && exists($coursedata->{$type})) { |
|
next if (lc($coursedata->{$type}) eq 'nan'); |
|
$studentcount += $coursedata->{'stdno'}; |
|
$sum += ($coursedata->{$type}*$coursedata->{'stdno'}); |
|
push(@Values,$coursedata->{$type}); |
|
push(@Students,$coursedata->{'stdno'}); |
|
} |
|
} |
if (defined($studentcount) && $studentcount>0) { |
if (defined($studentcount) && $studentcount>0) { |
$data{$type} = $sum/$studentcount; |
$data{$type} = $sum/$studentcount; |
$data{$type.'_list'} = join(',',@Values); |
$data{$type.'_list'} = join(',',@Values); |
Line 829 sub process_dynamic_metadata {
|
Line 833 sub process_dynamic_metadata {
|
# |
# |
# Find out the number of students who have completed the resource... |
# Find out the number of students who have completed the resource... |
my $stdno; |
my $stdno; |
foreach my $coursedata (values(%{$resdata->{'statistics'}}), |
my %course_counted; |
values(%{$resdata->{'stats'}})) { |
|
if (ref($coursedata) eq 'HASH' && exists($coursedata->{'stdno'})) { |
|
$stdno += $coursedata->{'stdno'}; |
|
} |
|
} |
|
if (exists($resdata->{'stats'})) { |
if (exists($resdata->{'stats'})) { |
# |
# |
# For the number of students, take the maximum found for the class |
# For the number of students, take the maximum found for the class |
Line 847 sub process_dynamic_metadata {
|
Line 846 sub process_dynamic_metadata {
|
} |
} |
if ($current_course ne $coursedata->{'course'}) { |
if ($current_course ne $coursedata->{'course'}) { |
$stdno += $coursemax; |
$stdno += $coursemax; |
|
$course_counted{$coursedata->{'course'}}++; |
$coursemax = 0; |
$coursemax = 0; |
$current_course = $coursedata->{'course'}; |
$current_course = $coursedata->{'course'}; |
} |
} |
Line 856 sub process_dynamic_metadata {
|
Line 856 sub process_dynamic_metadata {
|
} |
} |
$stdno += $coursemax; # pick up the final course in the list |
$stdno += $coursemax; # pick up the final course in the list |
} |
} |
|
# check for old data that has not been run since the format was changed |
|
foreach my $course (keys(%{$resdata->{'statistics'}})) { |
|
next if (exists($course_counted{$course})); |
|
my $coursedata = $resdata->{'statistics'}{$course}; |
|
if (ref($coursedata) eq 'HASH' && exists($coursedata->{'stdno'})) { |
|
$stdno += $coursedata->{'stdno'}; |
|
} |
|
} |
$data{'stdno'}=$stdno; |
$data{'stdno'}=$stdno; |
# |
# |
# Get the context data |
# Get the context data |
Line 928 sub dynamic_metadata_storage {
|
Line 936 sub dynamic_metadata_storage {
|
return %Store; |
return %Store; |
} |
} |
|
|
|
############################################################### |
|
############################################################### |
|
### ### |
|
### &portfolio_metadata($filepath,$dom,$uname,$group) ### |
|
### Retrieve metadata for the given file ### |
|
### Returns array - ### |
|
### contains reference to metadatahash and ### |
|
### optional reference to addedfields hash ### |
|
### ### |
|
############################################################### |
|
############################################################### |
|
|
|
sub portfolio_metadata { |
|
my ($fullpath,$dom,$uname,$group)=@_; |
|
my ($mime) = ( $fullpath=~/\.(\w+)$/ ); |
|
my %metacache=(); |
|
if ($fullpath !~ /\.meta$/) { |
|
$fullpath .= '.meta'; |
|
} |
|
my (@standard_fields,%addedfields); |
|
my $colsref = $Portfolio_metadata_table_description; |
|
if (ref($colsref) eq 'ARRAY') { |
|
my @columns = @{$colsref}; |
|
foreach my $coldata (@columns) { |
|
push(@standard_fields,$coldata->{'name'}); |
|
} |
|
} |
|
my $metastring=&getfile($fullpath); |
|
if (! defined($metastring)) { |
|
$metacache{'keys'}= 'owner,domain,mime'; |
|
$metacache{'owner'} = $uname.':'.$dom; |
|
$metacache{'domain'} = $dom; |
|
$metacache{'mime'} = $mime; |
|
if ($group ne '') { |
|
$metacache{'keys'} .= ',courserestricted'; |
|
$metacache{'courserestricted'} = 'course.'.$dom.'_'.$uname; |
|
} |
|
} else { |
|
my $parser=HTML::TokeParser->new(\$metastring); |
|
my $token; |
|
while ($token=$parser->get_token) { |
|
if ($token->[0] eq 'S') { |
|
my $entry=$token->[1]; |
|
if ($metacache{'keys'}) { |
|
$metacache{'keys'}.=','.$entry; |
|
} else { |
|
$metacache{'keys'}=$entry; |
|
} |
|
my $value = $parser->get_text('/'.$entry); |
|
if (!grep(/^\Q$entry\E$/,@standard_fields)) { |
|
my $clean_value = lc($value); |
|
$clean_value =~ s/\s/_/g; |
|
if ($clean_value ne $entry) { |
|
if (defined($addedfields{$entry})) { |
|
$addedfields{$entry} .=','.$value; |
|
} else { |
|
$addedfields{$entry} = $value; |
|
} |
|
} |
|
} else { |
|
$metacache{$entry} = $value; |
|
} |
|
} |
|
} # End of ($token->[0] eq 'S') |
|
|
|
if (!exists($metacache{'domain'})) { |
|
$metacache{'domain'} = $dom; |
|
} |
|
} |
|
return (\%metacache,$metacache{'courserestricted'},\%addedfields); |
|
} |
|
|
|
sub process_portfolio_access_data { |
|
my ($dbh,$simulate,$newnames,$url,$fullpath,$access_hash,$caller) = @_; |
|
my %loghash; |
|
if ($caller eq 'update') { |
|
# Delete old data (no error if deleting non-existent record). |
|
my $error=&delete_metadata($dbh,$newnames->{'access'},$url); |
|
if (defined($error)) { |
|
$loghash{'access'}{'err'} = "MySQL Error Delete: ".$error; |
|
return %loghash; |
|
} |
|
} |
|
# Check the file exists |
|
if (-e $fullpath) { |
|
foreach my $key (keys(%{$access_hash})) { |
|
my $acc_data; |
|
$acc_data->{url} = $url; |
|
$acc_data->{keynum} = $key; |
|
my ($num,$scope,$end,$start) = |
|
($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/); |
|
next if (($scope ne 'public') && ($scope ne 'guest')); |
|
$acc_data->{scope} = $scope; |
|
if ($end != 0) { |
|
$acc_data->{end} = &sqltime($end); |
|
} |
|
$acc_data->{start} = &sqltime($start); |
|
if (! $simulate) { |
|
my ($count,$err) = |
|
&store_metadata($dbh,$newnames->{'access'}, |
|
'portfolio_access',$acc_data); |
|
if ($err) { |
|
$loghash{$key}{'err'} = "MySQL Error Insert: ".$err; |
|
} |
|
if ($count < 1) { |
|
$loghash{$key}{'count'} = |
|
"Unable to insert record into MySQL database for $url"; |
|
} |
|
} |
|
} |
|
} |
|
return %loghash; |
|
} |
|
|
|
sub process_portfolio_metadata { |
|
my ($dbh,$simulate,$newnames,$url,$fullpath,$is_course,$dom,$uname,$group,$caller) = @_; |
|
my %loghash; |
|
if ($caller eq 'update') { |
|
# Delete old data (no error if deleting non-existent record). |
|
my $error=&delete_metadata($dbh,$newnames->{'portfolio'},$url); |
|
if (defined($error)) { |
|
$loghash{'metadata'}{'err'} = "MySQL Error delete metadata: ". |
|
$error; |
|
return %loghash; |
|
} |
|
$error=&delete_metadata($dbh,$newnames->{'addedfields'},$url); |
|
if (defined($error)) { |
|
$loghash{'addedfields'}{'err'}="MySQL Error delete addedfields: ".$error; |
|
} |
|
} |
|
# Check the file exists. |
|
if (-e $fullpath) { |
|
my ($ref,$crs,$addedfields) = &portfolio_metadata($fullpath,$dom,$uname, |
|
$group); |
|
&getfiledates($ref,$fullpath); |
|
if ($is_course) { |
|
$ref->{'groupname'} = $group; |
|
} |
|
my %Data; |
|
if (ref($ref) eq 'HASH') { |
|
%Data = %{$ref}; |
|
} |
|
%Data = ( |
|
%Data, |
|
'url'=>$url, |
|
'version'=>'current', |
|
); |
|
my %loghash; |
|
if (! $simulate) { |
|
my ($count,$err) = |
|
&store_metadata($dbh,$newnames->{'portfolio'},'portfolio_metadata', |
|
\%Data); |
|
if ($err) { |
|
$loghash{'metadata'."\0"}{'err'} = "MySQL Error Insert: ".$err; |
|
} |
|
if ($count < 1) { |
|
$loghash{'metadata'."\0"}{'count'} = "Unable to insert record into MySQL portfolio_metadata database table for $url"; |
|
} |
|
if (ref($addedfields) eq 'HASH') { |
|
if (keys(%{$addedfields}) > 0) { |
|
foreach my $key (keys(%{$addedfields})) { |
|
my $added_data = { |
|
'url' => $url, |
|
'field' => $key, |
|
'value' => $addedfields->{$key}, |
|
'courserestricted' => $crs, |
|
}; |
|
my ($count,$err) = |
|
&store_metadata($dbh,$newnames->{'addedfields'}, |
|
'portfolio_addedfields',$added_data); |
|
if ($err) { |
|
$loghash{$key}{'err'} = |
|
"MySQL Error Insert: ".$err; |
|
} |
|
if ($count < 1) { |
|
$loghash{$key}{'count'} = "Unable to insert record into MySQL portfolio_addedfields database table for url = $url and field = $key"; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
return %loghash; |
|
} |
|
|
###################################################################### |
###################################################################### |
###################################################################### |
###################################################################### |
|
|
|
sub getfile { |
|
my $file = shift(); |
|
if (! -e $file ) { |
|
return undef; |
|
} |
|
open(my $fh,"<$file"); |
|
my $contents = ''; |
|
while (<$fh>) { |
|
$contents .= $_; |
|
} |
|
return $contents; |
|
} |
|
|
|
## |
|
## &getfiledates() |
|
## Converts creationdate and modifieddates to SQL format |
|
## Applies stat() to file to retrieve dates if missing |
|
sub getfiledates { |
|
my ($ref,$target) = @_; |
|
if (! defined($ref->{'creationdate'}) || |
|
$ref->{'creationdate'} =~ /^\s*$/) { |
|
$ref->{'creationdate'} = (stat($target))[9]; |
|
} |
|
if (! defined($ref->{'lastrevisiondate'}) || |
|
$ref->{'lastrevisiondate'} =~ /^\s*$/) { |
|
$ref->{'lastrevisiondate'} = (stat($target))[9]; |
|
} |
|
$ref->{'creationdate'} = &sqltime($ref->{'creationdate'}); |
|
$ref->{'lastrevisiondate'} = &sqltime($ref->{'lastrevisiondate'}); |
|
} |
|
|
|
## |
|
## &sqltime($timestamp) |
|
## |
|
## Convert perl $timestamp to MySQL time. MySQL expects YYYY-MM-DD HH:MM:SS |
|
## |
|
sub sqltime { |
|
my ($time) = @_; |
|
my $mysqltime; |
|
if ($time =~ |
|
/(\d+)-(\d+)-(\d+) # YYYY-MM-DD |
|
\s # a space |
|
(\d+):(\d+):(\d+) # HH:MM::SS |
|
/x ) { |
|
# Some of the .meta files have the time in mysql |
|
# format already, so just make sure they are 0 padded and |
|
# pass them back. |
|
$mysqltime = sprintf('%04d-%02d-%02d %02d:%02d:%02d', |
|
$1,$2,$3,$4,$5,$6); |
|
} elsif ($time =~ /^\d+$/) { |
|
my @TimeData = gmtime($time); |
|
# Alter the month to be 1-12 instead of 0-11 |
|
$TimeData[4]++; |
|
# Alter the year to be from 0 instead of from 1900 |
|
$TimeData[5]+=1900; |
|
$mysqltime = sprintf('%04d-%02d-%02d %02d:%02d:%02d', |
|
@TimeData[5,4,3,2,1,0]); |
|
} elsif (! defined($time) || $time == 0) { |
|
$mysqltime = 0; |
|
} else { |
|
&log(0," sqltime:Unable to decode time ".$time); |
|
$mysqltime = 0; |
|
} |
|
return $mysqltime; |
|
} |
|
|
###################################################################### |
###################################################################### |
###################################################################### |
###################################################################### |