@gumbelj
Edit: After I posted this, I noticed I was on a page asking about teacher time. I changed all the StudentEnrollment to TeacherEnrollment. That also makes the GraphQL approach so much faster because there are limited teachers per course. I did not update the rest of the information -- it refers to student enrollments. Using GraphQL with terms is probably the fastest way to get all of the information for instructors. The request that took 35.9 seconds for students took 8.9 seconds for instructors.
The link I provided in the message you're responding to takes you to the main page for the Enrollments API. The reason I didn't list a specific API endpoint is because there are several that will return the information. You can list enrollments for a course, a section, or user. There are other ways to get an enrollment from that API as well, but I use the course endpoint.
For GraphQL, you could do something like this:
query enrollmentActivity($courseId: ID!) {
course(id: $courseId) {
enrollmentsConnection(filter: {types: TeacherEnrollment}) {
nodes {
lastActivityAt
totalActivityTime
state
user {
_id
sisId
sortableName
}
}
}
}
}
and then pass Canvas course ID in the variables section. Make sure you pass one of your course IDs and not one of mine.
{ "courseId": 3750081 }
You can play around with GraphQL through the GraphQL Explorer by adding /graphiql to the end of your dashboard URL. Once you have it working the way you want, you can use it programmatically with the GraphQL API.
The enrollment API, which is where this data comes from, is an expensive one. Expect that you will need to use pagination. With the REST API calls, you can typically get up to 100 at a time, so if none of your classes exceed that, then you can get each class with a single call. Otherwise, it will involve bookmarks and the page=# will not work.The graphql approach allows you up to 60 seconds and is more likely to return an entire dataset with a single call.
Both of the above approaches need you to get a list of courses and iterate through them.
You could -- highly not recommended -- fetch the enrollments for your entire instance using graphql. This will certainly require pagination unless you're a small school in your first semester. Change the 12345 to be your account number.
query allEnrollmentActivity {
account(id: "12345") {
coursesConnection(first: 100, after: "") {
nodes {
_id
sisId
name
enrollmentsConnection(filter: {types: TeacherEnrollment}) {
nodes {
lastActivityAt
totalActivityTime
state
user {
_id
sisId
sortableName
}
}
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
That request took me about 4.25 seconds to make with just 100 courses. In theory, I could go about 1400 (60 seconds / 4.25 seconds per 100 enrollments) but it may not work that way. When I requested 1000 courses, it timed out. 500 courses took me about 16 seconds. It is dependent on the number of enrollments in each class, so use a smaller number than you think you should be able to get.
What will happen when you get the results is that there will be a pageInfo object in the results.
"pageInfo": {
"endCursor": "MTAw",
"hasNextPage": true
}
The hasNextPage lets me know whether I'm done or not. The endCursor becomes the value for the "after" in the next request.
For example, my next request would start
query allEnrollmentActivity {
account(id: "12345") {
coursesConnection(first: 100, after: "MTAw") {
The pagination is not why I do not recommend getting them for all courses. We've been using Canvas since 2012 and there is no reason for us to repeatedly download the enrollments for the last 12 years when I just want the current semester. There is no way to filter courses with that request. It's better to get a list of courses that you want and then request the enrollments.
You could get the results by term. Our Spring 2024 term has a SIS ID of "2024a". We're small, so I was able to fetch all this in one request -- but it took 35.9 seconds. It would be safer to use pagination.
query termEnrollmentActivity {
term(sisId: "2024a") {
coursesConnection {
nodes {
_id
name
sisId
enrollmentsConnection(filter: {types: TeacherEnrollment}) {
nodes {
lastActivityAt
totalActivityTime
state
user {
_id
sisId
sortableName
}
}
}
}
}
}
}
Finally, I'm using "state" as one of the properties returned. This gives you all of the enrollment states. You can filter off enrollment states to get only the "active" students, for example. Active doesn't mean they're actively participating, it means they're still enrolled in the course.
Through the GraphQL Explorer, it only allows you to specify one state at at time
filter: {types: TeacherEnrollment, states: active}
However, you can manually edit the code to provide an array list.
filter: {types: TeacherEnrollment, states: [active, completed]}
If you filter by state you will (1) get the results faster and (2) potentially not need to include the state in the request.