placementt-core 1.400.966 → 1.400.968

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3348,4 +3348,136 @@ export type LeaverGroup = {
3348
3348
  };
3349
3349
  };
3350
3350
  };
3351
+ /** Keys identifying each importable LMI source. Used for lmiMeta/status and
3352
+ * for addressing individual sync callables from the admin cards. */
3353
+ export type LmiSourceKey = "socDescriptions" | "asheNational" | "asheRegional" | "nomisEmployment" | "standards" | "titleAliases";
3354
+ /** One searchable job-title alias pointing into a 4-digit SOC profile, from the
3355
+ * ONS SOC 2020 Volume 2 coding index (~30k titles). Stored in lmiTitleAliases
3356
+ * so a student searching "physiotherapist" or "ethical hacker" resolves to the
3357
+ * right occupation even though those aren't SOC unit-group names. */
3358
+ export type LmiTitleAlias = {
3359
+ title: string;
3360
+ search: string;
3361
+ keywords: string[];
3362
+ soc: string;
3363
+ };
3364
+ /** National full-time gross annual pay percentiles for an occupation (whole
3365
+ * pounds). Any percentile ONS suppressed ("x" / "..") is stored as null. */
3366
+ export type LmiSalaryPercentiles = {
3367
+ p10: number | null;
3368
+ p25: number | null;
3369
+ median: number | null;
3370
+ p75: number | null;
3371
+ p90: number | null;
3372
+ };
3373
+ /** Median full-time gross annual pay for an occupation within one ONS work
3374
+ * region / UK nation (ASHE Table 15). */
3375
+ export type LmiRegionalSalary = {
3376
+ median: number | null;
3377
+ regionName: string;
3378
+ };
3379
+ /** Employment (job count) for an occupation within one region, from the Nomis
3380
+ * Annual Population Survey. Keyed by the parent 2-digit SOC. */
3381
+ export type LmiRegionalEmployment = {
3382
+ jobs: number | null;
3383
+ regionName: string;
3384
+ };
3385
+ /** A linked apprenticeship standard (IfATE / Skills England). Doubles as
3386
+ * "routes in" content for the future student-facing career profile. */
3387
+ export type LmiApprenticeshipStandard = {
3388
+ name: string;
3389
+ level: number;
3390
+ reference: string;
3391
+ };
3392
+ /** One document per 4-digit SOC 2020 occupation: lmiOccupations/{soc4Code}.
3393
+ * Each source owns and merge-writes only its own fields, so a partial sync
3394
+ * never clobbers data written by another source. */
3395
+ export type LmiOccupation = {
3396
+ soc: string;
3397
+ title: string;
3398
+ description: string;
3399
+ entryRoutes?: string;
3400
+ salary?: {
3401
+ year: number;
3402
+ national: LmiSalaryPercentiles;
3403
+ byRegion: {
3404
+ [regionCode: string]: LmiRegionalSalary;
3405
+ };
3406
+ };
3407
+ employment?: {
3408
+ year: number;
3409
+ soc2: string;
3410
+ byRegion: {
3411
+ [regionCode: string]: LmiRegionalEmployment;
3412
+ };
3413
+ };
3414
+ skills?: string[];
3415
+ apprenticeshipStandards?: LmiApprenticeshipStandard[];
3416
+ lastUpdated: string;
3417
+ };
3418
+ /** Editable per-source configuration (lmiMeta/config) so next year's refresh
3419
+ * needs only a URL/edition change in the admin card — no redeploy. */
3420
+ export type LmiMetaConfig = {
3421
+ sources: {
3422
+ asheTable14: {
3423
+ url: string;
3424
+ edition: number;
3425
+ };
3426
+ asheTable15: {
3427
+ url: string;
3428
+ edition: number;
3429
+ };
3430
+ socVolume1: {
3431
+ url: string;
3432
+ };
3433
+ nomis: {
3434
+ datasetId: string;
3435
+ geography: string;
3436
+ };
3437
+ standards?: {
3438
+ url: string;
3439
+ };
3440
+ titleAliases?: {
3441
+ url: string;
3442
+ };
3443
+ };
3444
+ /** Optional per-source note surfaced in the admin card, e.g. to flag a URL
3445
+ * that could not be verified and should be checked/corrected by an admin. */
3446
+ notes?: {
3447
+ [K in LmiSourceKey]?: string;
3448
+ };
3449
+ };
3450
+ export type LmiSyncState = "idle" | "running" | "success" | "error";
3451
+ /** Per-source sync state read by the admin status cards (lmiMeta/status). */
3452
+ export type LmiSyncStatus = {
3453
+ lastRun: string;
3454
+ lastSuccess?: string;
3455
+ state: LmiSyncState;
3456
+ error?: string;
3457
+ counts?: {
3458
+ occupationsWritten: number;
3459
+ rowsParsed: number;
3460
+ };
3461
+ };
3462
+ /** lmiMeta/status document shape — one LmiSyncStatus per source key. */
3463
+ export type LmiMetaStatus = Partial<Record<LmiSourceKey, LmiSyncStatus>>;
3464
+ /** Phase 3 — proposed title-based matches between apprenticeship standards and
3465
+ * occupations, written to lmiMeta/standardsMatchReview for human review when
3466
+ * the standards data carries no SOC code to link on directly. */
3467
+ export type LmiStandardsMatchReview = {
3468
+ generatedAt: string;
3469
+ unmatchedStandards?: {
3470
+ reference: string;
3471
+ name: string;
3472
+ level: number;
3473
+ }[];
3474
+ proposedMatches: {
3475
+ soc: string;
3476
+ occupationTitle: string;
3477
+ standardReference: string;
3478
+ standardName: string;
3479
+ standardLevel: number;
3480
+ confidence: number;
3481
+ }[];
3482
+ };
3351
3483
  export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "private": false,
3
3
  "name": "placementt-core",
4
4
  "author": "Placementt",
5
- "version": "1.400.966",
5
+ "version": "1.400.968",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
8
8
  "scripts": {
@@ -3319,4 +3319,132 @@ export type LeaverGroup = {
3319
3319
  resultsDay?: {sentAt?: string, reminderSentAt?: string, totalSent?: number, totalResponded?: number},
3320
3320
  christmas?: {sentAt?: string, reminderSentAt?: string, totalSent?: number, totalResponded?: number},
3321
3321
  },
3322
+ }
3323
+
3324
+ /* ------------------------------------------------------------------------- *
3325
+ * Labour Market Information (LMI) *
3326
+ * *
3327
+ * Free, open-data occupation intelligence used to power career *
3328
+ * recommendations. Populated server-side by the lmiImport callables from *
3329
+ * ONS (ASHE Tables 14 & 15, SOC 2020 Volume 1), the Nomis API and *
3330
+ * (Phase 3) IfATE / Skills England apprenticeship standards. *
3331
+ * Firestore: lmiOccupations/{soc4}, lmiMeta/config, lmiMeta/status. *
3332
+ * ------------------------------------------------------------------------- */
3333
+
3334
+ /** Keys identifying each importable LMI source. Used for lmiMeta/status and
3335
+ * for addressing individual sync callables from the admin cards. */
3336
+ export type LmiSourceKey =
3337
+ "socDescriptions"|"asheNational"|"asheRegional"|"nomisEmployment"|
3338
+ "standards"|"titleAliases";
3339
+
3340
+ /** One searchable job-title alias pointing into a 4-digit SOC profile, from the
3341
+ * ONS SOC 2020 Volume 2 coding index (~30k titles). Stored in lmiTitleAliases
3342
+ * so a student searching "physiotherapist" or "ethical hacker" resolves to the
3343
+ * right occupation even though those aren't SOC unit-group names. */
3344
+ export type LmiTitleAlias = {
3345
+ title: string, // display title, natural word order
3346
+ search: string, // sanitised lowercase title for prefix search
3347
+ keywords: string[], // sanitised tokens for word (array-contains) search
3348
+ soc: string, // 4-digit SOC 2020 the title codes to
3349
+ }
3350
+
3351
+ /** National full-time gross annual pay percentiles for an occupation (whole
3352
+ * pounds). Any percentile ONS suppressed ("x" / "..") is stored as null. */
3353
+ export type LmiSalaryPercentiles = {
3354
+ p10: number|null,
3355
+ p25: number|null,
3356
+ median: number|null,
3357
+ p75: number|null,
3358
+ p90: number|null,
3359
+ }
3360
+
3361
+ /** Median full-time gross annual pay for an occupation within one ONS work
3362
+ * region / UK nation (ASHE Table 15). */
3363
+ export type LmiRegionalSalary = {
3364
+ median: number|null,
3365
+ regionName: string,
3366
+ }
3367
+
3368
+ /** Employment (job count) for an occupation within one region, from the Nomis
3369
+ * Annual Population Survey. Keyed by the parent 2-digit SOC. */
3370
+ export type LmiRegionalEmployment = {
3371
+ jobs: number|null,
3372
+ regionName: string,
3373
+ }
3374
+
3375
+ /** A linked apprenticeship standard (IfATE / Skills England). Doubles as
3376
+ * "routes in" content for the future student-facing career profile. */
3377
+ export type LmiApprenticeshipStandard = {
3378
+ name: string,
3379
+ level: number,
3380
+ reference: string,
3381
+ }
3382
+
3383
+ /** One document per 4-digit SOC 2020 occupation: lmiOccupations/{soc4Code}.
3384
+ * Each source owns and merge-writes only its own fields, so a partial sync
3385
+ * never clobbers data written by another source. */
3386
+ export type LmiOccupation = {
3387
+ soc: string, // "2236" — 4-digit SOC 2020, also the doc id
3388
+ title: string,
3389
+ description: string,
3390
+ entryRoutes?: string, // qualifications / entry text from SOC vol 1
3391
+ salary?: {
3392
+ year: number, // ASHE edition year
3393
+ national: LmiSalaryPercentiles,
3394
+ byRegion: {[regionCode: string]: LmiRegionalSalary},
3395
+ },
3396
+ employment?: {
3397
+ year: number,
3398
+ soc2: string, // parent 2-digit code the stats are keyed on
3399
+ byRegion: {[regionCode: string]: LmiRegionalEmployment},
3400
+ },
3401
+ skills?: string[], // Phase 3 — condensed IfATE/SE KSB skill names
3402
+ apprenticeshipStandards?: LmiApprenticeshipStandard[], // Phase 3
3403
+ lastUpdated: string, // ISO timestamp
3404
+ }
3405
+
3406
+ /** Editable per-source configuration (lmiMeta/config) so next year's refresh
3407
+ * needs only a URL/edition change in the admin card — no redeploy. */
3408
+ export type LmiMetaConfig = {
3409
+ sources: {
3410
+ asheTable14: {url: string, edition: number},
3411
+ asheTable15: {url: string, edition: number},
3412
+ socVolume1: {url: string},
3413
+ nomis: {datasetId: string, geography: string},
3414
+ standards?: {url: string}, // Phase 3 — IfATE/Skills England endpoint
3415
+ titleAliases?: {url: string}, // ONS SOC 2020 Vol 2 coding index
3416
+ },
3417
+ /** Optional per-source note surfaced in the admin card, e.g. to flag a URL
3418
+ * that could not be verified and should be checked/corrected by an admin. */
3419
+ notes?: {[K in LmiSourceKey]?: string},
3420
+ }
3421
+
3422
+ export type LmiSyncState = "idle"|"running"|"success"|"error";
3423
+
3424
+ /** Per-source sync state read by the admin status cards (lmiMeta/status). */
3425
+ export type LmiSyncStatus = {
3426
+ lastRun: string,
3427
+ lastSuccess?: string,
3428
+ state: LmiSyncState,
3429
+ error?: string,
3430
+ counts?: {occupationsWritten: number, rowsParsed: number},
3431
+ }
3432
+
3433
+ /** lmiMeta/status document shape — one LmiSyncStatus per source key. */
3434
+ export type LmiMetaStatus = Partial<Record<LmiSourceKey, LmiSyncStatus>>;
3435
+
3436
+ /** Phase 3 — proposed title-based matches between apprenticeship standards and
3437
+ * occupations, written to lmiMeta/standardsMatchReview for human review when
3438
+ * the standards data carries no SOC code to link on directly. */
3439
+ export type LmiStandardsMatchReview = {
3440
+ generatedAt: string,
3441
+ unmatchedStandards?: {reference: string, name: string, level: number}[],
3442
+ proposedMatches: {
3443
+ soc: string,
3444
+ occupationTitle: string,
3445
+ standardReference: string,
3446
+ standardName: string,
3447
+ standardLevel: number,
3448
+ confidence: number, // 0-1 heuristic score
3449
+ }[],
3322
3450
  }