gts-x / src /schema /schema.ts
JAMES HAN
GTS-X: Full HITL editor + QC + file converter for HuggingFace Spaces
c273232
Raw
History Blame Contribute Delete
13.4 kB
import {
pgTable,
text,
varchar,
boolean,
integer,
timestamp,
jsonb,
uuid,
pgEnum,
real,
index,
} from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { relations } from "drizzle-orm";
// ─── Enums ──────────────────────────────────────────────────────────────────
export const jobStatusEnum = pgEnum("job_status", [
"pending",
"assigned",
"in_progress",
"validating",
"review",
"completed",
"cancelled",
]);
export const assignmentTypeEnum = pgEnum("assignment_type", [
"auto",
"manual",
"reassigned",
]);
export const acceptanceStatusEnum = pgEnum("acceptance_status", [
"pending",
"accepted",
"declined",
]);
export const qcStatusEnum = pgEnum("qc_status", [
"none",
"queued",
"running",
"completed",
"failed",
]);
export const changeCategoryEnum = pgEnum("change_category", [
"timing_fix",
"translation_fix",
"style_correction",
"cpl_fix",
"split_merge",
"other",
]);
export const errorTypeEnum = pgEnum("error_type", [
"input_error",
"cicd_needed",
"style_preference",
"false_positive",
]);
export const affectedInputEnum = pgEnum("affected_input", [
"neon",
"alto_curated",
"lsx_prompt",
"lsx_logic",
"arc_config",
]);
export const reviewStatusEnum = pgEnum("review_status", [
"pending",
"approved",
"rejected",
]);
// ─── General Settings (existing) ────────────────────────────────────────────
export const generalSettings = pgTable("general_settings", {
id: varchar("id").primaryKey().default("general"),
displayName: text("display_name").notNull().default("Studio Admin"),
contactEmail: text("contact_email")
.notNull()
.default("admin@cpxstudio.com"),
primaryRole: text("primary_role").default("Studio Manager"),
twoFactorEnabled: boolean("two_factor_enabled").notNull().default(false),
});
export const insertGeneralSettingsSchema = createInsertSchema(
generalSettings
).omit({ id: true });
export const updateGeneralSettingsSchema = insertGeneralSettingsSchema.partial();
export type InsertGeneralSettings = z.infer<
typeof insertGeneralSettingsSchema
>;
export type UpdateGeneralSettings = z.infer<
typeof updateGeneralSettingsSchema
>;
export type GeneralSettings = typeof generalSettings.$inferSelect;
// ─── Resources ──────────────────────────────────────────────────────────────
export const resources = pgTable("resources", {
id: uuid("id").primaryKey().defaultRandom(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
atlasResourceId: text("atlas_resource_id"),
branchId: text("branch_id"),
active: boolean("active").notNull().default(true),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().defaultNow(),
});
export const insertResourceSchema = createInsertSchema(resources).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export type InsertResource = z.infer<typeof insertResourceSchema>;
export type Resource = typeof resources.$inferSelect;
// ─── Resource Skill Sets ────────────────────────────────────────────────────
export const resourceSkillSets = pgTable("resource_skill_sets", {
id: uuid("id").primaryKey().defaultRandom(),
resourceId: uuid("resource_id")
.notNull()
.references(() => resources.id, { onDelete: "cascade" }),
platform: text("platform").notNull().default("GTS_X"),
grade: text("grade"),
sourceLanguageId: text("source_language_id"),
targetLanguageId: text("target_language_id"),
unitPrice: real("unit_price"),
currencyId: text("currency_id"),
active: boolean("active").notNull().default(true),
});
// ─── Jobs ───────────────────────────────────────────────────────────────────
export const jobs = pgTable(
"jobs",
{
id: uuid("id").primaryKey().defaultRandom(),
arcJobId: text("arc_job_id"),
atlasTaskId: text("atlas_task_id"),
orderId: text("order_id"),
sourceLanguage: text("source_language").notNull(),
targetLanguage: text("target_language").notNull(),
clientId: text("client_id"),
status: jobStatusEnum("status").notNull().default("pending"),
inputS3Key: text("input_s3_key"),
currentS3Key: text("current_s3_key"),
// Store the initial parsed document as JSON for diff comparison
initialDocument: jsonb("initial_document"),
// Store the current working document
currentDocument: jsonb("current_document"),
metadata: jsonb("metadata").$type<Record<string, unknown>>(),
langfuseTraceId: text("langfuse_trace_id"),
videoS3Key: text("video_s3_key"),
qcStatus: qcStatusEnum("qc_status").notNull().default("none"),
qcResults: jsonb("qc_results"),
techQcPassed: boolean("tech_qc_passed").notNull().default(false),
rationaleValidated: boolean("rationale_validated").notNull().default(false),
qcReviewedAt: timestamp("qc_reviewed_at"),
lockedAt: timestamp("locked_at"),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().defaultNow(),
},
(table) => [
index("idx_jobs_status").on(table.status),
index("idx_jobs_arc_job_id").on(table.arcJobId),
index("idx_jobs_client_id").on(table.clientId),
]
);
export const insertJobSchema = createInsertSchema(jobs).omit({
id: true,
createdAt: true,
updatedAt: true,
});
export type InsertJob = z.infer<typeof insertJobSchema>;
export type Job = typeof jobs.$inferSelect;
// ─── Job Assignments ────────────────────────────────────────────────────────
export const jobAssignments = pgTable(
"job_assignments",
{
id: uuid("id").primaryKey().defaultRandom(),
jobId: uuid("job_id")
.notNull()
.references(() => jobs.id, { onDelete: "cascade" }),
resourceId: uuid("resource_id")
.notNull()
.references(() => resources.id),
segmentIndex: integer("segment_index").notNull().default(0),
assignmentType: assignmentTypeEnum("assignment_type")
.notNull()
.default("manual"),
acceptanceStatus: acceptanceStatusEnum("acceptance_status")
.notNull()
.default("pending"),
acceptanceToken: text("acceptance_token"),
acceptanceExpiresAt: timestamp("acceptance_expires_at"),
deadline: timestamp("deadline"),
startedAt: timestamp("started_at"),
completedAt: timestamp("completed_at"),
createdAt: timestamp("created_at").notNull().defaultNow(),
},
(table) => [
index("idx_assignments_job").on(table.jobId),
index("idx_assignments_resource").on(table.resourceId),
index("idx_assignments_status").on(table.acceptanceStatus),
]
);
export const insertJobAssignmentSchema = createInsertSchema(
jobAssignments
).omit({ id: true, createdAt: true });
export type InsertJobAssignment = z.infer<typeof insertJobAssignmentSchema>;
export type JobAssignment = typeof jobAssignments.$inferSelect;
// ─── Edit Sessions ──────────────────────────────────────────────────────────
export const editSessions = pgTable(
"edit_sessions",
{
id: uuid("id").primaryKey().defaultRandom(),
jobId: uuid("job_id")
.notNull()
.references(() => jobs.id, { onDelete: "cascade" }),
resourceId: uuid("resource_id")
.notNull()
.references(() => resources.id),
startedAt: timestamp("started_at").notNull().defaultNow(),
endedAt: timestamp("ended_at"),
linesChanged: integer("lines_changed").notNull().default(0),
timeSpentSeconds: integer("time_spent_seconds").notNull().default(0),
},
(table) => [index("idx_sessions_job").on(table.jobId)]
);
export const insertEditSessionSchema = createInsertSchema(editSessions).omit({
id: true,
});
export type InsertEditSession = z.infer<typeof insertEditSessionSchema>;
export type EditSession = typeof editSessions.$inferSelect;
// ─── Edit Diffs ─────────────────────────────────────────────────────────────
export const editDiffs = pgTable(
"edit_diffs",
{
id: uuid("id").primaryKey().defaultRandom(),
jobId: uuid("job_id")
.notNull()
.references(() => jobs.id, { onDelete: "cascade" }),
editSessionId: uuid("edit_session_id").references(() => editSessions.id),
subtitleIndex: integer("subtitle_index").notNull(),
field: text("field").notNull(), // text | tc_in | tc_out | position | style
oldValue: text("old_value"),
newValue: text("new_value"),
rationale: text("rationale").notNull(), // WHY β€” required, drives CI/CD analysis
changeCategory: changeCategoryEnum("change_category")
.notNull()
.default("other"),
createdAt: timestamp("created_at").notNull().defaultNow(),
},
(table) => [
index("idx_diffs_job").on(table.jobId),
index("idx_diffs_session").on(table.editSessionId),
index("idx_diffs_category").on(table.changeCategory),
]
);
export const insertEditDiffSchema = createInsertSchema(editDiffs).omit({
id: true,
createdAt: true,
});
export type InsertEditDiff = z.infer<typeof insertEditDiffSchema>;
export type EditDiff = typeof editDiffs.$inferSelect;
// ─── Feedback Analyses ──────────────────────────────────────────────────────
export const feedbackAnalyses = pgTable(
"feedback_analyses",
{
id: uuid("id").primaryKey().defaultRandom(),
jobId: uuid("job_id")
.notNull()
.references(() => jobs.id, { onDelete: "cascade" }),
errorType: errorTypeEnum("error_type").notNull(),
confidenceScore: real("confidence_score").notNull(),
description: text("description").notNull(),
affectedInput: affectedInputEnum("affected_input"),
reviewStatus: reviewStatusEnum("review_status")
.notNull()
.default("pending"),
reviewerId: uuid("reviewer_id"),
reviewedAt: timestamp("reviewed_at"),
actionTaken: jsonb("action_taken").$type<Record<string, unknown>>(),
createdAt: timestamp("created_at").notNull().defaultNow(),
},
(table) => [
index("idx_feedback_job").on(table.jobId),
index("idx_feedback_status").on(table.reviewStatus),
index("idx_feedback_error_type").on(table.errorType),
]
);
export const insertFeedbackAnalysisSchema = createInsertSchema(
feedbackAnalyses
).omit({ id: true, createdAt: true });
export type InsertFeedbackAnalysis = z.infer<
typeof insertFeedbackAnalysisSchema
>;
export type FeedbackAnalysis = typeof feedbackAnalyses.$inferSelect;
// ─── Relations ──────────────────────────────────────────────────────────────
export const resourcesRelations = relations(resources, ({ many }) => ({
skillSets: many(resourceSkillSets),
assignments: many(jobAssignments),
editSessions: many(editSessions),
}));
export const resourceSkillSetsRelations = relations(
resourceSkillSets,
({ one }) => ({
resource: one(resources, {
fields: [resourceSkillSets.resourceId],
references: [resources.id],
}),
})
);
export const jobsRelations = relations(jobs, ({ many }) => ({
assignments: many(jobAssignments),
editSessions: many(editSessions),
editDiffs: many(editDiffs),
feedbackAnalyses: many(feedbackAnalyses),
}));
export const jobAssignmentsRelations = relations(
jobAssignments,
({ one }) => ({
job: one(jobs, {
fields: [jobAssignments.jobId],
references: [jobs.id],
}),
resource: one(resources, {
fields: [jobAssignments.resourceId],
references: [resources.id],
}),
})
);
export const editSessionsRelations = relations(
editSessions,
({ one, many }) => ({
job: one(jobs, {
fields: [editSessions.jobId],
references: [jobs.id],
}),
resource: one(resources, {
fields: [editSessions.resourceId],
references: [resources.id],
}),
diffs: many(editDiffs),
})
);
export const editDiffsRelations = relations(editDiffs, ({ one }) => ({
job: one(jobs, {
fields: [editDiffs.jobId],
references: [jobs.id],
}),
editSession: one(editSessions, {
fields: [editDiffs.editSessionId],
references: [editSessions.id],
}),
}));
export const feedbackAnalysesRelations = relations(
feedbackAnalyses,
({ one }) => ({
job: one(jobs, {
fields: [feedbackAnalyses.jobId],
references: [jobs.id],
}),
})
);