-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCourse_Attendance.ps1
179 lines (151 loc) · 6.43 KB
/
Course_Attendance.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#
# developed by jeff.kelley@blackboard.com
# with attribution to mike.buchanon@rasmussen.edu
# from https://community.blackboard.com/docs/DOC-4949-devcon-playing-nice-with-powershell-and-the-learn-rest-api
#
# BLACKBOARD MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
# OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
# TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE, OR NON-INFRINGEMENT. BLACKBOARD SHALL NOT BE LIABLE FOR ANY
# DAMAGES SUFFERED AS A RESULT OF USING, MODIFYING OR
# DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
#
# tested against Learn release 3700.5 and 3700.7
#
Param(
[string]$CRSID,
[string]$PROP
)
# Import and set Constants
$workingCourse = $CRSID
$Props = convertfrom-stringdata (get-content ./$PROP -raw)
$Props | Format-Table
$LMSURL = $Props.host
$key = $Props.key
$secret= $Props.secret
$resultLimit = $Props.resultLimit
# Authentication Function returns auth token
function doAuthenticate ($thisHost, $thisKey, $thisSecret)
{
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $thisKey,$thisSecret)))
$body = "grant_type=client_credentials"
$apiuri = "$thisHost/learn/api/public/v1/oauth2/token"
try {
$authResponse = Invoke-RestMethod -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)} -body $body -Uri $apiuri -Method Post
}
catch {
write-host "Error calling API at $apiuri`n$_"
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
exit
}
$expires_in = [int]($authResponse.expires_in / 60)
$token = $authResponse.access_token
write-host "Token: $token expires in $expires_in minutes"
return $token
# todo: routine to refresh token if less than X minutes remain
# End Authenticate
}
# Function to get all users enrolled in course
function getUsers ($thisHost, $thisToken, $thisCourseId)
{
$coursePath = "/learn/api/public/v1/courses/courseId:$thisCourseId"
$extPath = "/users?expand=user&fields=user.id,user.externalId,user.userName,user.studentId&limit=$resultLimit"
$fullPath = $coursePath + $extPath
while ($fullPath.Length -gt 0){
$apiuri = $thisHost + $fullPath
try {
$membersResponse = Invoke-RestMethod -Headers @{Authorization=("Bearer $thisToken")} -Uri $apiuri -Method "GET"
} catch {
write-host "Error calling API at $apiuri`n$_"
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
exit
}
$fullPath = $membersResponse.paging.nextPage
$users = $users + $membersResponse.results.user
}
return $users
}
# Function to get all the attendance meetings for the course
function getMeetings ($thisHost, $thisToken, $thisCourseId)
{
$coursePath = "/learn/api/public/v1/courses/courseId:$thisCourseId"
$extPath = "/meetings?limit=$resultLimit"
$fullPath = $coursePath + $extPath
while ($fullPath.Length -gt 0){
$apiuri = $thisHost + $fullPath
try {
$meetingsResponse = Invoke-RestMethod -Headers @{Authorization=("Bearer $thisToken")} -Uri $apiuri -Method "GET"
} catch {
write-host "Error calling API at $apiuri`n$_"
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
exit
}
$fullPath = $meetingsResponse.paging.nextPage
$meetings = $meetings + $meetingsResponse.results
}
return $meetings
}
# Function to get all the attendance record from Course/Meeing pair
function getRecords ($thisHost, $thisToken, $thisCrsId, $thisMeetingId)
{
$coursePath = "/learn/api/public/v1/courses/$thisCrsId"
$extPath = "/meetings/$thisMeetingId/users?limit=$resultLimit"
$fullPath = $coursePath + $extPath
while ($fullPath.Length -gt 0){
$apiuri = $thisHost + $fullPath
try {
$recordsResponse = Invoke-RestMethod -Headers @{Authorization=("Bearer $thisToken")} -Uri $apiuri -Method "GET"
} catch {
write-host "Error calling API at $apiuri`n$_"
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
exit
}
$fullPath = $recordsResponse.paging.nextPage
$records = $records + $recordsResponse.results
}
return $records
}
# BEGIN SCRIPT
[System.Net.ServicePointManager]::SecurityProtocol += 'tls12'
$token = doAuthenticate $LMSURL $key $secret
$members = getUsers $LMSURL $token $workingCourse
#$members | Format-Table -Property id, userName, externalId, studentId
$memberCount = $members.Length
$meetings = getMeetings $LMSURL $token $workingCourse
#$meetings | Format-Table -Property id, courseId, start, end
$meetingCount = $meetings.Length
foreach ($meeting in $meetings)
{
$recs = getRecords $LMSURL $token $meeting.courseId $meeting.id
$allRecords = $allRecords + $recs
}
#$allRecords | Format-Table -Property id, meetingId, userId, status
$recordCount = $allRecords.Length
$output = @()
foreach ($rec in $allRecords)
{
$usr = $members | where {$_.id -eq $rec.userId}
# todo: catch users without student_id - use this method for other attributes that might be null.
$meet = $meetings | where {$_.id -eq $rec.meetingId}
$outputRecord = @{
"course_id" = $workingCourse
"courseId" = $meet.courseId
"recordId" = $rec.id
"meetingId" = $meet.id
"userId" = $usr.id
"userName" = $usr.UserName
"externalId" = $usr.externalId
"studentId"= $usr.studentId
"start" = $meet.start
"status" = $rec.status
}
$output += New-Object PSObject -Property $outputRecord
}
Write-Host "Course $workingCourse has $memberCount members, $meetingCount meetings, and $recordCount attendance records."
$Output | Sort-Object "start" | Format-Table
$Output | Export-Csv -Path .\$workingCourse.attendance.csv -NoTypeInformation
Remove-Variable * -ErrorAction SilentlyContinue