At this moment, I have a script that works correctly to list students of a class in Google Classroom, but it does NOT list ALL of the students, only the first 30. I need it to list ALL of the students, no matter how many there are. What I have now is the following:
function listStudents() {
var s = SpreadsheetApp.getActiveSpreadsheet();
var sh = s.getSheetByName('CLASS');
var r = sh.getDataRange();
var n = r.getNumRows();
var d = r.getValues();
for (x = 0; x < n; x++) {
var i = d[x][0];
if(i == ''){ continue; } else if (i == 'D') {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('LISTSTUDENTS');
var tea = Classroom.Courses.Students.list(d[x][8]);
var t = tea.students;
var arr = [];
try {
for (i = 0; i < t.length; i++) {
var c = t[i];
var ids = c.profile;
var em = ids.emailAddress;
arr.push([em]);
}
}
catch (e) { continue; }
sh.getRange(d[x][14], d[x][15], arr.length, arr[0].length).setValues(arr);
}
}
}
You receive only 30 students in the query because you are only accessing the first page of results. Almost every "advanced service" functions in a similar manner with regards to collections, in that they return a variable number of items in the call (usually up to a size that can be specified in the query, but there are limits). This is to ensure timely service availability for everyone who uses it.
For example, consider Bob (from Accounting). This style of request pagination means he can't request a single response with 20,000 items, during which the service is slower for everyone else. He can, however, request the next 100 items, 200 times. While Bob is consuming those 100 items from his most recent query, others are able to use the service without disruption.
To set this up, you want to use a code loop that is guaranteed to execute at least once, and uses the nextPageToken
that is included in the response to the call to .list()
to control the loop. In Javascript / Google Apps Script, this can be a do .. while
loop:
// Runs once, then again until nextPageToken is missing in the response.
const roster = [],
// The optional arguments pageToken and pageSize can be independently omitted or included.
// In general, 'pageToken' is essentially required for large collections.
options = {pageSize: /* reasonable number */};
do {
// Get the next page of students for this course.
var search = Classroom.Courses.Students.list(courseId, options);
// Add this page's students to the local collection of students.
// (Could do something else with them now, too.)
if (search.students)
Array.prototype.push.apply(roster, search.students);
// Update the page for the request
options.pageToken = search.nextPageToken;
} while (options.pageToken);
Logger.log("There are %s students in class # %s", roster.length, courseId);