|
#Requires -Version 7.0 |
|
|
|
<# |
|
Created by Duncan Gibson. This script is shared under the terms of the |
|
CC Attribution-NonCommercial-ShareAlike 4.0 International license, which |
|
may be found here: https://creativecommons.org/licenses/by-nc-sa/4.0/ |
|
#> |
|
|
|
<# |
|
The PowerShell help system can be invoked to show you how to use this |
|
script by running `Get-Help helper.ps1` |
|
#> |
|
|
|
<# |
|
.SYNOPSIS |
|
This script was designed to help automate the grading of homework |
|
assignments for Dr. Sakire Arslan Ay's CptS 355 class. |
|
|
|
It expects there to be a "submissions.zip" file from Canvas in the working |
|
directory. You can acquire one of these files by going to the assignment in |
|
Canvas and clicking "Download Submissions" in the panel on the right. |
|
|
|
It also expects you to have a file of unit tests that check your assignment. |
|
|
|
.DESCRIPTION |
|
This script will unzip the submissions.zip file and group the contents by |
|
student. Then it will copy each student's files to a subdirectory named |
|
"workspace", one student at a time. Submitted .zip files are a special |
|
case; they will be unzipped and their contents added to the workspace. |
|
|
|
The test file and other provided files will likewise be copied to the |
|
workspace. |
|
|
|
Once all files for a student are in place, it will run the test file, |
|
display its outputs, and pause. If you need to re-run the tests for a |
|
student for some reason, open another terminal and run the test file in the |
|
workspace. Workspace files are safe to edit without affecting other |
|
students. |
|
|
|
The workspace is reset before the next student's submission is loaded. |
|
|
|
WARNING: This script does not make guarantees about the safety of submitted |
|
files. If you're feeling paranoid, you should probably use a VM. |
|
|
|
.NOTES |
|
This script is currently configured to run Haskell and Python programs. |
|
More languages can be supported by adding a case for them in the |
|
configuration section of the script, right below the parameter declaration. |
|
|
|
.LINK |
|
https://gist.github.com/legowerewolf/f34835c07acb06527358155e9ca66c4c |
|
#> |
|
|
|
[CmdletBinding()] |
|
param ( |
|
[Parameter(Mandatory)] |
|
[string]$TestFileName, |
|
|
|
[Parameter()] |
|
<# |
|
A list of files that we provide that should be in the workspace for |
|
tests to run. |
|
#> |
|
[string[]]$TesterProvidedFiles = @(), |
|
|
|
[Parameter()] |
|
<# |
|
A list of files that we expect to be in the submission. If a given file |
|
isn't detected, the grader will be prompted to check the workspace and |
|
correct the filename. |
|
|
|
This list should, at minimum, include files that are depended on by the |
|
main test file. |
|
#> |
|
[string[]]$ExpectedFiles = @() |
|
) |
|
|
|
#################### START CONFIGURATION ################### |
|
|
|
<# |
|
This specifies the program that gets run for the selected test file. You |
|
shouldn't have to edit this unless Sakire uses a language other than Python or |
|
Haskell. |
|
#> |
|
$program = { |
|
if ($TestFileName -match ".*`.hs") { return "runghc" } |
|
if ($TestFileName -match ".*`.py") { return "python" } |
|
}.Invoke() |
|
|
|
##################### END CONFIGURATION #################### |
|
|
|
# helper function for the script's interface |
|
function Start-Pause { |
|
[CmdletBinding()] |
|
param ( |
|
[Parameter(Mandatory)] |
|
[ValidateLength(1, 1)] |
|
[string]$ResumeChar, |
|
|
|
[Parameter(Mandatory)] |
|
[string]$Prompt |
|
) |
|
|
|
Write-Output $Prompt |
|
Start-Sleep -s 1 |
|
$Host.UI.RawUI.FlushInputBuffer() |
|
while ($Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown').Character -notlike $ResumeChar) {} |
|
} |
|
|
|
# real start of the script |
|
|
|
# verify and fix configuration |
|
if ($TesterProvidedFiles.GetType().FullName -eq "System.String") { $TesterProvidedFiles = , $TesterProvidedFiles } |
|
if ($ExpectedFiles.GetType().FullName -eq "System.String") { |
|
$ExpectedFiles = , $ExpectedFiles |
|
} |
|
|
|
# purge existing files |
|
Remove-Item "./submissions/" -Recurse -ErrorAction SilentlyContinue |
|
Remove-Item "./workspace/" -Recurse -ErrorAction SilentlyContinue |
|
|
|
# prep files and folder structure |
|
Expand-Archive "./submissions.zip" -DestinationPath "./submissions/" |
|
|
|
# group files by student |
|
$submissiongroups = Get-ChildItem "./submissions/" | Where-Object Name -NotLike "*test*" | ForEach-Object -Begin { $groups = @{} } -Process { |
|
$key = $_.Name.Split("_")[0] |
|
|
|
if ($groups.ContainsKey($key)) { |
|
$groups[$key] += $_ |
|
} |
|
else { |
|
$groups.Add($key, @($_)) |
|
} |
|
} -End { $groups } |
|
|
|
# loop over each student |
|
foreach ($submission in ($submissiongroups.GetEnumerator() | Sort-Object -Property Name)) { |
|
# reset the workspace |
|
Remove-Item "./workspace/" -Recurse -Force -ErrorAction SilentlyContinue |
|
New-Item -Path "./workspace" -ItemType Directory >$null |
|
|
|
# copy our provided files to the workspace |
|
foreach ($file in $TesterProvidedFiles) { |
|
Copy-Item $file -Destination "./workspace/" |
|
} |
|
|
|
# copy the student's files to the workspace |
|
foreach ($file in $submission.Value) { |
|
$destname = $file.Name.Split("_")[-1] |
|
|
|
$destname = $destname -replace "-\d+\.", "." |
|
|
|
Copy-Item $file -Destination "./workspace/$destname" |
|
|
|
if ($destname -like "*.zip") { |
|
Expand-Archive "./workspace/$destname" -DestinationPath "./workspace/zipped/" |
|
|
|
$zip_contents = Get-ChildItem "./workspace/zipped/" |
|
|
|
if ($zip_contents.count -eq 1 -and $zip_contents[0].Mode -like "d*") { |
|
$zip_contents = $zip_contents[0] | Get-ChildItem |
|
} |
|
|
|
$zip_contents | ForEach-Object { Move-Item -Path $_.FullName -Destination ".\workspace\" } |
|
|
|
Remove-Item -Recurse .\workspace\zipped\ |
|
Remove-Item "./workspace/$destname" |
|
} |
|
} |
|
|
|
# check that the submission has the expected filenames |
|
foreach ($expected_file_name in $ExpectedFiles) { |
|
if (-not (Test-Path "./workspace/$expected_file_name")) { |
|
Start-Pause -ResumeChar 't' -Prompt "Warning! No $expected_file_name detected! Correct this, then press [t] to continue." |
|
} |
|
} |
|
|
|
# copy the test file to the workspace |
|
Copy-Item $TestFileName -Destination "./workspace/" |
|
|
|
Write-Output "Loaded files for $($submission.Key)." |
|
|
|
# set current working directory to the workspace |
|
Push-Location |
|
Set-Location "./workspace" |
|
|
|
# run the test file |
|
Invoke-Expression "$program $TestFileName" |
|
|
|
# return to the original directory |
|
Pop-Location |
|
|
|
Write-Output "Above results are for $($submission.Key)." |
|
|
|
Start-Pause -ResumeChar 't' -Prompt (("=" * 20) + " Press [t] to continue to next student. " + ("=" * 20)) |
|
} |