65 lines
2.9 KiB
TypeScript
65 lines
2.9 KiB
TypeScript
// The tables of the documents module (spec §16). Two, both project-owned.
|
|
//
|
|
// `documents_document` is one uploaded file's processing state. `documents_line` holds only what a
|
|
// person has APPROVED: an extracted line lives as a proposal (kit-proposals) until somebody
|
|
// decides it, and the proposal's writer inserts the line in the approving transaction. That is how
|
|
// "nothing is exported without approval" is a property of the schema and not of a filter: the
|
|
// export reads this table, and nothing but an approval writes to it.
|
|
|
|
import { defineEntity, idSchema, instantSchema } from "@conusai-kit/data"
|
|
import { z } from "zod"
|
|
import { DOCUMENT_STATUSES, FAILURE_CODES, PAGE_CLASSES } from "./constants.js"
|
|
|
|
export const documents = defineEntity({
|
|
name: "document",
|
|
table: "documents_document",
|
|
tenancy: "project",
|
|
fields: {
|
|
/** The row of the files module this document was made from: an opaque id, never a foreign key (spec §4.4). */
|
|
fileId: idSchema,
|
|
filename: z.string().min(1).max(255),
|
|
status: z.enum(DOCUMENT_STATUSES),
|
|
/** The platform's OCR job id, recorded as soon as the job is accepted so a retry resumes it. */
|
|
ocrJobId: z.number().int().nonnegative().nullable(),
|
|
pageCount: z.number().int().nonnegative().nullable(),
|
|
/** How many lines the extraction proposed; the proposals themselves are the reviewer's queue. */
|
|
proposedCount: z.number().int().nonnegative().nullable(),
|
|
failureCode: z.enum(FAILURE_CODES).nullable(),
|
|
/** The pages the extraction left out, with the class that excluded each, so a reviewer sees what was not read. Null until extracted. */
|
|
skippedPages: z
|
|
.array(z.object({ page: z.number().int().min(1), pageClass: z.enum(PAGE_CLASSES) }).strict())
|
|
.nullable(),
|
|
finishedAt: instantSchema.nullable(),
|
|
},
|
|
// One document per file: the job that creates it is retried, and a second row would be a second queue.
|
|
unique: [["fileId"]],
|
|
indexes: [["status"]],
|
|
})
|
|
|
|
export const lines = defineEntity({
|
|
name: "line",
|
|
table: "documents_line",
|
|
tenancy: "project",
|
|
fields: {
|
|
documentId: idSchema,
|
|
/** The approved proposal this line came from: its provenance, and the reason a line is written once. */
|
|
proposalId: idSchema,
|
|
label: z.string().min(1).max(200),
|
|
value: z.string().min(1).max(2_000),
|
|
/** 1-based page of the source document. */
|
|
page: z.number().int().min(1),
|
|
/** Reading order within the document: the export's order, which a creation time cannot give. */
|
|
position: z.number().int().min(0),
|
|
/** The words the value was read from, verbatim. */
|
|
quote: z.string().min(1).max(2_000),
|
|
/** True when a reviewer changed the label or value before it was approved. */
|
|
edited: z.boolean(),
|
|
approvedBy: z.string().min(1).max(128),
|
|
},
|
|
unique: [["proposalId"]],
|
|
indexes: [["documentId"]],
|
|
})
|
|
|
|
/** Every table this module owns, in the order a migration creates them. */
|
|
export const DOCUMENTS_ENTITIES = Object.freeze([documents, lines])
|