Refactor experiment management and update data structures

- Renamed columns in the experimental run sheet CSV for clarity.
- Updated the ExperimentForm component to include new fields for weight per repetition and additional parameters specific to Meyer Cracker experiments.
- Enhanced the data entry logic to handle new experiment phases and machine types.
- Refactored repetition scheduling logic to use scheduled_date instead of schedule_status for better clarity in status representation.
- Improved the user interface for displaying experiment phases and their associated statuses.
- Removed outdated seed data and updated database migration scripts to reflect the new schema changes.
This commit is contained in:
salirezav
2025-09-24 14:27:28 -04:00
parent 879f06ded6
commit 00f29ba6b4
33 changed files with 6489 additions and 1123 deletions

View File

@@ -38,6 +38,11 @@ export interface ExperimentPhase {
id: string
name: string
description?: string | null
has_soaking: boolean
has_airdrying: boolean
has_cracking: boolean
has_shelling: boolean
cracking_machine_type_id?: string | null
created_at: string
updated_at: string
created_by: string
@@ -47,15 +52,14 @@ export interface Experiment {
id: string
experiment_number: number
reps_required: number
soaking_duration_hr: number
air_drying_time_min: number
plate_contact_frequency_hz: number
throughput_rate_pecans_sec: number
crush_amount_in: number
entry_exit_height_diff_in: number
weight_per_repetition_lbs: number
results_status: ResultsStatus
completion_status: boolean
phase_id?: string | null
soaking_id?: string | null
airdrying_id?: string | null
cracking_id?: string | null
shelling_id?: string | null
created_at: string
updated_at: string
created_by: string
@@ -63,25 +67,115 @@ export interface Experiment {
// Machine Types
export interface MachineType {
id: string
name: string
description?: string | null
created_at: string
updated_at: string
created_by: string
}
// Phase-specific interfaces
export interface Soaking {
id: string
experiment_id: string
repetition_id?: string | null
scheduled_start_time: string
actual_start_time?: string | null
soaking_duration_minutes: number
scheduled_end_time: string
actual_end_time?: string | null
created_at: string
updated_at: string
created_by: string
}
export interface Airdrying {
id: string
experiment_id: string
repetition_id?: string | null
scheduled_start_time: string
actual_start_time?: string | null
duration_minutes: number
scheduled_end_time: string
actual_end_time?: string | null
created_at: string
updated_at: string
created_by: string
}
export interface Cracking {
id: string
experiment_id: string
repetition_id?: string | null
machine_type_id: string
scheduled_start_time: string
actual_start_time?: string | null
actual_end_time?: string | null
created_at: string
updated_at: string
created_by: string
}
export interface Shelling {
id: string
experiment_id: string
repetition_id?: string | null
scheduled_start_time: string
actual_start_time?: string | null
actual_end_time?: string | null
created_at: string
updated_at: string
created_by: string
}
// Machine-specific parameter interfaces
export interface JCCrackerParameters {
id: string
cracking_id: string
plate_contact_frequency_hz: number
throughput_rate_pecans_sec: number
crush_amount_in: number
entry_exit_height_diff_in: number
created_at: string
updated_at: string
}
export interface MeyerCrackerParameters {
id: string
cracking_id: string
motor_speed_hz: number
jig_displacement_inches: number
spring_stiffness_nm: number
created_at: string
updated_at: string
}
export interface CreateExperimentPhaseRequest {
name: string
description?: string
has_soaking: boolean
has_airdrying: boolean
has_cracking: boolean
has_shelling: boolean
cracking_machine_type_id?: string
}
export interface UpdateExperimentPhaseRequest {
name?: string
description?: string
has_soaking?: boolean
has_airdrying?: boolean
has_cracking?: boolean
has_shelling?: boolean
}
export interface CreateExperimentRequest {
experiment_number: number
reps_required: number
soaking_duration_hr: number
air_drying_time_min: number
plate_contact_frequency_hz: number
throughput_rate_pecans_sec: number
crush_amount_in: number
entry_exit_height_diff_in: number
weight_per_repetition_lbs: number
results_status?: ResultsStatus
completion_status?: boolean
phase_id?: string
@@ -90,12 +184,7 @@ export interface CreateExperimentRequest {
export interface UpdateExperimentRequest {
experiment_number?: number
reps_required?: number
soaking_duration_hr?: number
air_drying_time_min?: number
plate_contact_frequency_hz?: number
throughput_rate_pecans_sec?: number
crush_amount_in?: number
entry_exit_height_diff_in?: number
weight_per_repetition_lbs?: number
results_status?: ResultsStatus
completion_status?: boolean
phase_id?: string
@@ -105,12 +194,10 @@ export interface CreateRepetitionRequest {
experiment_id: string
repetition_number: number
scheduled_date?: string | null
schedule_status?: ScheduleStatus
}
export interface UpdateRepetitionRequest {
scheduled_date?: string | null
schedule_status?: ScheduleStatus
completion_status?: boolean
}
@@ -137,7 +224,6 @@ export interface ExperimentRepetition {
experiment_id: string
repetition_number: number
scheduled_date?: string | null
schedule_status: ScheduleStatus
completion_status: boolean
is_locked: boolean
locked_at?: string | null
@@ -219,6 +305,58 @@ export interface UpdatePhaseDataRequest {
[key: string]: any // For phase-specific data fields
}
// Phase creation request interfaces
export interface CreateSoakingRequest {
experiment_id: string
repetition_id?: string
scheduled_start_time: string
soaking_duration_minutes: number
actual_start_time?: string
actual_end_time?: string
}
export interface CreateAirdryingRequest {
experiment_id: string
repetition_id?: string
scheduled_start_time?: string // Will be auto-calculated from soaking if not provided
duration_minutes: number
actual_start_time?: string
actual_end_time?: string
}
export interface CreateCrackingRequest {
experiment_id: string
repetition_id?: string
machine_type_id: string
scheduled_start_time?: string // Will be auto-calculated from airdrying if not provided
actual_start_time?: string
actual_end_time?: string
}
export interface CreateShellingRequest {
experiment_id: string
repetition_id?: string
scheduled_start_time: string
actual_start_time?: string
actual_end_time?: string
}
// Machine parameter creation request interfaces
export interface CreateJCCrackerParametersRequest {
cracking_id: string
plate_contact_frequency_hz: number
throughput_rate_pecans_sec: number
crush_amount_in: number
entry_exit_height_diff_in: number
}
export interface CreateMeyerCrackerParametersRequest {
cracking_id: string
motor_speed_hz: number
jig_displacement_inches: number
spring_stiffness_nm: number
}
export interface UserRole {
id: string
user_id: string
@@ -624,6 +762,349 @@ export const experimentManagement = {
}
}
// Machine Type Management
export const machineTypeManagement = {
// Get all machine types
async getAllMachineTypes(): Promise<MachineType[]> {
const { data, error } = await supabase
.from('machine_types')
.select('*')
.order('name')
if (error) throw error
return data
},
// Get machine type by ID
async getMachineTypeById(id: string): Promise<MachineType | null> {
const { data, error } = await supabase
.from('machine_types')
.select('*')
.eq('id', id)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
}
}
// Phase Management
export const phaseManagement = {
// Soaking management
async createSoaking(request: CreateSoakingRequest): Promise<Soaking> {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) throw new Error('User not authenticated')
const { data, error } = await supabase
.from('soaking')
.insert({
...request,
created_by: user.id
})
.select()
.single()
if (error) throw error
return data
},
async updateSoaking(id: string, updates: Partial<Soaking>): Promise<Soaking> {
const { data, error } = await supabase
.from('soaking')
.update(updates)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
async getSoakingByExperimentId(experimentId: string): Promise<Soaking | null> {
const { data, error } = await supabase
.from('soaking')
.select('*')
.eq('experiment_id', experimentId)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
},
async getSoakingByRepetitionId(repetitionId: string): Promise<Soaking | null> {
const { data, error } = await supabase
.from('soaking')
.select('*')
.eq('repetition_id', repetitionId)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
},
// Airdrying management
async createAirdrying(request: CreateAirdryingRequest): Promise<Airdrying> {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) throw new Error('User not authenticated')
const { data, error } = await supabase
.from('airdrying')
.insert({
...request,
created_by: user.id
})
.select()
.single()
if (error) throw error
return data
},
async updateAirdrying(id: string, updates: Partial<Airdrying>): Promise<Airdrying> {
const { data, error } = await supabase
.from('airdrying')
.update(updates)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
async getAirdryingByExperimentId(experimentId: string): Promise<Airdrying | null> {
const { data, error } = await supabase
.from('airdrying')
.select('*')
.eq('experiment_id', experimentId)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
},
async getAirdryingByRepetitionId(repetitionId: string): Promise<Airdrying | null> {
const { data, error } = await supabase
.from('airdrying')
.select('*')
.eq('repetition_id', repetitionId)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
},
// Cracking management
async createCracking(request: CreateCrackingRequest): Promise<Cracking> {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) throw new Error('User not authenticated')
const { data, error } = await supabase
.from('cracking')
.insert({
...request,
created_by: user.id
})
.select()
.single()
if (error) throw error
return data
},
async updateCracking(id: string, updates: Partial<Cracking>): Promise<Cracking> {
const { data, error } = await supabase
.from('cracking')
.update(updates)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
async getCrackingByExperimentId(experimentId: string): Promise<Cracking | null> {
const { data, error } = await supabase
.from('cracking')
.select('*')
.eq('experiment_id', experimentId)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
},
async getCrackingByRepetitionId(repetitionId: string): Promise<Cracking | null> {
const { data, error } = await supabase
.from('cracking')
.select('*')
.eq('repetition_id', repetitionId)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
},
// Shelling management
async createShelling(request: CreateShellingRequest): Promise<Shelling> {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) throw new Error('User not authenticated')
const { data, error } = await supabase
.from('shelling')
.insert({
...request,
created_by: user.id
})
.select()
.single()
if (error) throw error
return data
},
async updateShelling(id: string, updates: Partial<Shelling>): Promise<Shelling> {
const { data, error } = await supabase
.from('shelling')
.update(updates)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
async getShellingByExperimentId(experimentId: string): Promise<Shelling | null> {
const { data, error } = await supabase
.from('shelling')
.select('*')
.eq('experiment_id', experimentId)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
},
async getShellingByRepetitionId(repetitionId: string): Promise<Shelling | null> {
const { data, error } = await supabase
.from('shelling')
.select('*')
.eq('repetition_id', repetitionId)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
}
}
// Machine Parameter Management
export const machineParameterManagement = {
// JC Cracker parameters
async createJCCrackerParameters(request: CreateJCCrackerParametersRequest): Promise<JCCrackerParameters> {
const { data, error } = await supabase
.from('jc_cracker_parameters')
.insert(request)
.select()
.single()
if (error) throw error
return data
},
async updateJCCrackerParameters(id: string, updates: Partial<JCCrackerParameters>): Promise<JCCrackerParameters> {
const { data, error } = await supabase
.from('jc_cracker_parameters')
.update(updates)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
async getJCCrackerParametersByCrackingId(crackingId: string): Promise<JCCrackerParameters | null> {
const { data, error } = await supabase
.from('jc_cracker_parameters')
.select('*')
.eq('cracking_id', crackingId)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
},
// Meyer Cracker parameters
async createMeyerCrackerParameters(request: CreateMeyerCrackerParametersRequest): Promise<MeyerCrackerParameters> {
const { data, error } = await supabase
.from('meyer_cracker_parameters')
.insert(request)
.select()
.single()
if (error) throw error
return data
},
async updateMeyerCrackerParameters(id: string, updates: Partial<MeyerCrackerParameters>): Promise<MeyerCrackerParameters> {
const { data, error } = await supabase
.from('meyer_cracker_parameters')
.update(updates)
.eq('id', id)
.select()
.single()
if (error) throw error
return data
},
async getMeyerCrackerParametersByCrackingId(crackingId: string): Promise<MeyerCrackerParameters | null> {
const { data, error } = await supabase
.from('meyer_cracker_parameters')
.select('*')
.eq('cracking_id', crackingId)
.single()
if (error) {
if (error.code === 'PGRST116') return null // Not found
throw error
}
return data
}
}
// Experiment Repetitions Management
export const repetitionManagement = {
// Get all repetitions for an experiment
@@ -673,7 +1154,6 @@ export const repetitionManagement = {
async scheduleRepetition(id: string, scheduledDate: string): Promise<ExperimentRepetition> {
const updates: UpdateRepetitionRequest = {
scheduled_date: scheduledDate,
schedule_status: 'scheduled'
}
return this.updateRepetition(id, updates)
@@ -683,7 +1163,6 @@ export const repetitionManagement = {
async removeRepetitionSchedule(id: string): Promise<ExperimentRepetition> {
const updates: UpdateRepetitionRequest = {
scheduled_date: null,
schedule_status: 'pending schedule'
}
return this.updateRepetition(id, updates)
@@ -700,15 +1179,14 @@ export const repetitionManagement = {
},
// Get repetitions by status
async getRepetitionsByStatus(scheduleStatus?: ScheduleStatus): Promise<ExperimentRepetition[]> {
async getRepetitionsByStatus(isScheduled?: boolean): Promise<ExperimentRepetition[]> {
let query = supabase.from('experiment_repetitions').select('*')
if (scheduleStatus) {
query = query.eq('schedule_status', scheduleStatus)
if (isScheduled === true) {
query = query.not('scheduled_date', 'is', null)
} else if (isScheduled === false) {
query = query.is('scheduled_date', null)
}
const { data, error } = await query.order('created_at', { ascending: false })
if (error) throw error
return data
},
@@ -743,8 +1221,7 @@ export const repetitionManagement = {
for (let i = 1; i <= experiment.reps_required; i++) {
repetitions.push({
experiment_id: experimentId,
repetition_number: i,
schedule_status: 'pending schedule'
repetition_number: i
})
}