SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
3 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
2.8 KB · 39 lines typescript
Raw Blame History
1import { parseAllMw } from "@dci/core";23/**4 * MW figures that describe the announced SITE, as opposed to a company's portfolio, a market statistic or a regional5 * total. Shared by the page classifier (`classifyPage().mw`) and the news announcement extractor, so a press release6 * saying "brings the company's footprint to 1.4 GW" never puts 1 400 MW on a project or a facility.7 */89/** Portfolio / company-wide context BEFORE a MW figure — not the size of the announced site. */10export const PORTFOLIO_BEFORE_RE = /\b(total(?:l?ing)?|across|portfolio|combined|footprint|pipeline|capacity to(?: over| more than)?|to over|to more than|brings?(?: its| the| total)?[^.]{0,60}(?:to|capacity)|bringing[^.]{0,60}to|taking[^.]{0,60}to|now (?:has|operates|owns|manages)|operates?(?: over| more than)?|owns?(?: over| more than)?|manages?(?: over| more than)?|(?:more than|over) \d+ (?:data cent(?:er|re)s?|facilities|sites|campuses)[^.]{0,20}|globally|worldwide|company-?wide|nationwide|in (?:asia|apj|japan|europe|emea|the region|australia|the us|the u\.s\.|the country|india|latin america|the americas|the uk|the middle east)|of (?:total )?capacity (?:in|across))\s*(?:of\s*)?$/i;11/** Portfolio / company-wide context AFTER a MW figure. */12export const PORTFOLIO_AFTER_RE = /^\s*(?:MW|GW|megawatts?|gigawatts?)?\s*(?:across|portfolio|globally|worldwide|company-?wide|nationwide|in (?:asia|apj|japan|europe|emea|the region|australia|the us|the country|india|latin america|the americas|the uk|the middle east)|of (?:total )?(?:capacity |power )?(?:across|in operation|under (?:management|development)|planned)|of (?:contracted|committed|operational|total) (?:capacity|power))\b/i;13const MW_TOKEN_RE = /(\d{1,3}(?:[.,]\d{3})+|\d+(?:[.,]\d+)?)\+?\s?(gigawatts?|gw|megawatts?|mw)\b/gi;1415/** Is the MW token at `start`…`end` of `scope` a portfolio / company-wide figure rather than the site's? */16export function isPortfolioFigure(scope: string, start: number, end: number): boolean {17  const before = scope.slice(Math.max(0, start - 100), start);18  const after = scope.slice(end, end + 40);19  return PORTFOLIO_BEFORE_RE.test(before) || PORTFOLIO_AFTER_RE.test(after);20}2122/**23 * MW figures in a scope that describe the announced site (largest first, deduplicated, 0 < MW ≤ 20 000), skipping24 * company-wide totals and market statistics. When every figure in the scope is a portfolio total, none is returned —25 * the caller moves to the next scope.26 */27export function projectMwFigures(scope: string): number[] {28  const site: number[] = [];29  MW_TOKEN_RE.lastIndex = 0;30  for (const m of scope.matchAll(MW_TOKEN_RE)) {31    const start = m.index ?? 0;32    const value = parseAllMw(m[0])[0];33    if (value == null || value <= 0 || value > 20_000) continue;34    if (isPortfolioFigure(scope, start, start + m[0].length)) continue;35    site.push(value);36  }37  return [...new Set(site)].sort((a, b) => b - a);38}39