85 lines
5.0 KiB
JavaScript
85 lines
5.0 KiB
JavaScript
// Сводка. Приходит ВСЕГДА — в том числе когда прогона не было вовсе:
|
|
// отсутствие сводки само по себе тревога, и это работает, только если она безусловна.
|
|
// Ничего не вычисляет: собирает готовые строки, которые ей положили.
|
|
function ru(ms) {
|
|
return new Intl.DateTimeFormat('ru-RU', { dateStyle: 'short', timeStyle: 'short', timeZone: 'Europe/Moscow' }).format(new Date(ms));
|
|
}
|
|
|
|
// 🔴 Единственное место, где итоговые состояния работника названы по-русски.
|
|
// Надзиратель (задачи 11 и 12) ввозит этот список ОТСЮДА и своего не заводит:
|
|
// два списка разошлись бы, и владелец читал бы про одного работника два разных слова.
|
|
// Нового имени в договоре о стыках нет — оно названо вслух в шапке этой задачи.
|
|
export const STATUS_WORD = {
|
|
extinguished: 'погашен',
|
|
paused: 'остановлен по команде «пауза»',
|
|
stalled: 'встал',
|
|
};
|
|
|
|
// Незнакомое слово печатается как есть: молча съесть его — соврать владельцу.
|
|
function statusRu(status) {
|
|
if (!status) return 'работал';
|
|
return STATUS_WORD[status] ?? status;
|
|
}
|
|
|
|
export function buildSummary({ run_id, workers = [], lines = [], disk_free_gb = null, server_rebooted_at = null, now = Date.now() }) {
|
|
const out = [];
|
|
out.push(`Сводка за ${ru(now)}`);
|
|
out.push('');
|
|
|
|
if (!run_id) {
|
|
out.push('Прогона не было. Ничего не запускалось.');
|
|
} else if (!workers.length && !lines.length) {
|
|
out.push(`Прогон ${run_id}: дел не было, всё тихо.`);
|
|
} else {
|
|
out.push(`Прогон ${run_id}.`);
|
|
}
|
|
|
|
if (workers.length) {
|
|
out.push('');
|
|
out.push('Кто что делал:');
|
|
for (const w of workers) {
|
|
const where = w.at_item ? `, место: пункт ${w.at_item}` : '';
|
|
const why = w.reason ? ` — ${w.reason}` : '';
|
|
out.push(` · ${w.worker_id}: ${statusRu(w.status)}${why}${where}`);
|
|
}
|
|
out.push('');
|
|
out.push('Что трогали (по каждому работнику отдельной строкой). Признак приблизительный:');
|
|
out.push('он складывается из того, что работник объявил сам, и из имён файлов в его коммитах.');
|
|
for (const w of workers) {
|
|
// 🪤 Заполнять эту строку было некому, и она навсегда говорила бы «нет, нет, нет» —
|
|
// то есть врала бы владельцу ровно про деньги, права и видимое клиенту.
|
|
// Теперь её кормит надзиратель (`whatWorkerTouched`, задача 11). Нет сведений —
|
|
// так и сказано, а не «нет».
|
|
const t = w.touched;
|
|
const say = v => (v ? 'по коммитам похоже, что трогал' : 'следов не видно');
|
|
out.push(t
|
|
? ` · ${w.worker_id}: деньги — ${say(t.money)}, права доступа — ${say(t.rights)}, видимое клиенту — ${say(t.client_visible)}`
|
|
: ` · ${w.worker_id}: деньги, права доступа и видимое клиенту — не записано, сведений нет.`);
|
|
}
|
|
}
|
|
|
|
if (lines.length) {
|
|
out.push('');
|
|
out.push('Отдельные строки:');
|
|
for (const l of lines) out.push(` · ${l}`);
|
|
}
|
|
|
|
if (server_rebooted_at) {
|
|
out.push('');
|
|
out.push(`Сервер перезагружался в ${ru(server_rebooted_at)} — прогон сорван, работники сами не поднимались.`);
|
|
}
|
|
|
|
out.push('');
|
|
// 🪤 Ноль почти всегда сбой замера, а не правда о мире (замер 20.07.2026). Но «почти» —
|
|
// не «всегда»: настоящий ноль означает, что диск кончился, и молчать об этом нельзя.
|
|
// Поэтому ноль не прячется под «замерить не удалось», а называется обоими смыслами.
|
|
const disk = disk_free_gb === null
|
|
? 'замерить не удалось'
|
|
: (Number(disk_free_gb) === 0
|
|
? '0 ГБ — это либо сбой замера, либо диск вправду кончился; проверить надо немедленно'
|
|
: `${disk_free_gb} ГБ`);
|
|
out.push(`Свободного места на сервере: ${disk}.`);
|
|
|
|
return { subject: `Лидерра · сводка за ${ru(now)}`, text: out.join('\n') };
|
|
}
|