@damourlabs/euro-clinical-trials
Version:
A Nuxt 3 application for visualizing and managing clinical trials data in Europe, built with modern web technologies.
203 lines (191 loc) • 7.96 kB
text/typescript
// Drizzle ORM schema for Regulatory Compliance tables
import {
pgTable,
uuid,
varchar,
text,
timestamp,
decimal,
date,
boolean,
check,
pgEnum
} from 'drizzle-orm/pg-core';
import { sql } from 'drizzle-orm';
import { z } from 'zod';
import type { Expect, IsSameType } from '~/types/equality';
import { sites, trials, patients } from '.';
import { AESeverityEnum, ApprovalStatusEnum, ApprovalTypeEnum } from './enums';
export const regulatoryFieldDefaultValues = {
approvalType: ApprovalTypeEnum.Enum.ClinicalTrial,
status: ApprovalStatusEnum.Enum.Approved,
gdprCompliant: false,
overallCompliance: '0.00',
patientCompliance: '0.00',
siteCompliance: '0.00',
severity: AESeverityEnum.Enum.Mild
};
export const pgApprovalType = pgEnum('approval_type_enum', ApprovalTypeEnum.options);
export const pgApprovalStatus = pgEnum('approval_status_enum', ApprovalStatusEnum.options);
// ----------------------------
// --- Regulatory Approvals ---
// ----------------------------
export const regulatoryApprovals = pgTable('regulatory_approvals', {
uuid: uuid('uuid').primaryKey().defaultRandom(),
trialUuid: uuid('trial_uuid').notNull().references(() => trials.uuid, { onDelete: 'cascade' }),
approvalType: pgApprovalType().notNull().default(regulatoryFieldDefaultValues.approvalType),
status: pgApprovalStatus().notNull().default(regulatoryFieldDefaultValues.status),
authority: varchar('authority', { length: 255 }).notNull(),
approvalDate: date('approval_date').default(sql`CURRENT_DATE`),
expiryDate: date('expiry_date'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
}, (table) => [
check('regulatory_approvals_authority_not_empty', sql`LENGTH(TRIM(${table.authority})) > 0`),
check('regulatory_approvals_date_order', sql`${table.approvalDate} IS NULL OR ${table.expiryDate} IS NULL OR ${table.approvalDate} <= ${table.expiryDate}`),
]);
export const regulatoryApprovalsSchema = z.object({
uuid: z.string()
.uuid()
.describe("Unique identifier for the regulatory approval, automatically generated if not provided")
,
trialUuid: z.string()
.uuid()
.describe("UUID of the trial this approval belongs to")
,
approvalType: ApprovalTypeEnum
.describe("Type of regulatory approval")
.default(regulatoryFieldDefaultValues.approvalType)
,
status: ApprovalStatusEnum
.describe("Status of the approval")
.default(regulatoryFieldDefaultValues.status)
,
authority: z.string()
.max(255)
.min(1)
.describe("Regulatory authority that granted the approval")
,
approvalDate: z.string()
.nullable()
.describe("Date when the approval was granted")
,
expiryDate: z.string()
.nullable()
.describe("Date when the approval expires")
,
createdAt: z.date()
.nullable()
.describe("Timestamp of when the approval record was created")
,
});
// -------------------------
// --- Compliance Status ---
// -------------------------
export const complianceStatus = pgTable('compliance_status', {
uuid: uuid('uuid').primaryKey().defaultRandom(),
trialUuid: uuid('trial_uuid').notNull().unique().references(() => trials.uuid, { onDelete: 'cascade' }),
gdprCompliant: boolean('gdpr_compliant').notNull().default(regulatoryFieldDefaultValues.gdprCompliant),
overallCompliance: decimal('overall_compliance', { precision: 5, scale: 2 }).notNull().default(regulatoryFieldDefaultValues.overallCompliance),
patientCompliance: decimal('patient_compliance', { precision: 5, scale: 2 }).notNull().default(regulatoryFieldDefaultValues.patientCompliance),
siteCompliance: decimal('site_compliance', { precision: 5, scale: 2 }).notNull().default(regulatoryFieldDefaultValues.siteCompliance),
lastUpdated: date('last_updated').notNull().default(sql`CURRENT_DATE`),
}, (table) => [
check('compliance_status_percentages_valid', sql`${table.overallCompliance} >= 0.00 AND ${table.overallCompliance} <= 100.00 AND ${table.patientCompliance} >= 0.00 AND ${table.patientCompliance} <= 100.00 AND ${table.siteCompliance} >= 0.00 AND ${table.siteCompliance} <= 100.00`),
]);
export const complianceStatusSchema = z.object({
uuid: z.string()
.uuid()
.describe("Unique identifier for the compliance status, automatically generated if not provided")
,
trialUuid: z.string()
.uuid()
.describe("UUID of the trial this compliance status belongs to")
,
gdprCompliant: z.boolean()
.describe("Whether the trial is GDPR compliant")
.default(regulatoryFieldDefaultValues.gdprCompliant)
,
overallCompliance: z.string()
.describe("Overall compliance percentage")
.default(regulatoryFieldDefaultValues.overallCompliance)
,
patientCompliance: z.string()
.describe("Patient compliance percentage")
.default(regulatoryFieldDefaultValues.patientCompliance)
,
siteCompliance: z.string()
.describe("Site compliance percentage")
.default(regulatoryFieldDefaultValues.siteCompliance)
,
lastUpdated: z.string()
.describe("Date when compliance status was last updated")
,
});
// ---------------------------
// --- Protocol Deviations ---
// ---------------------------
export const pgSeverity = pgEnum('severity_enum', AESeverityEnum.options)
export const protocolDeviations = pgTable('protocol_deviations', {
uuid: uuid('uuid').primaryKey().defaultRandom(),
trialUuid: uuid('trial_uuid').notNull().references(() => trials.uuid, { onDelete: 'cascade' }),
patientUuid: uuid('patient_uuid').references(() => patients.uuid, { onDelete: 'cascade' }),
siteUuid: uuid('site_uuid').references(() => sites.uuid, { onDelete: 'cascade' }),
description: text('description').notNull(),
dateOccurred: date('date_occurred').notNull().default(sql`CURRENT_DATE`),
severity: pgSeverity("severity").notNull().default(regulatoryFieldDefaultValues.severity),
impactAssessment: text('impact_assessment'),
correctiveAction: text('corrective_action'),
reportedAt: timestamp('reported_at', { withTimezone: true }).defaultNow(),
}, (table) => [
check('protocol_deviations_description_not_empty', sql`LENGTH(TRIM(${table.description})) > 0`),
]);
export const protocolDeviationsSchema = z.object({
uuid: z.string()
.uuid()
.describe("Unique identifier for the protocol deviation, automatically generated if not provided")
,
trialUuid: z.string()
.uuid()
.describe("UUID of the trial this deviation belongs to")
,
patientUuid: z.string()
.uuid()
.nullable()
.describe("UUID of the patient involved in the deviation")
,
siteUuid: z.string()
.uuid()
.nullable()
.describe("UUID of the site where the deviation occurred")
,
description: z.string()
.min(1)
.describe("Description of the protocol deviation")
,
dateOccurred: z.string()
.describe("Date when the deviation occurred")
,
severity: AESeverityEnum
.describe("Severity of the deviation")
.default(regulatoryFieldDefaultValues.severity)
,
impactAssessment: z.string()
.nullable()
.describe("Assessment of the deviation's impact")
,
correctiveAction: z.string()
.nullable()
.describe("Corrective action taken for the deviation")
,
reportedAt: z.date()
.nullable()
.describe("Timestamp when the deviation was reported")
,
});
export type RegulatoryApproval = z.infer<typeof regulatoryApprovalsSchema>;
export type ComplianceStatus = z.infer<typeof complianceStatusSchema>;
export type ProtocolDeviation = z.infer<typeof protocolDeviationsSchema>;
// TODO: Fix type mismatches between Zod schemas and Drizzle table types
type _regulatoryApprovalSchemaMatchesTable = Expect<IsSameType<RegulatoryApproval, typeof regulatoryApprovals.$inferSelect>>
type _complianceStatusSchemaMatchesTable = Expect<IsSameType<ComplianceStatus, typeof complianceStatus.$inferSelect>>
type _protocolDeviationSchemaMatchesTable = Expect<IsSameType<ProtocolDeviation, typeof protocolDeviations.$inferSelect>>