Found this content helpful? Log in or sign up to leave a like!

Gradebook API to fetch Scores/Grades

Jump to solution
NitinN
Community Member

I am exploring the APIs to fetch Total score for every Module in a Course for all the Students. Please refer the attached screen print. I was able to get the scores for each assignment/quizzes using /submissions API and the final score for the whole course through /enrollments but finding difficulty to get the module total/average score highlighted in the pic. 

Could anyone please advise on this?

GET /api/v1/courses/:course_id/assignments/:assignment_id/submissions

 

 

 

Labels (1)
0 Likes
1 Solution
James
Community Champion

As @chriscas mentioned, there is no intrinsic notion of a module score, this is all done through filtering. The assignment information does not contain any information about which module(s) it is in. The modules and modules items contain information about which assignments it contains. Scores are tied to submissions that are tied to assignments and users, but not modules.

Since modules don't directly factor into the grades, I would not worry about following the rules for dropping scores. If you're looking for the lowest grade in a module, it may not be the lowest grade in the assignment group. Calculating which assignments are dropped is not trivial. Just realize that it isn't going to be perfect, but if you're analyzing at the module level, you may not want to consider dropped grades anyway.

You would have to get a list of the module items for the module you're concerned with and all of the grades and then do the calculations.

When you view the gradebook, several API calls are made. It then uses those items to filter.

  • The List modules endpoint gives a list of modules. It does not contain a list of the items (assignments) within a module.
  • The List assignment groups endpoint gives the assignment groups, but it also has include[]=module_ids as a query parameter. This is not included in the documentation, but it's how Canvas knows which assignments belong to each module. This object has a series of nested arrays. The outermost level contains one item for each assignment group. Within that is an array called assignments that contains one item for each assignment. Within that is a module_ids array that contains one string ID for each module. You can match the module_id values against the ids from the modules to match them up.
  • The List users in a course endpoint gives you a list of users.
  • The List submissions for multiple assignments endpoint is used to get submission information, including scores, user IDs, and assignment IDs. This call also uses some undocumented features to cut back on the amount of information returned.

It is important to note that an assignment may belong to more than one module. The Google Apps Script (GAS) that @JaredWall would be a convenient way of listing the scores for each of the modules, but the GAS would need to do the module calculations internally. If that's the route you were going, I would prefer some other langauge and then output the results. I mostly program in JavaScript and that's what GAS uses and I've written (and shared) some GAS sheets with people, so I guess it depends on your audience. Is this something that the teacher is going to use or something that someone is doing across multiple courses? If it's more than a single teacher, I would probably program it in something other than GAS.

Some of that information can be obtained using GraphQL. For example, I could combine the assignment information and submission information into one request. That would leave me the module information and user information as separate requests.

To test this out, add /graphiql to your Dashboard URL. Paste this code in the middle section.

query submissionsWithModulesInfo($courseId: ID!, $c1: String, $c2: String) {
  course(id: $courseId) {
    _id
    name
    courseCode
    assignmentsConnection(first: 100, after: $c1) {
      nodes {
        _id
        title
        modules {
          _id
        }
        submissionsConnection(
          filter: {enrollmentTypes: StudentEnrollment}
          first: 100
          after: $c2
        ) {
          nodes {
            score
            userId
          }
          pageInfo {
            hasNextPage
            endCursor
          }
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
}

Then expand the variables section at the bottom and add this, changing the courseId to match one of yours instead of my sandbox.

{
  "courseId": 896851,
  "c1": null,
  "c2": null
}

Then click the run button.

On the right panel, you'll get the ouput. There is some information about the course (ID, name, and code) followed by an assignmentsConnection section with a nodes array. There is one item for each assignment that contains the Canvas assignment ID, title, and which modules it belongs to.

Then there is a submissionsConnection with a nodes array that contains the score and user ID information for each submission for that assignment.

You still have to filter all that information.

The c1 and c2 variables are for pagination. If you have more than 100 assignments in a course, you will need to take the endCursor for the assignments pageInfo and place it into c1 and make the call again. If you have more than 100 submissions (students) in a course, you will need to worry about pagination with the c2 variable.

I normally try to keep my pagination variables to just one, which makes it easier to iterate through, but I don't know how big your classes are or how many assignments you have.

I realize that you could supply the module name along with the module IDs. You could also return user information instead of just user_id. That is, you could combine all four requests into a single request using GraphQL. I find the requests faster to fetch the user information and module information once rather than repeating it for each assignment or submission, but it lowers the complexity of the program to get it all at once.

Once you have it working correctly, then you can automate the GraphQL using the REST API.

View solution in original post