diff --git a/lib/WeBWorK/Utils/CourseManagement.pm b/lib/WeBWorK/Utils/CourseManagement.pm index 581cf10d71..514d086132 100644 --- a/lib/WeBWorK/Utils/CourseManagement.pm +++ b/lib/WeBWorK/Utils/CourseManagement.pm @@ -15,6 +15,7 @@ use DBI; use String::ShellQuote; use UUID::Tiny qw(create_uuid_as_string); use Mojo::File qw(path); +use Mojo::JSON qw(decode_json encode_json); use File::Copy::Recursive qw(dircopy); use File::Spec; use Archive::Tar; @@ -23,7 +24,7 @@ use WeBWorK::Debug; use WeBWorK::CourseEnvironment; use WeBWorK::DB; use WeBWorK::Utils qw(runtime_use); -use WeBWorK::Utils::Files qw(surePathToFile); +use WeBWorK::Utils::Files qw(surePathToFile getHumanReadableFileSize); use WeBWorK::Utils::Instructor qw(assignSetsToUsers); our @EXPORT_OK = qw( @@ -125,24 +126,35 @@ including the .tar.gz extension) and file C. For example, sub listArchivedCourses { my ($ce) = @_; - my $archivesDir = path("$ce->{webworkDirs}{courses}/$ce->{admin_course_id}/archives"); + my $archivesDir = path($ce->{webworkDirs}{courses})->child($ce->{admin_course_id}, 'archives'); surePathToFile($ce->{webworkDirs}{courses}, "$archivesDir/test"); # Ensure archives directory exists. my $archives = $archivesDir->list->grep(qr/\.tar\.gz$/i); + my $archiveDataFile = $archivesDir->child('archive-cache.json'); + my $archiveData = eval { decode_json($archiveDataFile->slurp) } || {}; + + my $archiveDataUpdated = 0; my %return; for (@$archives) { - my $size = $_->stat->size; - my @units = qw(B KB MB GB); - my $unit_idx = 0; - while ($size >= 1024 && $unit_idx < $#units) { - $size /= 1024; - ++$unit_idx; + my $basename = $_->basename; + my $lastModified = $_->stat->mtime; + + if ($archiveData->{$basename} && $archiveData->{$basename}{lastModified} >= $lastModified) { + $return{ $archiveData->{$basename}{courseID} } = { + filename => $basename, + size => $archiveData->{$basename}{size} + } + if defined $archiveData->{$basename}{courseID}; + next; } - my $basename = $_->basename; - my $arch = Archive::Tar->new($_); + + $archiveDataUpdated = 1; + $archiveData->{$basename} = { lastModified => $lastModified }; + + my $archive = Archive::Tar->new($_); my %top_level; - for my $file ($arch->get_files) { + for my $file ($archive->get_files) { (my $first = $file->full_path) =~ s{/.*}{}s; $top_level{$first} = 1 if length $first; } @@ -151,12 +163,22 @@ sub listArchivedCourses { next; } my ($currCourseID) = keys %top_level; - my $round = 10**($unit_idx > 0 ? $unit_idx - 1 : 0); - $return{$currCourseID} = { - filename => $basename, - size => sprintf("%s %s", int($size * $round) / $round, $units[$unit_idx]) - }; + + $archiveData->{$basename}{courseID} = $currCourseID; + $archiveData->{$basename}{size} = getHumanReadableFileSize($_); + $return{$currCourseID} = { filename => $basename, size => $archiveData->{$basename}{size} }; } + + my %archives = map { $_->basename => 1 } @$archives; + for (keys %$archiveData) { + unless ($archives{$_}) { + delete $archiveData->{$_}; + $archiveDataUpdated = 1; + } + } + + $archiveDataFile->spew(encode_json($archiveData)) if $archiveDataUpdated; + return %return; } @@ -936,6 +958,21 @@ sub archiveCourse { } _archiveCourse_remove_dump_dir($ce, $dump_dir); + # Update the admin course archive cache if this is saved to the archives directory of the admin course. + if (-e $archive_path + && $archive_path eq "$ce->{webworkDirs}{courses}/$ce->{admin_course_id}/archives/$courseID.tar.gz") + { + my $archiveDataFile = + path($ce->{webworkDirs}{courses})->child($ce->{admin_course_id}, 'archives', 'archive-cache.json'); + my $archiveData = eval { decode_json($archiveDataFile->slurp) } || {}; + $archiveData->{"$courseID.tar.gz"} = { + courseID => $courseID, + size => getHumanReadableFileSize(path($archive_path)), + lastModified => time + }; + $archiveDataFile->spew(encode_json($archiveData)); + } + return $message; } diff --git a/lib/WeBWorK/Utils/Files.pm b/lib/WeBWorK/Utils/Files.pm index 911489fb7a..13787b9ae8 100644 --- a/lib/WeBWorK/Utils/Files.pm +++ b/lib/WeBWorK/Utils/Files.pm @@ -10,6 +10,7 @@ our @EXPORT_OK = qw( readFile listFilesRecursive path_is_subdir + getHumanReadableFileSize ); sub surePathToFile ($start_directory, $path) { @@ -93,6 +94,19 @@ sub path_is_subdir ($path, $dir, $allow_relative = 0) { return 1; } +sub getHumanReadableFileSize ($file) { + my $size = $file->stat->size; + my @units = qw(B KB MB GB); + my $unit_idx = 0; + while ($size >= 1024 && $unit_idx < $#units) { + $size /= 1024; + ++$unit_idx; + } + my $round = 10**($unit_idx > 0 ? $unit_idx - 1 : 0); + + return sprintf("%s %s", int($size * $round) / $round, $units[$unit_idx]); +} + 1; =head1 NAME @@ -145,4 +159,12 @@ prefix of it matches C<$dir>. If either of these checks fails, a false value is returned. Otherwise, a true value is returned. +=head2 getHumanReadableFileSize + + getHumanReadableFileSize($file) + +Returns the human readable size (the size with the appropriate unit of C, +C, C, or C) of a C<$file> where C<$file> is a C object. +Note that this method assumes the passed C<$file> exists and is readable. + =cut