mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-10 21:32:23 +00:00
fix(firms): label a snapshot that ends at a set time with that time, not LIVE
A FIRMS source may answer the 24 hours to a set time, naming its end as `at` (epoch ms). The layer row then reads "24 h to MM-DD HH:MM UTC" instead of "LIVE · updated ...", and getStats() reports `at`. A snapshot without `at` (the trailing 24 hours to now, as the dev proxy serves) keeps the LIVE label. Translated in every catalog.
This commit is contained in:
1 parent
9cfac35e38
commit
43706b8dd0
17 files changed
+73
-3
No files matched your search
@@ -145,6 +145,10 @@ export async function fetchFirms(
|
||||
*
|
||||
* Routes:
|
||||
* GET /api/firms → {fetchedAt, stale, ttlMs, sources, count, fires}
|
||||
*
|
||||
* The trailing 24 hours to now. A source may instead answer the 24 hours to
|
||||
* a set time, naming its end as `at` (epoch ms); the layer's row then shows
|
||||
* that time rather than LIVE. This proxy never sets `at`.
|
||||
* GET /api/firms/status → {hasKey, lastFetch, count, stale, ttlMs, transactions}
|
||||
*
|
||||
* Keyless (no FIRMS_MAP_KEY): /api/firms → 503 {error:'no_key'}; status →
|
||||
|
||||
@@ -43,3 +43,43 @@ for (const [age, zh, en, hant] of cases) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test('a FIRMS snapshot that ends at a set time names it instead of LIVE', (context) => {
|
||||
const now = 1800000000000;
|
||||
context.mock.method(Date, 'now', () => now);
|
||||
const previousLocale = getLocale();
|
||||
const at = Date.UTC(2026, 9, 21, 15);
|
||||
const layerState = {
|
||||
_loading: false,
|
||||
_stale: false,
|
||||
_keyRequired: false,
|
||||
_error: null,
|
||||
_lastUpdate: now - 120000,
|
||||
_at: at,
|
||||
_count: 1,
|
||||
_cellCount: 0,
|
||||
_fires: [],
|
||||
};
|
||||
const model = createModel({ services: { anchors: {} } });
|
||||
const { methods } = createQueries({
|
||||
layerState,
|
||||
components: { model },
|
||||
config: {},
|
||||
});
|
||||
try {
|
||||
setLocale('en');
|
||||
assert.equal(methods.getStats().loadingLabel, '24 h to 10-21 15:00 UTC');
|
||||
assert.equal(methods.getStats().at, at);
|
||||
setLocale('de');
|
||||
assert.equal(
|
||||
methods.getStats().loadingLabel,
|
||||
'24 Std. bis 21.10. 15:00 UTC',
|
||||
);
|
||||
layerState._at = null;
|
||||
setLocale('en');
|
||||
assert.equal(methods.getStats().loadingLabel, 'LIVE · updated 2m ago');
|
||||
assert.equal(methods.getStats().at, null);
|
||||
} finally {
|
||||
setLocale(previousLocale);
|
||||
}
|
||||
});
|
||||
@@ -879,6 +879,7 @@
|
||||
"KEY REQUIRED": "المفتاح مطلوب",
|
||||
"STALE · cached {age}": "متقادم · مخزّن {age}",
|
||||
"LIVE · updated {age}": "مباشر · حُدّث {age}",
|
||||
"24 h to {day} {time} UTC": "24 ساعة حتى {day} {time} UTC",
|
||||
"<1m ago": "قبل أقل من دقيقة",
|
||||
"FIRE · {frp} MW": "حريق · {frp} MW",
|
||||
"▲ {frp} MW": "▲ {frp} MW",
|
||||
|
||||
@@ -164,6 +164,7 @@
|
||||
"loading...": "lädt...",
|
||||
"KEY REQUIRED": "SCHLÜSSEL ERFORDERLICH",
|
||||
"LIVE · updated {age}": "LIVE · aktualisiert {age}",
|
||||
"24 h to {day} {time} UTC": "24 Std. bis {day} {time} UTC",
|
||||
"loading mapped installation context": "Kontext der kartierten Anlage wird geladen",
|
||||
"MAPPED INSTALLATION": "KARTIERTE ANLAGE",
|
||||
"LAUNCH SITE · {site}": "STARTPLATZ · {site}",
|
||||
|
||||
@@ -737,6 +737,7 @@
|
||||
"KEY REQUIRED": "CLAVE REQUERIDA",
|
||||
"STALE · cached {age}": "DESACTUALIZADO · en caché hace {age}",
|
||||
"LIVE · updated {age}": "EN VIVO · actualizado {age}",
|
||||
"24 h to {day} {time} UTC": "24 h hasta {day} {time} UTC",
|
||||
"<1m ago": "hace <1 min",
|
||||
"FIRE · {frp} MW": "INCENDIO · {frp} MW",
|
||||
"▲ {frp} MW": "▲ {frp} MW",
|
||||
|
||||
@@ -734,6 +734,7 @@
|
||||
"loading...": "chargement…",
|
||||
"KEY REQUIRED": "CLÉ REQUISE",
|
||||
"LIVE · updated {age}": "DIRECT · mis à jour {age}",
|
||||
"24 h to {day} {time} UTC": "24 h jusqu’au {day} {time} UTC",
|
||||
"Source snapshot time unavailable": "Heure de l'instantané source indisponible",
|
||||
"Live data unavailable": "Données en direct indisponibles",
|
||||
"Intel Picture": "Situation renseignement",
|
||||
|
||||
@@ -734,6 +734,7 @@
|
||||
"loading...": "caricamento...",
|
||||
"KEY REQUIRED": "CHIAVE RICHIESTA",
|
||||
"LIVE · updated {age}": "LIVE · aggiornato {age}",
|
||||
"24 h to {day} {time} UTC": "24 h fino al {day} {time} UTC",
|
||||
"Source snapshot time unavailable": "Ora dello snapshot della fonte non disponibile",
|
||||
"Live data unavailable": "Dati live non disponibili",
|
||||
"Intel Picture": "Quadro intelligence",
|
||||
|
||||
@@ -716,6 +716,7 @@
|
||||
"loading...": "読込中…",
|
||||
"KEY REQUIRED": "キーが必要",
|
||||
"LIVE · updated {age}": "ライブ · 更新 {age}",
|
||||
"24 h to {day} {time} UTC": "{day} {time} UTC までの24時間",
|
||||
"Source snapshot time unavailable": "ソースのスナップショット時刻を取得できません",
|
||||
"Live data unavailable": "ライブデータを利用できません",
|
||||
"Intel Picture": "情勢図",
|
||||
|
||||
@@ -732,6 +732,7 @@
|
||||
"loading...": "carregando...",
|
||||
"KEY REQUIRED": "CHAVE NECESSÁRIA",
|
||||
"LIVE · updated {age}": "AO VIVO · atualizado {age}",
|
||||
"24 h to {day} {time} UTC": "24 h até {day} {time} UTC",
|
||||
"Source snapshot time unavailable": "Horário do snapshot da fonte indisponível",
|
||||
"Live data unavailable": "Dados ao vivo indisponíveis",
|
||||
"Intel Picture": "Panorama de inteligência",
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
"loading...": "загрузка...",
|
||||
"KEY REQUIRED": "ТРЕБУЕТСЯ КЛЮЧ",
|
||||
"LIVE · updated {age}": "ОНЛАЙН · обновлено {age}",
|
||||
"24 h to {day} {time} UTC": "24 ч до {day} {time} UTC",
|
||||
"Retrying mapped sites…": "Повторный запрос объектов…",
|
||||
"Fetching mapped sites…": "Загрузка объектов…",
|
||||
"Zoom in to search mapped installations": "Приблизьте, чтобы искать картографированные объекты",
|
||||
|
||||
@@ -742,6 +742,7 @@
|
||||
"loading...": "завантаження...",
|
||||
"KEY REQUIRED": "ПОТРІБЕН КЛЮЧ",
|
||||
"LIVE · updated {age}": "ОНЛАЙН · оновлено {age}",
|
||||
"24 h to {day} {time} UTC": "24 год до {day} {time} UTC",
|
||||
"Source snapshot time unavailable": "Час знімка джерела невідомий",
|
||||
"Live data unavailable": "Дані в реальному часі недоступні",
|
||||
"Intel Picture": "Розвідувальна обстановка",
|
||||
|
||||
@@ -716,6 +716,7 @@
|
||||
"loading...": "加载中…",
|
||||
"KEY REQUIRED": "需要密钥",
|
||||
"LIVE · updated {age}": "实时 · 更新于 {age}",
|
||||
"24 h to {day} {time} UTC": "截至 {day} {time} UTC 的 24 小时",
|
||||
"Source snapshot time unavailable": "来源快照时间不可用",
|
||||
"Live data unavailable": "实时数据不可用",
|
||||
"Low-accuracy score < 2%": "低精度评分 < 2%",
|
||||
|
||||
@@ -711,6 +711,7 @@
|
||||
"loading...": "加載中…",
|
||||
"KEY REQUIRED": "需要密鑰",
|
||||
"LIVE · updated {age}": "實時 · 更新於 {age}",
|
||||
"24 h to {day} {time} UTC": "截至 {day} {time} UTC 的 24 小時",
|
||||
"Source snapshot time unavailable": "來源快照時間不可用",
|
||||
"Live data unavailable": "實時數據不可用",
|
||||
"Low-accuracy score < 2%": "低精度評分 < 2%",
|
||||
|
||||
@@ -39,6 +39,9 @@ export function createIngestion({
|
||||
layerState._keyRequired = false;
|
||||
layerState._error = null;
|
||||
layerState._stale = Boolean(payload?.stale);
|
||||
// A snapshot of the 24 hours to a set time names it; one of the
|
||||
// trailing 24 hours to now does not.
|
||||
layerState._at = Number.isFinite(payload?.at) ? payload.at : null;
|
||||
const previousSelection = layerState._selectedFire;
|
||||
layerState._selectedFire = null;
|
||||
layerState._allFires = adaptFirmsRecords(payload?.fires);
|
||||
|
||||
@@ -127,6 +127,7 @@ export function createLifecycle({
|
||||
layerState._rowControlsListener = null;
|
||||
layerState._cellCount = 0;
|
||||
layerState._lastUpdate = null;
|
||||
layerState._at = null;
|
||||
layerState._keyRequired = false;
|
||||
layerState._stale = false;
|
||||
layerState._error = null;
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
rampTrackGradient,
|
||||
sliderPositionForFrp,
|
||||
} from '../../data/firmsIntensity.js';
|
||||
import { formatNumber, t } from '../../i18n/index.js';
|
||||
import { formatNumber, formatUtcDayMonth, t } from '../../i18n/index.js';
|
||||
import { LOD_LEVELS, REFRESH_INTERVAL_MS } from './policy.js';
|
||||
|
||||
/** Slider resolution: whole-percent positions along the log axis. */
|
||||
@@ -190,8 +190,9 @@ export function createQueries({
|
||||
* Layer stats for the data panel. Degraded feed states surface through
|
||||
* `error` (established qa-failstate pattern: a dead feed must never look
|
||||
* like a healthy empty layer) with a matching human `loadingLabel`:
|
||||
* 'LIVE · updated Xm ago' fresh, 'STALE · cached Xh' when the proxy
|
||||
* served past-TTL cache, 'KEY REQUIRED' keyless.
|
||||
* 'LIVE · updated Xm ago' fresh, '24 h to MM-DD HH:MM UTC' for a
|
||||
* snapshot that ends at a set time (`at`), 'STALE · cached Xh' when the
|
||||
* proxy served past-TTL cache, 'KEY REQUIRED' keyless.
|
||||
*/
|
||||
getStats() {
|
||||
const now = Date.now();
|
||||
@@ -213,6 +214,11 @@ export function createQueries({
|
||||
loadingLabel = staleText;
|
||||
} else if (layerState._error) {
|
||||
loadingLabel = layerState._error;
|
||||
} else if (Number.isFinite(layerState._at)) {
|
||||
loadingLabel = t('24 h to {day} {time} UTC', {
|
||||
day: formatUtcDayMonth(layerState._at),
|
||||
time: new Date(layerState._at).toISOString().slice(11, 16),
|
||||
});
|
||||
} else if (layerState._lastUpdate) {
|
||||
loadingLabel = t('LIVE · updated {age}', {
|
||||
age: components.model.formatAgoMinutes(now - layerState._lastUpdate),
|
||||
@@ -226,6 +232,9 @@ export function createQueries({
|
||||
lastUpdate: layerState._lastUpdate,
|
||||
loading: layerState._loading,
|
||||
stale: layerState._stale,
|
||||
// The end of the 24 hours shown when the snapshot ends at a set time;
|
||||
// null for the trailing 24 hours to now.
|
||||
at: Number.isFinite(layerState._at) ? layerState._at : null,
|
||||
// The machine-readable half of the keyless state, ahead of the human
|
||||
// strings below: without it "no key configured" is indistinguishable
|
||||
// from a broken feed, and the row reads as a fault instead of a step
|
||||
|
||||
@@ -77,6 +77,8 @@ export function createFirmsState({ services, config }) {
|
||||
|
||||
layerState._lastUpdate = null;
|
||||
|
||||
layerState._at = null;
|
||||
|
||||
layerState._currentLodId = null;
|
||||
|
||||
layerState._currentLodIndex = -1;
|
||||
|
||||
Reference in new issue
Block a user