Skip to content

Instantly share code, notes, and snippets.

@nidrissi
Created June 25, 2023 06:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nidrissi/256dfa1d1afe980f80825d1a9d2fa10d to your computer and use it in GitHub Desktop.
Save nidrissi/256dfa1d1afe980f80825d1a9d2fa10d to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Splits a PDF file containing multiple exams along ranges specified in a CSV file, to be used with Moodle.
.DESCRIPTION
Given a CSV file named "exams.csv" with the following content:
Name;ID;Start;End
John Doe;1234;1;2
Jane Doe;5678;3;4
And a PDF file named "exams.pdf" with 4 pages, the following command:
Select-Exams -InputCSV exams.csv -InputPDF exams.pdf -OutputZip exams.zip
Will produce a zip file named "exams.zip" containing two PDF files:
- John Doe_1234_assignsubmission_file_Copie John Doe.pdf, containing pages 1 and 2 of the original PDF.
- Jane Doe_5678_assignsubmission_file_Copie Jane Doe.pdf, containing pages 3 and 4 of the original PDF.
.OUTPUTS
None
By default, the function will output nothing.
System.IO.FileInfo
The zip file created, if -PassThru is specified.
.NOTES
Requires PDFtk to be installed. See https://www.pdflabs.com/tools/pdftk-server/.
.LINK
https://idrissi.eu/post/select-exams
#>
function Select-Exams {
[CmdletBinding(SupportsShouldProcess)]
param (
# The path of the CSV file that explains how to split the PDF file. The headers of that CSV file should be:
# - Name: The name of the student.
# - ID: The Moodle participant for the student in that specific course (distinct from the global Moodle ID).
# - Start: The first page of that student's exam.
# - End: The last page of that student's exam.
[Parameter(Mandatory)]
[ValidateScript({ Test-Path $_ -PathType Leaf }, ErrorMessage = "File not found.")]
[String] $InputCSV,
# The path of the PDF containing all the pages of the students' exams.
[Parameter(Mandatory)]
[ValidateScript({ Test-Path $_ -PathType Leaf }, ErrorMessage = "File not found")]
[String] $InputPDF,
# The path of the zip file to be created.
[Parameter(Mandatory)]
[String] $OutputZip,
# The delimiter used in the CSV file.
[Parameter()]
[String] $Delimiter = ";",
# If specified, the function will return the zip file created.
[Parameter()]
[Switch] $PassThru
)
begin {
if (-not (Get-Command "pdftk.exe" -ErrorAction SilentlyContinue)) {
throw "PDFtk must be installed for this script to work."
}
}
process {
$ImportedCSV = Import-Csv -Path $InputCSV -Delimiter $Delimiter
$TempFolder = New-Item -Type Directory -Path (Join-Path Temp: (New-Guid))
Write-Verbose "Created temporary folder $TempFolder."
Push-Location $TempFolder
Write-Verbose "Bursting the PDF..."
# this produces files named pg_0001.pdf, pg_0002.pdf etc
pdftk.exe $InputPDF burst output (Join-Path $TempFolder "pg_%04d.pdf")
$FinalFolder = New-Item -Path $TempFolder -Name "final" -Type Directory
$Index = 0
foreach ($entry in $ImportedCSV) {
Write-Progress -Activity "PDFtk" -Status ($entry | ConvertTo-Json -Compress) -PercentComplete ($Index / $ImportedCSV.Count * 100)
if ((!$entry.Start) -or (!$entry.End) -or (!$entry.Name) -or (!$entry.ID)) {
Write-Warning ("Skipping $($entry | ConvertTo-Json -Compress)")
continue
}
$OutputPDFName = "{0}_{1}_assignsubmission_file_Copie {0}.pdf" -f $entry.Name, $entry.ID
$OutputPDF = Join-Path $FinalFolder $OutputPDFName
$Range = ($entry.Start)..($entry.End) | ForEach-Object {
Join-Path $TempFolder ("pg_{0:d4}.pdf" -f $_)
}
pdftk.exe @Range output $OutputPDF
$Index++
}
Write-Verbose "Compressing to $OutputZip."
Compress-Archive -Path @(Get-ChildItem $FinalFolder) -DestinationPath $OutputZip -PassThru:$PassThru
}
clean {
if ($null -ne $TempFolder) {
Remove-Item -Recurse $TempFolder
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment