Assignments scripts- Export
Below are the PowerShell scripts for Exports - EDU assignments.
<#
.Synopsis
Export the Edu Assignments data of a user in a set of classes specified in csv file.
.DESCRIPTION
The script reads the class details of user from input csv file. Get the assignments and submissions of each assignment and generates the assignment file(assignment.json) and report file(GetAssignmentsReport.csv). when extracting of submissions is done it updates the GetSubmissionsProcessed column to true for that specific class. If the script fails in between we can rerun the script with the same input file, because the file will be updated for last deletion of submission.
.Example
.\Export-EduAssignments.ps1 -userClassDetailsFile <full path of user class details csv file>
.Parameter userClassDetailsFile
This is the csv file(userClassDetails.csv) which is the output of Get-UserClasses.ps1 script. This file have information about the user classes and whether the deletion and get submissions are processed. This value should be full path of the file(drive:\GDPR\userClassDetails.csv)
.Notes
We need to have the Microsoft.IdentityModel.Clients.ActiveDirectory.dll and Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll are required
#>
param(
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[ValidateScript({
if(-Not ($_ | Test-Path)) {
throw "The userClassDetailsFile does not exist."
}
if(-Not ($_ | Test-Path -PathType Leaf) ){
throw "The userClassDetailsFile argument must be a file. Folder paths are not allowed."
}
if($_ -notmatch "(\.csv)"){
throw "The userClassDetailsFile should be a comma-separated file, generated using Get-UserClasses.ps1"
}
return $true
})]
[string] $userClassDetailsFile
)
# Load ADAL
Add-Type -Path ".\ADAL\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$educationEndpoint = "https://graph.microsoft.com/beta/education"
$script:userClassDetails = @()
$script:getAssignmentsReport = @()
$script:maxRetryAttempt = 3
$script:authHeaders = $null
$script:authenticationResult = $null
$graphEndpoint = "https://graph.microsoft.com"
$authString = "https://login.windows.net/common"
#Get the authheaders
function Get-AuthHeaders
{
Param(
[Parameter(Mandatory=$false)]
[bool] $useRefreshToken = $false
)
$clientId = "eb2298a1-a6bb-4f16-a27a-b582815db47b"
$redirectURI = New-Object System.Uri("urn:ietf:wg:oauth:2.0:oob")
$promptBehavior = [Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Always
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authString
if($useRefreshToken -eq $false)
{
$script:authenticationResult = $authContext.AcquireToken($graphEndpoint, $clientID, $redirectURI, $promptBehavior)
}
else
{
$script:authenticationResult = $authContext.AcquireTokenByRefreshToken($script:authenticationResult.RefreshToken, $clientId)
}
$authHeader = $script:authenticationResult.AccessTokenType + " " + $script:authenticationResult.AccessToken
$headers = @{"Authorization" = $authHeader; "Content-Type" = "Application/json"}
return $headers
}
#Retry logic and logging of errors for script
function Invoke-RequestWithRetry
{
param(
[Parameter(Mandatory=$true)]$url,
[Parameter(Mandatory=$false)]$classId,
[Parameter(Mandatory=$false)]$className,
[Parameter(Mandatory=$true)]$OperationName
)
for($i=1; $i -le $script:maxRetryAttempt; $i++)
{
try
{
$APIResult = Invoke-WebRequest -Method Get -Uri $url -Headers $script:authHeaders
$resultCount = 0
if($OperationName -eq "GetAssignments")
{
$valueStartIndex = ($APIResult.content.indexof('"value":') + 8)
$valueEndIndex = $APIResult.content.Length
$valueString = $APIResult.content.substring($valueStartIndex, $valueEndIndex-$valueStartIndex -1)
$resultCount = (($valuestring | select-string '"classId":' -AllMatches ).Matches.Count)
}
else
{
$submissionsJson = $APIResult.content | ConvertFrom-Json
$resultCount = $submissionsJson.value.count
}
$script:getAssignmentsReport += [PSCustomObject]@{
RequestUrl = $url
Method = "Get"
ResponseCode = $APIResult.StatusCode
ClassName = $className
ClassId = $classId
RequestId = $APIResult.Headers["request-id"]
StatusDescription = $APIResult.StatusDescription
NumberOfAttempts = $i
OperationName = $OperationName
ResultCount = $resultCount
}
return $APIResult
}
catch
{
if($null -ne $_.Exception.Response)
{
$responseCode = $_.Exception.Response.StatusCode.Value__
$requestId = $_.Exception.Response.Headers["request-id"]
}
$script:getAssignmentsReport += [PSCustomObject]@{
RequestUrl = $url
Method = "Get"
ResponseCode = $responseCode
ClassName = $className
ClassId = $classId
RequestId = $requestId
StatusDescription = $_.Exception.Message
NumberOfAttempts = $i
OperationName = $OperationName
ResultCount = $resultCount
}
if($i -eq $script:maxRetryAttempt)
{
throw $_
}
if($responseCode -eq 401)
{
$script:authHeaders = Get-AuthHeaders -useRefreshToken $true
}
}
}
}
#get assignments
function Get-Assignments
{
param(
[Parameter(Mandatory=$true)]$classDetails
)
try
{
$classId = $classDetails.ClassId
$userId = $classDetails.UserId
$role = $classDetails.Role
$className = $classId
if($null -ne $classDetails.ClassName -and $classDetail.ClassName.Length -ne 0)
{
$className = $classDetails.ClassName
}
$getAssignmentsUri = ("{0}/classes('{1}')/assignments?expand=categories,rubric,resources,submissions&TargetUserId={2}&UserClassRole={3}" -f $educationEndpoint, $classId, $userId, $role)
$assignments = $null
$assignments = Invoke-RequestWithRetry -url $getAssignmentsUri -classId $classId -className $className -OperationName "GetAssignments"
$outputstring = ""
$assignmentsStrings = @()
if($null -ne $assignments)
{
$valueStartIndex = ($assignments.content.indexof('"value":') + 8)
$valueEndIndex = $assignments.content.Length
$valueString = $assignments.content.substring($valueStartIndex, $valueEndIndex-$valueStartIndex -1)
$valueString = $valueString.TrimEnd("]")
$valueString = $valueString.TrimStart("[")
$indexValues = (($valuestring | select-string '"classId":' -AllMatches ).Matches.Index)
Write-Host "Retrieved $($indexValues.count) Assignments for $className class"
for($i=0; $i -lt $indexValues.count; $i++)
{
if($i -ne $indexValues.count -1)
{
$assignmentsStrings += $valueString.substring(($indexValues[$i]-1),($indexValues[$i+1] - $indexValues[$i] -1))
}
else
{
$assignmentsStrings += $valueString.substring(($indexValues[$i]-1), ($valueString.Length - $indexValues[$i] +1))
}
if($role -eq "Student")
{
$submissionsIndex = $assignmentsStrings[$i].indexof('"submissions":[')
$assignmentsStrings[$i] = $assignmentsStrings[$i].substring(0, $submissionsIndex+15)
$assignmentsStrings[$i] = $assignmentsStrings[$i] + "]}"
}
}
for($i=0; $i -lt $assignmentsStrings.Length ; $i++)
{
$assignmentString = $assignmentsStrings[$i]
$assignmentJson = $assignmentString | ConvertFrom-Json
$assignmentId = $assignmentJson.id
Write-Host "Getting submissions for $($assignmentJson.displayName) assignment"
$sumbmissionsEndpoint = ("{0}/classes('{1}')/assignments('{2}')/submissions?expand=outcomes,resources,submittedResources&TargetUserId={3}&UserClassRole={4}" -f $educationEndpoint, $classId, $assignmentId, $userId, $role)
$submissions = Invoke-RequestWithRetry -classId $classId -url $sumbmissionsEndpoint -classname $className -OperationName "GetSubmissions"
if($null -ne $submissions -and $submissions.Content.Length -ne 0 )
{
$submissionsJson = $submissions.content | ConvertFrom-Json
Write-Host "Retrieved $($submissionsJson.value.count) submissions for $($assignmentJson.displayName) assignment"
$valueStartIndex = ($submissions.content.indexof('"value":') + 9)
$valueEndIndex = $submissions.content.lastindexof("}")
$submissionsString = $submissions.content.substring($valueStartIndex, $valueEndIndex-$valueStartIndex -1)
$assignmentsStrings[$i] = $assignmentsStrings[$i].TrimEnd("]}")
$assignmentsStrings[$i] = $assignmentsStrings[$i] + $submissionsString + "]}"
}
}
}
$completeAssigmentsString = "["
if($assignmentsStrings.Length -eq 0)
{
$completeAssigmentsString = $completeAssigmentsString + "]"
}
else
{
for($i=0; $i -lt $assignmentsStrings.Length; $i++)
{
if($i -eq $assignmentsStrings.Length -1)
{
$completeAssigmentsString = $completeAssigmentsString + $assignmentsStrings[$i] + "]"
}
else
{
$completeAssigmentsString = $completeAssigmentsString + $assignmentsStrings[$i] + ","
}
}
}
$outputstring = '{"ClassId":"' + $classId + '","ClassName":"' + $className +'","assignments":' + $completeAssigmentsString
if($role -eq "Teacher")
{
Write-Host "Getting all assignment categories for $($classDetail.ClassName)"
$getCategoriesUri = ("{0}/classes('{1}')/assignmentCategories?TargetUserId={2}&UserClassRole={3}" -f $educationEndpoint, $classId, $userId, $role)
$categories = $null
$categories = Invoke-RequestWithRetry -url $getCategoriesUri -classId $classId -className $className -OperationName "GetAssignmentCategories"
$categoriesStrings = @()
if ($null -ne $categories)
{
$valueStartIndex = ($categories.content.indexof('"value":') + 8)
$valueEndIndex = $categories.content.Length
$valueString = $categories.content.substring($valueStartIndex, $valueEndIndex-$valueStartIndex -1)
$valueString = $valueString.TrimEnd("]")
$valueString = $valueString.TrimStart("[")
$indexValues = (($valuestring | select-string '"displayName":' -AllMatches ).Matches.Index)
Write-Host "Retrieved $($indexValues.count) Assignment Categories for $className class"
for($i=0; $i -lt $indexValues.count; $i++)
{
if($i -ne $indexValues.count -1)
{
$categoriesStrings += $valueString.substring(($indexValues[$i]-1),($indexValues[$i+1] - $indexValues[$i] -1))
}
else
{
$categoriesStrings += $valueString.substring(($indexValues[$i]-1), ($valueString.Length - $indexValues[$i] +1))
}
}
$outputstring += '","categories":' + $categoriesStrings
}
}
$outputstring += '}'
$dateTimeStamp = $(get-date -f yyyy-MM-dd)
$fileName = $directoryPath + "\" + $className + "_" + $userId + "_" + $dateTimeStamp + ".json"
$outputstring | Out-File $fileName
Write-Host("Assignments file for $className is generated at $fileName")
$classDetails.GetSubmissionsProcessed = "True"
$script:userClassDetails | Export-Csv -Path $($directoryPathOfFile) -NoTypeInformation -Force
}
catch
{
write-Error $_.Exception.Message
}
}
#get me rubrics
function Get-Rubrics
{
param(
[Parameter(Mandatory=$true)]$userId
)
try
{
$getRubricsUri = ("{0}/me/rubrics?TargetUserId={1}" -f $educationEndpoint, $userId)
$rubrics = $null
$rubrics = Invoke-RequestWithRetry -url $getRubricsUri -OperationName "GetRubrics"
$rubricsStrings = @()
if($null -ne $rubrics)
{
$valueStartIndex = ($rubrics.content.indexof('"value":') + 8)
$valueEndIndex = $rubrics.content.Length
$rubricsStrings = $rubrics.content.substring($valueStartIndex, $valueEndIndex-$valueStartIndex -1)
$outputstring = '{"Rubrics":' + $rubricsStrings + '}'
}
$dateTimeStamp = $(get-date -f yyyy-MM-dd)
$fileName = $directoryPath + "\" + "Rubrics_" + $userId + "_" + $dateTimeStamp + ".json"
$outputstring | Out-File $fileName
Write-Host("Rubrics file for $userId is generated at $fileName")
}
catch
{
write-Error $_.Exception.Message
}
}
$directoryPathOfFile = (Get-Item -path $($userClassDetailsFile)).FullName
$directoryPath = (Get-Item -Path ".\" -Verbose).FullName
$script:authHeaders = Get-AuthHeaders
$script:userClassDetails = import-csv $userClassDetailsFile
$userId = $null
$progressTracker = 1;
#loop through each line and get submissions if the getsubmissionsprocessed column is false
foreach($classDetail in $script:userClassDetails)
{
Write-Progress -Activity "Getting Assignments for user" -Status "Processing for class: $($classDetail.classId)" -PercentComplete ($progressTracker/$($script:userClassDetails | Measure-object).count * 100)
if($classDetail.GetSubmissionsProcessed -eq "False")
{
Write-Host "Getting Assignments for $($classDetail.ClassName)"
Get-Assignments -classDetails $classDetail
}
$progressTracker++
$userId = $classDetail.userId
}
#check if the user has any rubrics
Write-Host "Getting Rubrics for user $($userId)"
Get-Rubrics -userId $userId
$script:getAssignmentsReport | Export-Csv -Path .\GetAssignmentsReport.csv -NoTypeInformation
Write-Host "Report file(GetAssignmentsReport.csv) is generated at $directoryPath\GetAssignmentsReport.csv"
Write-Host "Updated Class details file($($directoryPathOfFile)) and generated updated file is at $($directoryPathOfFile)"