ca-grow-ops-manager/backend/prisma/seed-plants-realistic.js
fullsizemalt 916aedb278
Some checks are pending
Deploy to Production / deploy (push) Waiting to run
Test / backend-test (push) Waiting to run
Test / frontend-test (push) Waiting to run
feat: add realistic plant placement script that keeps batches together by room/stage
2025-12-18 11:49:23 -08:00

240 lines
7.9 KiB
JavaScript

/**
* REALISTIC PLANT PLACEMENT - 777 Wolfpack
*
* Places plants in a realistic layout:
* - Each batch stays in ONE area matching its stage
* - Veg batches → Veg rooms only
* - Flowering batches → Flower rooms only
* - Drying/Curing → Processing rooms only
* - Clones → Veg Room 2 (nursery area)
*
* Run AFTER seed-demo.js
* Usage: node prisma/seed-plants-realistic.js
*/
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const DEMO_PREFIX = '[DEMO]';
async function main() {
console.log('🌿 Placing plants in realistic layout...\n');
// Clear existing plants first to start fresh
console.log('🧹 Clearing existing plant placements...');
await prisma.facilityPlant.deleteMany({});
await prisma.facilityPosition.updateMany({
data: { status: 'EMPTY' }
});
console.log(' ✓ Cleared\n');
// Get all batches
const batches = await prisma.batch.findMany({
where: { name: { startsWith: DEMO_PREFIX } },
orderBy: { startDate: 'asc' }
});
console.log(`📦 Found ${batches.length} batches\n`);
// Define which sections belong to which room types
const sectionMapping = {
// Veg Room 1 sections (Tables T1-T4)
VEG_PRIMARY: ['T1', 'T2', 'T3', 'T4'],
// Mother Room sections (Tables M1-M2)
MOTHER: ['M1', 'M2'],
// Flower Room A sections (Tables A1-A6)
FLOWER_A: ['A1', 'A2', 'A3', 'A4', 'A5', 'A6'],
// Flower Room B sections (Tables B1-B6)
FLOWER_B: ['B1', 'B2', 'B3', 'B4', 'B5', 'B6'],
// Dry Room sections (Racks D1-D2)
DRY: ['D1', 'D2'],
// Cure Room sections (Shelves C1-C2)
CURE: ['C1', 'C2'],
};
// Assign batches to specific areas based on stage
const batchPlacements = [];
for (const batch of batches) {
let targetSections = [];
let fillPercentage = 0.85;
switch (batch.stage) {
case 'CLONE_IN':
case 'VEGETATIVE':
// Veg batches go to Veg Room 1
targetSections = sectionMapping.VEG_PRIMARY;
fillPercentage = 0.9;
break;
case 'FLOWERING':
// Assign different flowering batches to different rooms
// Look for batches by strain to keep them separated
if (batch.strain === 'Gorilla Glue #4') {
targetSections = sectionMapping.FLOWER_A;
} else if (batch.strain === 'Wedding Cake') {
targetSections = sectionMapping.FLOWER_B;
} else {
// Zkittlez and others - put in Flower A remaining space
targetSections = ['A4', 'A5', 'A6'];
}
fillPercentage = 0.95;
break;
case 'DRYING':
targetSections = sectionMapping.DRY;
fillPercentage = 0.6;
break;
case 'CURING':
targetSections = sectionMapping.CURE;
fillPercentage = 0.5;
break;
default:
console.log(` ⚠️ Unknown stage for ${batch.name}: ${batch.stage}`);
continue;
}
batchPlacements.push({
batch,
targetSections,
fillPercentage
});
}
// Now place plants for each batch
for (const placement of batchPlacements) {
const { batch, targetSections, fillPercentage } = placement;
console.log(`\n🌱 Placing ${batch.name}...`);
console.log(` Stage: ${batch.stage} | Strain: ${batch.strain}`);
console.log(` Target sections: ${targetSections.join(', ')}`);
// Get all positions in these sections
const sections = await prisma.facilitySection.findMany({
where: { code: { in: targetSections } },
include: {
positions: true,
room: true
}
});
if (sections.length === 0) {
console.log(` ⚠️ No sections found with codes: ${targetSections.join(', ')}`);
continue;
}
// Collect all available positions
let allPositions = [];
for (const section of sections) {
const availablePositions = section.positions.filter(p => p.status === 'EMPTY');
allPositions = allPositions.concat(
availablePositions.map(p => ({
...p,
sectionCode: section.code,
roomName: section.room?.name
}))
);
}
// Calculate how many to fill
const targetCount = Math.min(
Math.floor(allPositions.length * fillPercentage),
batch.plantCount || allPositions.length
);
console.log(` Available positions: ${allPositions.length}`);
console.log(` Planting: ${targetCount} plants (${Math.round(fillPercentage * 100)}% fill)`);
// Place plants
let planted = 0;
const plantsToCreate = [];
const positionUpdates = [];
for (let i = 0; i < targetCount && i < allPositions.length; i++) {
const pos = allPositions[i];
// Generate a realistic METRC-style tag
const tagPrefix = batch.strain.substring(0, 2).toUpperCase();
const tagNumber = `1A4-${tagPrefix}-${batch.id.substring(0, 4).toUpperCase()}-${String(i + 1).padStart(4, '0')}`;
plantsToCreate.push({
tagNumber,
batchId: batch.id,
positionId: pos.id,
address: `${pos.sectionCode}-R${pos.row}-C${pos.column}`,
status: 'ACTIVE'
});
positionUpdates.push(pos.id);
planted++;
}
// Batch insert plants
if (plantsToCreate.length > 0) {
await prisma.facilityPlant.createMany({
data: plantsToCreate,
skipDuplicates: true
});
// Update positions
await prisma.facilityPosition.updateMany({
where: { id: { in: positionUpdates } },
data: { status: 'OCCUPIED' }
});
}
console.log(` ✅ Planted ${planted} plants`);
}
// Summary
const totalPlants = await prisma.facilityPlant.count();
const totalOccupied = await prisma.facilityPosition.count({ where: { status: 'OCCUPIED' } });
const totalEmpty = await prisma.facilityPosition.count({ where: { status: 'EMPTY' } });
console.log('\n' + '='.repeat(50));
console.log('📊 SUMMARY');
console.log('='.repeat(50));
console.log(`Total plants placed: ${totalPlants}`);
console.log(`Occupied positions: ${totalOccupied}`);
console.log(`Empty positions: ${totalEmpty}`);
console.log('='.repeat(50));
// Show per-room breakdown
console.log('\n📍 Plants by Room:');
const plantsByRoom = await prisma.facilityPlant.groupBy({
by: ['positionId'],
_count: true
});
const roomsWithPlants = await prisma.facilityRoom.findMany({
include: {
sections: {
include: {
positions: {
include: { plant: true }
}
}
}
}
});
for (const room of roomsWithPlants) {
let roomPlantCount = 0;
for (const section of room.sections) {
for (const pos of section.positions) {
if (pos.plant) roomPlantCount++;
}
}
if (roomPlantCount > 0) {
console.log(` ${room.name}: ${roomPlantCount} plants`);
}
}
console.log('\n✨ Realistic plant placement complete!');
}
main()
.catch((e) => {
console.error('Plant placement failed:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});