Skip to content

Instantly share code, notes, and snippets.

@alexshpilkin
Created March 14, 2021 00:27
Show Gist options
  • Save alexshpilkin/63a6395beba21c032bf1f56e9befc4ad to your computer and use it in GitHub Desktop.
Save alexshpilkin/63a6395beba21c032bf1f56e9befc4ad to your computer and use it in GitHub Desktop.
Google Apps Script program to post student grades
// SPDX-License-Identifier: CC0-1.0
const EMAIL = 3 /* C */, TUTOR = 5 /* E */, PROBLEMS = 7 /* G: */;
const TOKEN = 'SLACK_TOKEN';
function slack(url, payload) {
var token = PropertiesService.getDocumentProperties().getProperty(TOKEN);
if (token === null)
throw new Error('Slack bot token not set');
var req = {headers: {'Authorization': `Bearer ${token}`}};
if (typeof payload !== 'undefined') {
req.method = 'POST';
req.contentType = 'application/json;charset=utf-8';
req.payload = JSON.stringify(payload);
}
var res = UrlFetchApp.fetch(`https://slack.com/api/${url}`, req);
return JSON.parse(res.getContentText());
}
var users = new Map();
function findUser(email) {
var user = users.get(email);
if (typeof user === 'undefined') {
var result = slack(`users.lookupByEmail?email=${encodeURIComponent(email)}`);
if (!result.ok) throw new Error(`user ${email} not found`);
users.set(email, user = result.user.id);
}
return user;
}
function sendToSlack() {
var ui = SpreadsheetApp.getUi(), sheet;
// Map short tutor names to Slack user IDs
sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Семинаристы');
var tutors = new Map();
for (var row = sheet.getFrozenRows() + 1; row <= sheet.getLastRow(); row++) {
tutors.set(sheet.getRange(row, 1).getValue(),
findUser(sheet.getRange(row, 2).getValue()));
}
// Get problem set and problem names
sheet = SpreadsheetApp.getActiveSheet();
var prset = sheet.getName().trim(), problems = [];
for (var col = PROBLEMS; col <= sheet.getLastColumn(); col++)
problems.push(sheet.getRange(1, col).getValue());
// Walk through selected rows and collect the grades
var data = [];
for (var range of sheet.getActiveRangeList().getRanges()) {
if (range.getColumn() != 1 || range.getLastColumn() < sheet.getLastColumn())
throw new Error('selection does not extend all the way to the right');
for (var row = range.getRow(); row <= range.getLastRow(); row++) {
if (row <= sheet.getFrozenRows() || sheet.isRowHiddenByFilter(row)) continue;
if (row > sheet.getLastRow()) break;
var entry = {
student: findUser(sheet.getRange(row, EMAIL).getValue()),
tutor: tutors.get(sheet.getRange(row, TUTOR).getValue()),
grades: problems.map((_, i) => sheet.getRange(row, PROBLEMS+i).getValue()),
};
if (typeof entry.tutor === 'undefined')
throw new Error('unknown or unspecified tutor');
if (entry.grades.every(grade => grade === ''))
continue;
if (entry.grades.some(grade => grade === ''))
throw new Error('there are ungraded problems');
data.push(entry);
}
}
var confirmation = `Send grades for ${prset} to ${data.length} student(s)?`;
if (ui.alert(confirmation, ui.ButtonSet.OK_CANCEL) !== ui.Button.OK)
return;
// From this point on, we are committed to sending messages. Slack can still
// return an error to us and the user will have to resend manually, risking
// duplicates, but this can't be fixed without remembering additional state.
// Format and send the grades
for ({student, tutor, grades} of data) {
var message, result;
grades = grades.map((grade, i) => `*${problems[i]}* ${grade}`).join(' ');
message = {
channel: student,
text: `<@${tutor}> проверил(а) ваше решение ${prset} ` +
`и поставил(а) оценки:\n${grades}`,
};
result = slack('chat.postMessage', message);
if (!result.ok) throw new Error(`Slack returned ${result.error}`);
message = {
channel: tutor,
text: `<@${student}> получил(а) поставленные вами оценки ` +
`за ${prset}:\n${grades}`,
}
result = slack('chat.postMessage', message);
if (!result.ok) throw new Error(`Slack returned ${result.error}`);
}
}
function setSlackToken() {
var ui = SpreadsheetApp.getUi(),
res = ui.prompt('Enter Slack bot token', ui.ButtonSet.OK_CANCEL);
if (res.getSelectedButton() !== ui.Button.OK)
return;
var token = res.getResponseText();
if (!token.startsWith('xoxb-'))
throw new Error('not a valid Slack bot token');
// token must have chat:write, im:write, user:read, user:read.email scopes
PropertiesService.getDocumentProperties().setProperty(TOKEN, token);
}
function onOpen() {
SpreadsheetApp.getUi().createAddonMenu()
.addItem('Send to Slack', 'sendToSlack')
.addItem('Set Slack bot token', 'setSlackToken')
.addToUi();
}

Последнее за сегодня: я скрутил гуглотаблицу и слак скриптом, который от имени приложения «Оценки» присылает выбранным студентам (и их семинаристам) в слак их оценки, если нажать на кнопочку в таблице.

Инструкция:

  • Открыть лист, соответствующий нужному ДЗ (название задания берётся из названия листа);
  • Настроить фильтры (если нужно) и выделить нужные строки таблицы (в любом случае);
  • Выбрать в меню Add-ons > Grades > Send to Slack;
  • Проверить в появившемся окошке количество выбранных студентов и нажать ОК.

Для проверки можете погонять его на разных строках листа «Отладочное ДЗ», прописав там себя в качестве студента и/или семинариста.

Скрипт игнорирует студентов, у которых не проверено ни одной задачи, но обижается на студентов, у которых не указан семинарист или проверены только некоторые задачи (если задачи нет в работе, ставьте за неё оценку «0»).

Скрипт строго привязан к номерам столбцов на листе (хотя обрабатывает произвольное количество задач), при необходимости меняйте константы в исходном коде.

Исходный код открывается через меню Tools > Script editor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment