240 lines
7.9 KiB
JavaScript
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();
|
|
});
|