Context
We send emails to students to let them know they have an after-school detention, including the date, time and place. However, Classroom is used for so much more communication than email that most of our students struggle to remember to check their email as frequently as they do Classroom. Wanting to leverage this, we sought to duplicate the email notification as an assignment (visible only to the individual student) in Classroom. However, the system that sends the emails can’t talk directly to Classroom, so instead the email is used to trigger a script that in turn then posts an assignment to Classroom.
The solution in steps
- The notification emails to individual students are CC’d to an admin Gmail account. A filter (setup in the Gmail Settings menu) runs on this account to apply the label “SendToClassroom” to any detention notification emails as they are received.
- Once an hour, the script below checks for any emails with that label.
- If there are any, it extracts the detention information from the email and posts it as an assignment to the relevant “whole year-group” courses in Classroom that all students are members of. The assignment is set to only be visible to that one student.
- The script removes the label from the original email before finishing.
The script
There are five whole year-group courses, one each for Year 7, Year 8, etc. To post an assignment, the script needs to know the courseId for each. See Find the Ids of your courses for a function that obtains these.
They are presented as an object with keys matching the year the year group joined the school in Year 7 (2019, 2020, etc), as the format of the students’ school email address begins with these two digits, making it easy to match the student to the correct course from their email address.
const courseId = {
'23': '#########132', // 7
'22': '#########575', // 8
'21': '#########422', // 9
'20': '#########287', // 10
'19': '#########222' // 11
}In order not to clutter the course with assignments about detentions, the script collects them all together under a “Detentions” topic label. The next few lines return the topicId of this topic, creating the topic if it doesn’t already exist.
function getTopicId(courseId) {
const topics = Classroom.Courses.Topics.list(courseId).topic;
for (const topic of topics) {
if (topic.name == 'Detentions') { return topic.topicId; }
}
try {
const newTopic = Classroom.Courses.Topics.create({name:'Detentions'},courseId);
return newTopic.topicId;
} catch (err) {
return false;
}
}Finally, this function actually does all the work, the one that the trigger runs hourly.
function createAssignmentFromEmail() {
const label = GmailApp.getUserLabelByName('SendToClassroom');
const threads = label.getThreads();
for (const thread of threads) {
const messages = thread.getMessages();
for (const message of messages) { // There should only be one per thread
// Extract the detention information from the email:
const body = message.getPlainBody();
const stringToFind = 'StartEndRoom';
const startIndex = body.indexOf(stringToFind) + stringToFind.length ;
const details = body.slice(startIndex, startIndex + 30).trim().split(' ');
const day = details[0];
const date = details[1];
const month = details[2];
const level = details[3];
const startTime = details[4];
const endTime = details[5];
const room = details[6];
// Date things:
const dateNow = new Date();
const year = dateNow.getFullYear().toString();
const monthAsNumber = { Jan: '1', Feb: '2', Mar: '3', Apr: '4', May: '5', Jun: '6', Jul: '7', Aug: '8', Sep: '9', Oct: '10', Nov: '11', Dec: '12'};
const dueTime = endTime.split(':');
const dueUTCHours = (dueTime[0]*1 + dateNow.getUTCHours() - dateNow.getHours()).toString(); // Adjusts the hour to UTC if different from local (ie BST)
const dueMinutes = dueTime[1];
// Details for the Classroom assignment:
const student = message.getTo();
const yearGroup = student.slice(0,2);
let courseWork = {
workType: 'ASSIGNMENT',
state: 'PUBLISHED',
assigneeMode: 'INDIVIDUAL_STUDENTS',
individualStudentsOptions: {studentIds: [student]},
title: `${level} detention`,
description: `You have a ${level} detention in ${room} on ${day} ${date} ${month} from ${startTime} to ${endTime}. Please check your email for further details.`,
dueDate: {day: date, month: monthAsNumber[month], year: year},
dueTime: {hours: dueUTCHours, minutes: dueMinutes},
};
// Include the topicId of the "Detentions" topic, if it exists:
const topicId = getTopicId(courseId[yearGroup]);
if (topicId) { courseWork.topicId = topicId; }
// Post the assignment and remove the label in Gmail if successful:
try {
Classroom.Courses.CourseWork.create(courseWork, courseId[yearGroup]);
thread.removeLabel(label);
} catch (err) {
// If it fails it fails!
}
}
}
}The trigger
The Triggers menu is available on the left-side menu of the editor:

and the settings are as follows:

Notes
Be aware that the script (and its Trigger) must run from the Google account that is receiving and labelling the emails. The same account must also be a co-teacher of the Google Classroom course that the assignment is being posted to (or an admin account).

