CognxSafeTrack
feat: Implement Multi-AI Architecture with Google Gemini provider and failover logic
f41372b
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
phone String @unique
name String?
role Role @default(STUDENT)
language Language @default(FR)
city String?
activity String? // Business activity/sector
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
currentStreak Int @default(0)
longestStreak Int @default(0)
lastActivityAt DateTime?
enrollments Enrollment[]
responses Response[]
messages Message[]
payments Payment[]
progress UserProgress[]
businessProfile BusinessProfile?
}
model BusinessProfile {
id String @id @default(uuid())
userId String @unique
activityLabel String? // e.g. "Jus de Bissap"
activityPhrase String? // Optimized elevator pitch
activityType String? // e.g. "Production", "Service"
locationCity String?
mainCustomer String?
mainProblem String?
offerSimple String?
promise String?
marketData Json? // Stored Google Search results for TAM/SAM/SOM & Competition
competitorList Json? // List of rivals found or declared
financialProjections Json? // 3-year growth data
fundingAsk String? // Amount and purpose
lastUpdatedFromDay Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
}
model Track {
id String @id @default(uuid())
title String
description String?
duration Int // Duration in days
language Language @default(FR)
// Payment Integration Fields
isPremium Boolean @default(false)
priceAmount Int? // Price in smallest currency unit (e.g., cents/XOF)
stripePriceId String? // Stripe Price ID
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
days TrackDay[]
enrollments Enrollment[]
payments Payment[]
progress UserProgress[]
}
model TrackDay {
id String @id @default(uuid())
trackId String
dayNumber Float
title String?
audioUrl String?
imageUrl String?
videoUrl String?
videoCaption String?
lessonText String?
exerciseType ExerciseType @default(TEXT)
exercisePrompt String?
validationKeyword String?
buttonsJson Json?
exerciseCriteria Json?
badges Json? // Array of strings: ["B_MODULE_1_OK"]
unlockCondition String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
track Track @relation(fields: [trackId], references: [id])
}
model UserProgress {
id String @id @default(uuid())
userId String
trackId String
score Int @default(0)
lastInteraction DateTime @default(now())
exerciseStatus ExerciseStatus @default(PENDING)
badges Json? // Array of strings: ["CLARTE", "CONFIANCE"]
behavioralScoring Json? // { discipline_financiere: 0, organisation: 0, ... }
confidenceScore Float? // STT Whisper avg_logprob mapped to 0-100%
adminTranscription String?
overrideAudioUrl String?
reviewedBy String?
iterationCount Int @default(0) // Tracks Deep Dive loops
aiSource String? // Provider used (GEMINI, OPENAI)
// Removed Enriched Data for Pitch Deck (Moved to BusinessProfile - Sprint 38)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
track Track @relation(fields: [trackId], references: [id])
@@unique([userId, trackId])
}
model Enrollment {
id String @id @default(uuid())
userId String
trackId String
status EnrollmentStatus @default(ACTIVE)
currentDay Float @default(1)
startedAt DateTime @default(now())
completedAt DateTime?
lastActivityAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
track Track @relation(fields: [trackId], references: [id])
responses Response[]
}
model Response {
id String @id @default(uuid())
enrollmentId String
userId String
dayNumber Int
content String? // Text response
mediaUrl String? // Voice/Image response
createdAt DateTime @default(now())
aiSource String? // Provider used (GEMINI, OPENAI)
enrollment Enrollment @relation(fields: [enrollmentId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id])
}
model Message {
id String @id @default(uuid())
userId String
direction Direction // INBOUND, OUTBOUND
channel String @default("WHATSAPP")
content String?
mediaUrl String?
payload Json? // Raw payload from provider
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
@@index([userId, createdAt])
}
enum Role {
STUDENT
ADMIN
}
enum EnrollmentStatus {
ACTIVE
COMPLETED
DROPPED
}
enum Language {
FR
WOLOF
}
enum ContentType {
TEXT
AUDIO
IMAGE
VIDEO
}
enum Direction {
INBOUND
OUTBOUND
}
model Payment {
id String @id @default(uuid())
userId String
trackId String
amount Int
currency String @default("XOF")
status PaymentStatus @default(PENDING)
stripeSessionId String? @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
track Track @relation(fields: [trackId], references: [id])
}
enum PaymentStatus {
PENDING
COMPLETED
FAILED
REFUNDED
}
enum ExerciseType {
TEXT
AUDIO
BUTTON
}
enum ExerciseStatus {
PENDING
PENDING_REMEDIATION
PENDING_REVIEW
PENDING_DEEPDIVE
COMPLETED
}
model TrainingData {
id String @id @default(uuid())
audioUrl String
transcription String @db.Text
manualCorrection String? @db.Text
rawWER Float?
normalizedWER Float?
status TrainingStatus @default(PENDING)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum TrainingStatus {
PENDING
REVIEWED
IGNORED
}