SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
yesterdaylast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
7.8 KB · 94 lines typescript
Raw Blame History
1/**2 * Global infrastructure pulse — deterministic counts over a time window, computed from events and entity dates.3 * Nothing here is scored or weighted: every figure is a count or a sum of published MW figures.4 */5import type { Pulse } from "@dci/core";6import { pg, eventCols, eventJoins, projectLive, type Fragment, type Sql } from "../lib/sql.js";7import { int, num, reqStr, str, type Row } from "../lib/rows.js";8import { round2 } from "../lib/dto.js";9import { toEventDtos } from "./events.js";1011export type PulseWindow = Pulse["window"];12const WINDOW_INTERVAL: Record<PulseWindow, string> = { "24h": "24 hours", "7d": "7 days", "30d": "30 days" };1314export const PULSE_METHODOLOGY = "Deterministic counts over the window: events with review_status ≠ rejected; projects by created_at / construction_started_on; facilities by opened_on / first_seen; MW figures are sums of published site-scoped values on the records (no estimates). operatorsNewMarkets = operators whose first facility or project in a metro (or country when no metro) falls inside the window and who had none there before. Hidden (false-positive) and merged projects are excluded.";1516function partialDate(sql: Sql, col: Fragment, fallback: Fragment): Fragment {17  return sql`(case when ${col} ~ '^\\d{4}-\\d{2}-\\d{2}' then ${col}::date when ${col} ~ '^\\d{4}-\\d{2}$' then (${col} || '-01')::date when ${col} ~ '^\\d{4}$' then (${col} || '-01-01')::date else ${fallback} end)`;18}1920export async function pulse(window: PulseWindow = "24h"): Promise<Pulse> {21  const sql = pg();22  const since = sql`(now() - ${WINDOW_INTERVAL[window]}::interval)`;23  const sinceDate = sql`(now() - ${WINDOW_INTERVAL[window]}::interval)::date`;24  const conDate = partialDate(sql, sql`p.construction_started_on`, sql`null::date`);25  const openedDate = partialDate(sql, sql`f.opened_on`, sql`null::date`);26  const fFirst = sql`coalesce(${partialDate(sql, sql`f.opened_on`, sql`null::date`)}, f.first_seen::date)`;27  const pFirst = partialDate(sql, sql`p.announced_on`, sql`p.created_at::date`);28  const [ev, pr, fa, markets, major, byCountry, byOperator, sinceRow] = await Promise.all([29    sql<Row[]>`select count(*)::int as total,30        count(*) filter (where e.event_type = 'facility_opened')::int as opened_ev,31        count(*) filter (where e.event_type = 'cloud_region_announced')::int as cloud_ann,32        count(*) filter (where e.event_type = 'power_agreement')::int as power,33        count(*) filter (where e.event_type in ('grid_constraint', 'utility_event'))::int as grid,34        count(*) filter (where e.event_type = 'acquisition')::int as acq,35        count(*) filter (where e.event_type = 'investment_announced')::int as fin,36        count(*) filter (where e.event_type = 'facility_discovered')::int as discovered,37        count(*) filter (where e.event_type in ('capacity_changed', 'planned_capacity_changed'))::int as cap,38        count(distinct e.project_id) filter (where e.project_id is not null and (e.event_type = 'construction_started' or (e.event_type = 'project_status_changed' and e.new_value::text ilike '%under_construction%')))::int as con_ev39      from events e where e.review_status <> 'rejected' and e.detected_at >= ${since}`,40    sql<Row[]>`select41        count(*) filter (where p.created_at >= ${since})::int as new_n, sum(p.planned_mw) filter (where p.created_at >= ${since})::float as new_mw,42        count(*) filter (where ${conDate} >= ${sinceDate})::int as con_n, sum(p.planned_mw) filter (where ${conDate} >= ${sinceDate})::float as con_mw43      from projects p where ${projectLive(sql)}`,44    sql<Row[]>`select count(*) filter (where ${openedDate} >= ${sinceDate} and f.status in ('operational', 'partially_operational', 'expansion'))::int as opened, count(*) filter (where f.first_seen >= ${since})::int as indexed from facilities f where f.merged_into is null`,45    sql<Row[]>`46      with entries as (47        select f.operator_id, f.metro_id, f.country_iso2, ${fFirst} as d from facilities f where f.merged_into is null and f.operator_id is not null and (f.metro_id is not null or f.country_iso2 is not null)48        union all49        select p.operator_id, p.metro_id, p.country_iso2, ${pFirst} from projects p where ${projectLive(sql)} and p.operator_id is not null and (p.metro_id is not null or p.country_iso2 is not null)50      ), firsts as (51        select operator_id, metro_id, country_iso2, min(d) as first_d from entries group by 1, 2, 352      )53      select o.id, o.slug, o.name, m.id as met_id, m.slug as met_slug, m.name as met_name, fs.country_iso2, fs.first_d54      from firsts fs join operators o on o.id = fs.operator_id left join metros m on m.id = fs.metro_id55      where fs.first_d >= ${sinceDate}56        and not exists (select 1 from firsts f2 where f2.operator_id = fs.operator_id and f2.first_d < ${sinceDate} and (f2.metro_id is not distinct from fs.metro_id) and (fs.metro_id is not null or f2.country_iso2 is not distinct from fs.country_iso2))57      order by fs.first_d desc, o.name limit 25`,58    sql<Row[]>`select ${eventCols(sql)} from events e ${eventJoins(sql)} where e.review_status <> 'rejected' and e.detected_at >= ${since} and e.significance >= 75 order by e.significance desc, e.detected_at desc limit 15`,59    sql<Row[]>`select c.iso2, c.name, c.slug, coalesce(ev.n, 0) as events, coalesce(pj.n, 0) as new_projects60      from countries c61      left join (select country_iso2, count(*)::int as n from events where review_status <> 'rejected' and detected_at >= ${since} and country_iso2 is not null group by 1) ev on ev.country_iso2 = c.iso262      left join (select p.country_iso2, count(*)::int as n from projects p where ${projectLive(sql)} and p.created_at >= ${since} and p.country_iso2 is not null group by 1) pj on pj.country_iso2 = c.iso263      where coalesce(ev.n, 0) > 0 or coalesce(pj.n, 0) > 0 order by events desc, new_projects desc, c.name limit 15`,64    sql<Row[]>`select o.id, o.slug, o.name, coalesce(ev.n, 0) as events, coalesce(pj.n, 0) as new_projects65      from operators o66      left join (select operator_id, count(*)::int as n from events where review_status <> 'rejected' and detected_at >= ${since} and operator_id is not null group by 1) ev on ev.operator_id = o.id67      left join (select p.operator_id, count(*)::int as n from projects p where ${projectLive(sql)} and p.created_at >= ${since} and p.operator_id is not null group by 1) pj on pj.operator_id = o.id68      where coalesce(ev.n, 0) > 0 or coalesce(pj.n, 0) > 0 order by events desc, new_projects desc, o.name limit 15`,69    sql<Row[]>`select ${since} as since`,70  ]);71  const e = ev[0] ?? {}, p = pr[0] ?? {}, f = fa[0] ?? {};72  return {73    window,74    since: new Date(String(sinceRow[0]?.since ?? Date.now())).toISOString(),75    newProjects: int(p.new_n),76    projectsEnteredConstruction: Math.max(int(p.con_n), int(e.con_ev)),77    facilitiesOpened: Math.max(int(f.opened), int(e.opened_ev)),78    newlyAnnouncedMw: round2(num(p.new_mw)),79    constructionStartedMw: round2(num(p.con_mw)),80    operatorsNewMarkets: markets.map((r) => ({ operator: { id: reqStr(r.id), slug: reqStr(r.slug), name: reqStr(r.name) }, market: r.met_id ? { id: reqStr(r.met_id), slug: reqStr(r.met_slug), name: reqStr(r.met_name) } : null, countryIso2: str(r.country_iso2) })),81    cloudRegionsAnnounced: int(e.cloud_ann),82    powerAgreements: int(e.power),83    gridConstraintEvents: int(e.grid),84    acquisitions: int(e.acq),85    financingEvents: int(e.fin),86    newFacilitiesIndexed: Math.max(int(f.indexed), int(e.discovered)),87    capacityChanges: int(e.cap),88    eventsTotal: int(e.total),89    majorEvents: await toEventDtos(major),90    byCountry: byCountry.map((r) => ({ iso2: reqStr(r.iso2), name: reqStr(r.name), slug: reqStr(r.slug), events: int(r.events), newProjects: int(r.new_projects) })),91    byOperator: byOperator.map((r) => ({ id: reqStr(r.id), slug: reqStr(r.slug), name: reqStr(r.name), events: int(r.events), newProjects: int(r.new_projects) })),92  };93}94