mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-07-22 08:48:34 +00:00
68 lines
2.9 KiB
TypeScript
68 lines
2.9 KiB
TypeScript
import { pgTable, uuid, text, timestamp, jsonb, boolean } from "drizzle-orm/pg-core";
|
|
import { sql } from "drizzle-orm";
|
|
|
|
// Shape of the multi_entry_summary jsonb (ADR-0004). Co-located with the column
|
|
// so the schema is self-contained; the detection logic in
|
|
// src/lib/bulkUpload/multiEntry.ts imports these.
|
|
export interface MultiEntryEntry {
|
|
raw: string;
|
|
description: string;
|
|
}
|
|
export interface MultiEntryColumn {
|
|
field: string;
|
|
header: string;
|
|
entries: MultiEntryEntry[];
|
|
}
|
|
export interface MultiEntrySample {
|
|
address: string;
|
|
count: number;
|
|
columns: MultiEntryColumn[];
|
|
}
|
|
export interface MultiEntrySummary {
|
|
multiValuedFields: string[];
|
|
countDistribution: Record<string, number>;
|
|
largestCount: number;
|
|
// Step 1 (verify) sample: the largest-count row when multi-entry, else the
|
|
// first classified row. `null` ⇒ nothing to verify.
|
|
sample: MultiEntrySample | null;
|
|
// Step 2 (order): one sample per distinct entry-count ≥ 2 present in the file,
|
|
// keyed by count. Each count needs its OWN confirmed permutation — a smaller
|
|
// count's ordering can't be derived from a larger one (ADR-0004, amended
|
|
// 2026-06-05). Absent on uploads detected before that amendment.
|
|
samplesByCount?: Record<string, MultiEntrySample>;
|
|
}
|
|
|
|
// User-confirmed building-part ordering (ADR-0004, amended 2026-06-05). Keyed by
|
|
// entry-count: a permutation is captured for EVERY distinct count ≥ 2 in the
|
|
// file (the v2 fact layer can't derive one count's order from another).
|
|
// permutations[count][k] = the 0-based file position holding building part k,
|
|
// where 0 = Main building, 1..N-1 = Extension 1..N-1.
|
|
// e.g. { "2": [1, 0] } => for 2-part rows the main building is file position 1.
|
|
export interface MultiEntryOrdering {
|
|
permutations: Record<string, number[]>;
|
|
// True once EVERY detected count ≥ 2 has a permutation; gates Finalise when the
|
|
// upload is multi-entry.
|
|
confirmed: boolean;
|
|
}
|
|
|
|
export const bulkAddressUploads = pgTable("bulk_address_uploads", {
|
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
portfolioId: text("portfolio_id").notNull(),
|
|
userId: text("user_id").notNull(),
|
|
s3Bucket: text("s3_bucket").notNull(),
|
|
s3Key: text("s3_key").notNull(),
|
|
filename: text("filename").notNull(),
|
|
status: text("status").notNull().default("ready_for_processing"),
|
|
sourceHeaders: text("source_headers").array().notNull().default(sql`'{}'`),
|
|
columnMapping: jsonb("column_mapping").$type<Record<string, string>>(),
|
|
multiEntrySummary: jsonb("multi_entry_summary").$type<MultiEntrySummary>(),
|
|
multiEntryOrdering: jsonb("multi_entry_ordering").$type<MultiEntryOrdering>(),
|
|
verifyAck: boolean("verify_ack").notNull().default(false),
|
|
taskId: uuid("task_id"),
|
|
combinedOutputS3Uri: text("combined_output_s3_uri"),
|
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
|
.notNull()
|
|
.defaultNow()
|
|
.$onUpdate(() => new Date()),
|
|
});
|