/** * 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(); });