Celebrate Excellence in Education: Nominate Outstanding Educators by April 15!
Found this content helpful? Log in or sign up to leave a like!
Hi all,
I'm prototyping an integration which messages students who haven't logged into Canvas for over a month.
I was planning on using the "?include=last_login" parameter on the /api/v1/users/ endpoint and using that as the basis of the reminder.
Just curious if anybody knows what generates this value? Is it only updated for a full authentication event (i.e. filling in the login screen)?
Would there be a better way to obtain the last time a person was active in Canvas?
Kind regards,
Paul
Solved! Go to Solution.
Good catch, @matthew_buckett . This is why I shouldn't try to throw something together in a hurry when I need to be somewhere else before I even start.
Canvas is pushing us towards GraphQL, but their current implementation of it is not as powerful or useful as the REST API in many cases (for me at least). I also find it harder to work with, I have to know the actual structure of the data instead of just having an array of objects to iterate through. I actually use the REST API to get the last activity and total time from the enrollments API. The REST API gives the total time spent in the course as well (graphql does not). I was trying to make it happen with GraphQL based on statements about where things are headed.
There is not a usersConnection under the account, you have to go through the coursesConnection to get to it. It's reasons like this that I don't like the push to GraphQL. With the REST API, the way to get the full list of users is through the Accounts API, but with GraphQL, you cannot get a user unless they're enrolled in a course.
In my rush to get the message finished, I missed that there was an enrollmentsConnection under the coursesConnection and introduced an extra layer of complexity into the query by involving the usersConnection.
Because of the lack of filtering involved, I would actually start with a list of all of the courses in the account, do some filtering on my own, and then fetch the enrollments for each of those courses. At least that's how I do it currently and it would avoid having to download the 12,679 enrollments for the student resources course we put all students into.
One problem of using the enrollments to get the lastActivityAt field is that if a student is enrolled in multiple sections within a course, then you will get a separate entry, with the same lastActivityAt value, for each.
Here is a better GraphQL query than what I had the first time. It only gives one entry for each user (subject to the multiple enrollments) rather than duplicating them. I've also added a filter to restrict it to just the student enrollments (this excludes the test student).
query MyQuery {
account(id: "12345") {
coursesConnection(first: 2) {
nodes {
_id
enrollmentsConnection(filter: {types: StudentEnrollment}) {
nodes {
lastActivityAt
user {
_id
}
}
}
}
}
}
}
There is another approach. If you had a specific user in mind and wanted to know their last substantial activity in any course, you could request the last activity for all courses for that one user and then take the most recent value.
query MyQuery {
legacyNode(_id: "123", type: User) {
... on User {
enrollments {
lastActivityAt
}
_id
}
}
}
Technically, you don't need to return the _id for the user since you know who the user is. I often write scripts that gather the information in an asynchronous call and save it as part of a bigger report and it is easier if that information is completely contained within the response rather than having to track it from the request.
I haven't tested it but would expect that if a user has logged into the Student/Teacher app their last login could be quite old even though they are regularly using Canvas.
We've used the account report for "Last User Access" which can be accessed through: https://canvas.instructure.com/doc/api/account_reports.html that details when a user was last active.
If you don't have access to the account reports then I'd try looking at the recent page views for a user: https://canvas.instructure.com/doc/api/users.html#method.page_views.index
I wouldn't recommend Page views as it's kind of slow and you have to do it for every student. Instead I would use the course enrollments. It's for the course and not for all of Canvas, but you can get an entire course in one call (unless pagination is needed).
You could speed that up by using GraphIQL. You would still need to do pagination, but you could use something like this. Replace the 12345 by your account ID and you'll need to mess with the pagination. I just put in a limit of 2 to keep the report fast. It times out if you take more than 30 seconds.
query MyQuery {
account(id: "12345") {
coursesConnection(first: 2) {
nodes {
usersConnection {
nodes {
enrollments {
lastActivityAt
user {
_id
}
}
}
}
}
}
}
}
There may be more efficient ways to do that, but page views happen even if the student isn't doing anything inside Canvas.
Definitely don't use logins as they can be stale for the app.
Nice idea @James, does this end up giving you each enrolment multiple times are for each course it lists all the user and then for each user it lists all that users enrolments (across all courses). So if a user is enrolled in 20 courses there will be 20*20=400 last active entries for that user.
Good catch, @matthew_buckett . This is why I shouldn't try to throw something together in a hurry when I need to be somewhere else before I even start.
Canvas is pushing us towards GraphQL, but their current implementation of it is not as powerful or useful as the REST API in many cases (for me at least). I also find it harder to work with, I have to know the actual structure of the data instead of just having an array of objects to iterate through. I actually use the REST API to get the last activity and total time from the enrollments API. The REST API gives the total time spent in the course as well (graphql does not). I was trying to make it happen with GraphQL based on statements about where things are headed.
There is not a usersConnection under the account, you have to go through the coursesConnection to get to it. It's reasons like this that I don't like the push to GraphQL. With the REST API, the way to get the full list of users is through the Accounts API, but with GraphQL, you cannot get a user unless they're enrolled in a course.
In my rush to get the message finished, I missed that there was an enrollmentsConnection under the coursesConnection and introduced an extra layer of complexity into the query by involving the usersConnection.
Because of the lack of filtering involved, I would actually start with a list of all of the courses in the account, do some filtering on my own, and then fetch the enrollments for each of those courses. At least that's how I do it currently and it would avoid having to download the 12,679 enrollments for the student resources course we put all students into.
One problem of using the enrollments to get the lastActivityAt field is that if a student is enrolled in multiple sections within a course, then you will get a separate entry, with the same lastActivityAt value, for each.
Here is a better GraphQL query than what I had the first time. It only gives one entry for each user (subject to the multiple enrollments) rather than duplicating them. I've also added a filter to restrict it to just the student enrollments (this excludes the test student).
query MyQuery {
account(id: "12345") {
coursesConnection(first: 2) {
nodes {
_id
enrollmentsConnection(filter: {types: StudentEnrollment}) {
nodes {
lastActivityAt
user {
_id
}
}
}
}
}
}
}
There is another approach. If you had a specific user in mind and wanted to know their last substantial activity in any course, you could request the last activity for all courses for that one user and then take the most recent value.
query MyQuery {
legacyNode(_id: "123", type: User) {
... on User {
enrollments {
lastActivityAt
}
_id
}
}
}
Technically, you don't need to return the _id for the user since you know who the user is. I often write scripts that gather the information in an asynchronous call and save it as part of a bigger report and it is easier if that information is completely contained within the response rather than having to track it from the request.
Hi @James thanks as always for such a thoughtful and helpful response and also to you @matthew_buckett .
GraphQL holds a lot of potential. I think I've been avoiding digging into it because I often get lost when drafting up queries in the instructure.com/graphiql interface. The REST API just makes more sense. But seeing how fast GraphQL works is cool.
I think I can get the data I need though I same still getting issues with GraphQL giving me duplicate records - it's a great start though - thank you.
To participate in the Instructure Community, you need to sign up or log in:
Sign In