localgreenchain/prisma/seed.ts
Claude 3d2ccdc29a
feat(db): implement PostgreSQL database integration with Prisma ORM
Agent 2 - Database Integration (P0 Critical):

- Add Prisma ORM with PostgreSQL for persistent data storage
- Create comprehensive database schema with 20+ models:
  - User & authentication models
  - Plant & lineage tracking
  - Transport events & supply chain
  - Vertical farming (farms, zones, batches, recipes)
  - Demand & market matching
  - Audit logging & blockchain storage

- Implement complete database service layer (lib/db/):
  - users.ts: User CRUD with search and stats
  - plants.ts: Plant operations with lineage tracking
  - transport.ts: Transport events and carbon tracking
  - farms.ts: Vertical farm and crop batch management
  - demand.ts: Consumer preferences and market matching
  - audit.ts: Audit logging and blockchain integrity

- Add PlantChainDB for database-backed blockchain
- Create development seed script with sample data
- Add database documentation (docs/DATABASE.md)
- Update package.json with Prisma dependencies and scripts

This provides the foundation for all other agents to build upon
with persistent, scalable data storage.
2025-11-23 03:56:40 +00:00

835 lines
23 KiB
TypeScript

/**
* LocalGreenChain Database Seed Script
* Populates the database with development data
*
* Run with: bun run db:seed
*/
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
console.log('Starting database seed...');
// Clean existing data (in reverse dependency order)
console.log('Cleaning existing data...');
await prisma.auditLog.deleteMany();
await prisma.blockchainBlock.deleteMany();
await prisma.marketMatch.deleteMany();
await prisma.plantingRecommendation.deleteMany();
await prisma.demandForecast.deleteMany();
await prisma.seasonalPlan.deleteMany();
await prisma.supplyCommitment.deleteMany();
await prisma.demandSignal.deleteMany();
await prisma.consumerPreference.deleteMany();
await prisma.farmAnalytics.deleteMany();
await prisma.resourceUsage.deleteMany();
await prisma.harvestBatch.deleteMany();
await prisma.cropBatch.deleteMany();
await prisma.growingRecipe.deleteMany();
await prisma.growingZone.deleteMany();
await prisma.verticalFarm.deleteMany();
await prisma.transportEvent.deleteMany();
await prisma.seedBatch.deleteMany();
await prisma.plant.deleteMany();
await prisma.user.deleteMany();
// Create users
console.log('Creating users...');
const users = await Promise.all([
prisma.user.create({
data: {
email: 'alice@localgreenchain.io',
name: 'Alice Green',
userType: 'GROWER',
latitude: 37.7749,
longitude: -122.4194,
city: 'San Francisco',
country: 'USA',
bio: 'Urban farmer specializing in leafy greens and microgreens',
walletAddress: '0x1234567890abcdef1234567890abcdef12345678',
},
}),
prisma.user.create({
data: {
email: 'bob@localgreenchain.io',
name: 'Bob Farmer',
userType: 'GROWER',
latitude: 37.8044,
longitude: -122.2712,
city: 'Oakland',
country: 'USA',
bio: 'Vertical farm operator with 5 years experience',
walletAddress: '0xabcdef1234567890abcdef1234567890abcdef12',
},
}),
prisma.user.create({
data: {
email: 'carol@localgreenchain.io',
name: 'Carol Consumer',
userType: 'CONSUMER',
latitude: 37.7849,
longitude: -122.4094,
city: 'San Francisco',
country: 'USA',
bio: 'Health-conscious consumer supporting local farms',
},
}),
prisma.user.create({
data: {
email: 'david@localgreenchain.io',
name: 'David Distributor',
userType: 'DISTRIBUTOR',
latitude: 37.7649,
longitude: -122.3994,
city: 'San Francisco',
country: 'USA',
bio: 'Local food distributor serving Bay Area restaurants',
},
}),
prisma.user.create({
data: {
email: 'eve@localgreenchain.io',
name: 'Eve Admin',
userType: 'ADMIN',
latitude: 37.7949,
longitude: -122.4294,
city: 'San Francisco',
country: 'USA',
bio: 'LocalGreenChain platform administrator',
},
}),
]);
console.log(`Created ${users.length} users`);
// Create plants
console.log('Creating plants...');
const plants = await Promise.all([
// Original tomato plant
prisma.plant.create({
data: {
commonName: 'Cherry Tomato',
scientificName: 'Solanum lycopersicum var. cerasiforme',
species: 'lycopersicum',
genus: 'Solanum',
family: 'Solanaceae',
propagationType: 'ORIGINAL',
generation: 0,
plantedDate: new Date('2024-03-15'),
status: 'MATURE',
latitude: 37.7749,
longitude: -122.4194,
city: 'San Francisco',
country: 'USA',
ownerId: users[0].id,
notes: 'Heirloom variety from local seed library',
images: ['tomato-seedling.jpg', 'tomato-mature.jpg'],
},
}),
// Basil plant
prisma.plant.create({
data: {
commonName: 'Sweet Basil',
scientificName: 'Ocimum basilicum',
species: 'basilicum',
genus: 'Ocimum',
family: 'Lamiaceae',
propagationType: 'SEED',
generation: 1,
plantedDate: new Date('2024-04-01'),
status: 'GROWING',
latitude: 37.7749,
longitude: -122.4194,
city: 'San Francisco',
country: 'USA',
ownerId: users[0].id,
notes: 'Italian large leaf variety',
},
}),
// Lettuce
prisma.plant.create({
data: {
commonName: 'Butterhead Lettuce',
scientificName: 'Lactuca sativa var. capitata',
species: 'sativa',
genus: 'Lactuca',
family: 'Asteraceae',
propagationType: 'SEED',
generation: 0,
plantedDate: new Date('2024-05-01'),
status: 'GROWING',
latitude: 37.8044,
longitude: -122.2712,
city: 'Oakland',
country: 'USA',
ownerId: users[1].id,
notes: 'Vertical farm grown with LED lighting',
},
}),
// Kale
prisma.plant.create({
data: {
commonName: 'Curly Kale',
scientificName: 'Brassica oleracea var. sabellica',
species: 'oleracea',
genus: 'Brassica',
family: 'Brassicaceae',
propagationType: 'SEED',
generation: 0,
plantedDate: new Date('2024-04-15'),
status: 'MATURE',
latitude: 37.8044,
longitude: -122.2712,
city: 'Oakland',
country: 'USA',
ownerId: users[1].id,
notes: 'Organic certified',
},
}),
// Microgreens
prisma.plant.create({
data: {
commonName: 'Sunflower Microgreens',
scientificName: 'Helianthus annuus',
species: 'annuus',
genus: 'Helianthus',
family: 'Asteraceae',
propagationType: 'SEED',
generation: 0,
plantedDate: new Date('2024-05-20'),
status: 'SPROUTED',
latitude: 37.7749,
longitude: -122.4194,
city: 'San Francisco',
country: 'USA',
ownerId: users[0].id,
notes: 'Fast-growing microgreen variety',
},
}),
]);
console.log(`Created ${plants.length} plants`);
// Create tomato clone (child plant)
const tomatoClone = await prisma.plant.create({
data: {
commonName: 'Cherry Tomato Clone',
scientificName: 'Solanum lycopersicum var. cerasiforme',
species: 'lycopersicum',
genus: 'Solanum',
family: 'Solanaceae',
parentPlantId: plants[0].id,
propagationType: 'CLONE',
generation: 1,
plantedDate: new Date('2024-04-20'),
status: 'GROWING',
latitude: 37.8044,
longitude: -122.2712,
city: 'Oakland',
country: 'USA',
ownerId: users[1].id,
notes: 'Cloned from Alice\'s heirloom tomato',
},
});
console.log('Created tomato clone');
// Create seed batches
console.log('Creating seed batches...');
const seedBatches = await Promise.all([
prisma.seedBatch.create({
data: {
species: 'Solanum lycopersicum',
variety: 'Cherry Heirloom',
quantity: 100,
quantityUnit: 'seeds',
generation: 0,
germinationRate: 0.92,
purityPercentage: 0.99,
harvestDate: new Date('2023-10-15'),
expirationDate: new Date('2026-10-15'),
certifications: ['organic', 'heirloom'],
status: 'AVAILABLE',
},
}),
prisma.seedBatch.create({
data: {
species: 'Lactuca sativa',
variety: 'Butterhead',
quantity: 500,
quantityUnit: 'seeds',
generation: 0,
germinationRate: 0.88,
harvestDate: new Date('2024-01-20'),
expirationDate: new Date('2027-01-20'),
certifications: ['organic', 'non_gmo'],
status: 'AVAILABLE',
},
}),
]);
console.log(`Created ${seedBatches.length} seed batches`);
// Create vertical farm
console.log('Creating vertical farms...');
const farm = await prisma.verticalFarm.create({
data: {
name: 'Oakland Urban Greens',
ownerId: users[1].id,
latitude: 37.8044,
longitude: -122.2712,
address: '123 Industrial Blvd',
city: 'Oakland',
country: 'USA',
timezone: 'America/Los_Angeles',
specs: {
totalAreaSqm: 500,
growingAreaSqm: 400,
numberOfLevels: 5,
ceilingHeightM: 4,
totalGrowingPositions: 2000,
currentActivePlants: 1500,
powerCapacityKw: 100,
waterStorageL: 5000,
backupPowerHours: 8,
certifications: ['gap', 'haccp'],
buildingType: 'warehouse',
insulation: 'high_efficiency',
},
environmentalControl: {
hvacUnits: [
{ id: 'hvac1', type: 'heat_pump', capacityKw: 20, status: 'running' },
{ id: 'hvac2', type: 'cooling', capacityKw: 15, status: 'running' },
],
co2Injection: { type: 'tank', capacityKg: 50, currentLevelKg: 35 },
},
lightingSystem: {
type: 'LED',
totalWattage: 50000,
efficacyUmolJ: 2.8,
},
automationLevel: 'SEMI_AUTOMATED',
status: 'OPERATIONAL',
operationalSince: new Date('2023-06-01'),
capacityUtilization: 0.75,
yieldEfficiency: 0.85,
energyEfficiencyScore: 0.78,
},
});
console.log('Created vertical farm');
// Create growing zones
console.log('Creating growing zones...');
const zones = await Promise.all([
prisma.growingZone.create({
data: {
name: 'Zone A - Leafy Greens',
farmId: farm.id,
level: 1,
areaSqm: 80,
lengthM: 10,
widthM: 8,
growingMethod: 'NFT',
plantPositions: 400,
currentCrop: 'Butterhead Lettuce',
plantingDate: new Date('2024-05-01'),
expectedHarvestDate: new Date('2024-05-28'),
environmentTargets: {
temperatureC: { min: 18, max: 24, target: 21 },
humidityPercent: { min: 60, max: 80, target: 70 },
co2Ppm: { min: 800, max: 1200, target: 1000 },
lightPpfd: { min: 200, max: 400, target: 300 },
lightHours: 16,
},
currentEnvironment: {
timestamp: new Date().toISOString(),
temperatureC: 21.5,
humidityPercent: 68,
co2Ppm: 950,
ppfd: 310,
waterTempC: 20,
ec: 1.4,
ph: 6.2,
},
status: 'GROWING',
},
}),
prisma.growingZone.create({
data: {
name: 'Zone B - Herbs',
farmId: farm.id,
level: 2,
areaSqm: 60,
lengthM: 10,
widthM: 6,
growingMethod: 'DWC',
plantPositions: 300,
currentCrop: 'Sweet Basil',
plantingDate: new Date('2024-05-10'),
expectedHarvestDate: new Date('2024-06-10'),
status: 'GROWING',
},
}),
prisma.growingZone.create({
data: {
name: 'Zone C - Microgreens',
farmId: farm.id,
level: 3,
areaSqm: 40,
growingMethod: 'RACK_SYSTEM',
plantPositions: 500,
status: 'EMPTY',
},
}),
]);
console.log(`Created ${zones.length} growing zones`);
// Create growing recipe
console.log('Creating growing recipes...');
const recipe = await prisma.growingRecipe.create({
data: {
name: 'Butterhead Lettuce - Fast Cycle',
cropType: 'Lettuce',
variety: 'Butterhead',
version: '2.0',
stages: [
{
name: 'Germination',
daysStart: 0,
daysEnd: 3,
temperature: { day: 20, night: 18 },
humidity: { day: 80, night: 85 },
lightHours: 18,
lightPpfd: 150,
},
{
name: 'Seedling',
daysStart: 4,
daysEnd: 10,
temperature: { day: 21, night: 18 },
humidity: { day: 70, night: 75 },
lightHours: 16,
lightPpfd: 250,
},
{
name: 'Vegetative',
daysStart: 11,
daysEnd: 24,
temperature: { day: 22, night: 18 },
humidity: { day: 65, night: 70 },
lightHours: 16,
lightPpfd: 350,
},
{
name: 'Harvest',
daysStart: 25,
daysEnd: 28,
temperature: { day: 18, night: 16 },
humidity: { day: 60, night: 65 },
lightHours: 12,
lightPpfd: 300,
},
],
expectedDays: 28,
expectedYieldGrams: 150,
expectedYieldPerSqm: 3.5,
requirements: {
positions: 1,
zoneType: 'NFT',
minimumPpfd: 200,
idealTemperatureC: 20,
},
source: 'INTERNAL',
author: 'Bob Farmer',
rating: 4.5,
timesUsed: 15,
},
});
console.log('Created growing recipe');
// Create crop batch
console.log('Creating crop batches...');
const cropBatch = await prisma.cropBatch.create({
data: {
farmId: farm.id,
zoneId: zones[0].id,
cropType: 'Butterhead Lettuce',
variety: 'Bibb',
recipeId: recipe.id,
seedBatchId: seedBatches[1].id,
plantCount: 400,
plantingDate: new Date('2024-05-01'),
expectedHarvestDate: new Date('2024-05-28'),
expectedYieldKg: 60,
currentStage: 'vegetative',
currentDay: 18,
healthScore: 92,
status: 'GROWING',
environmentLog: [
{
timestamp: new Date('2024-05-15').toISOString(),
readings: { temperatureC: 21, humidityPercent: 68, ppfd: 320 },
},
{
timestamp: new Date('2024-05-18').toISOString(),
readings: { temperatureC: 21.5, humidityPercent: 67, ppfd: 325 },
},
],
},
});
console.log('Created crop batch');
// Create transport events
console.log('Creating transport events...');
const transportEvents = await Promise.all([
prisma.transportEvent.create({
data: {
eventType: 'SEED_ACQUISITION',
fromLatitude: 37.7849,
fromLongitude: -122.4094,
fromLocationType: 'SEED_BANK',
fromFacilityName: 'Bay Area Seed Library',
toLatitude: 37.7749,
toLongitude: -122.4194,
toLocationType: 'FARM',
toFacilityName: 'Alice\'s Urban Farm',
distanceKm: 1.2,
durationMinutes: 15,
transportMethod: 'BICYCLE',
carbonFootprintKg: 0,
senderId: users[2].id,
receiverId: users[0].id,
status: 'VERIFIED',
notes: 'Picked up heirloom tomato seeds',
eventData: {
seedBatchId: seedBatches[0].id,
quantity: 50,
sourceType: 'seed_library',
},
},
}),
prisma.transportEvent.create({
data: {
eventType: 'DISTRIBUTION',
fromLatitude: 37.8044,
fromLongitude: -122.2712,
fromLocationType: 'VERTICAL_FARM',
fromFacilityName: 'Oakland Urban Greens',
toLatitude: 37.7849,
toLongitude: -122.4094,
toLocationType: 'MARKET',
toFacilityName: 'Ferry Building Farmers Market',
distanceKm: 8.5,
durationMinutes: 25,
transportMethod: 'ELECTRIC_TRUCK',
carbonFootprintKg: 0.51,
senderId: users[1].id,
receiverId: users[3].id,
status: 'DELIVERED',
notes: 'Weekly lettuce delivery',
eventData: {
batchIds: [cropBatch.id],
quantityKg: 45,
destinationType: 'market',
},
},
}),
]);
console.log(`Created ${transportEvents.length} transport events`);
// Create consumer preference
console.log('Creating consumer preferences...');
await prisma.consumerPreference.create({
data: {
consumerId: users[2].id,
latitude: 37.7849,
longitude: -122.4094,
maxDeliveryRadiusKm: 15,
city: 'San Francisco',
region: 'Bay Area',
dietaryType: ['vegetarian', 'flexitarian'],
allergies: [],
dislikes: ['cilantro'],
preferredCategories: ['leafy_greens', 'herbs', 'microgreens'],
preferredItems: [
{ produceType: 'Lettuce', category: 'leafy_greens', priority: 'must_have' },
{ produceType: 'Basil', category: 'herbs', priority: 'preferred' },
{ produceType: 'Kale', category: 'leafy_greens', priority: 'nice_to_have' },
],
certificationPreferences: ['organic', 'local'],
freshnessImportance: 5,
priceImportance: 3,
sustainabilityImportance: 5,
deliveryPreferences: {
method: ['farmers_market', 'home_delivery'],
frequency: 'weekly',
preferredDays: ['saturday', 'sunday'],
},
householdSize: 2,
weeklyBudget: 75,
currency: 'USD',
},
});
console.log('Created consumer preference');
// Create demand signal
console.log('Creating demand signals...');
const demandSignal = await prisma.demandSignal.create({
data: {
centerLat: 37.7849,
centerLon: -122.4094,
radiusKm: 20,
regionName: 'San Francisco Bay Area',
periodStart: new Date('2024-05-01'),
periodEnd: new Date('2024-05-31'),
seasonalPeriod: 'spring',
demandItems: [
{
produceType: 'Butterhead Lettuce',
category: 'leafy_greens',
weeklyDemandKg: 150,
monthlyDemandKg: 600,
consumerCount: 45,
aggregatePriority: 8,
urgency: 'this_week',
inSeason: true,
},
{
produceType: 'Sweet Basil',
category: 'herbs',
weeklyDemandKg: 30,
monthlyDemandKg: 120,
consumerCount: 32,
aggregatePriority: 7,
urgency: 'this_week',
inSeason: true,
},
],
totalConsumers: 78,
totalWeeklyDemandKg: 180,
confidenceLevel: 85,
currentSupplyKg: 120,
supplyGapKg: 60,
supplyStatus: 'SHORTAGE',
},
});
console.log('Created demand signal');
// Create supply commitment
console.log('Creating supply commitments...');
const supplyCommitment = await prisma.supplyCommitment.create({
data: {
growerId: users[1].id,
produceType: 'Butterhead Lettuce',
variety: 'Bibb',
committedQuantityKg: 60,
availableFrom: new Date('2024-05-28'),
availableUntil: new Date('2024-06-15'),
pricePerKg: 8.5,
currency: 'USD',
minimumOrderKg: 2,
bulkDiscountThreshold: 20,
bulkDiscountPercent: 10,
certifications: ['gap', 'local'],
freshnessGuaranteeHours: 24,
deliveryRadiusKm: 25,
deliveryMethods: ['grower_delivery', 'customer_pickup'],
status: 'AVAILABLE',
remainingKg: 60,
},
});
console.log('Created supply commitment');
// Create market match
console.log('Creating market matches...');
await prisma.marketMatch.create({
data: {
consumerId: users[2].id,
growerId: users[1].id,
demandSignalId: demandSignal.id,
supplyCommitmentId: supplyCommitment.id,
produceType: 'Butterhead Lettuce',
matchedQuantityKg: 4,
agreedPricePerKg: 8.5,
totalPrice: 34,
currency: 'USD',
deliveryDate: new Date('2024-05-30'),
deliveryMethod: 'farmers_market',
deliveryLatitude: 37.7955,
deliveryLongitude: -122.3937,
deliveryAddress: 'Ferry Building, San Francisco',
status: 'CONFIRMED',
},
});
console.log('Created market match');
// Create seasonal plan
console.log('Creating seasonal plans...');
await prisma.seasonalPlan.create({
data: {
growerId: users[1].id,
year: 2024,
season: 'summer',
location: {
latitude: 37.8044,
longitude: -122.2712,
hardinessZone: '10a',
},
growingCapacity: {
verticalFarmSqMeters: 400,
hydroponicUnits: 5,
},
plannedCrops: [
{
produceType: 'Butterhead Lettuce',
plantingDate: '2024-06-01',
quantity: 500,
expectedYieldKg: 75,
status: 'planned',
},
{
produceType: 'Sweet Basil',
plantingDate: '2024-06-15',
quantity: 300,
expectedYieldKg: 15,
status: 'planned',
},
],
expectedTotalYieldKg: 90,
expectedRevenue: 765,
status: 'DRAFT',
},
});
console.log('Created seasonal plan');
// Create audit logs
console.log('Creating audit logs...');
await Promise.all([
prisma.auditLog.create({
data: {
userId: users[0].id,
action: 'CREATE',
entityType: 'Plant',
entityId: plants[0].id,
newValue: { commonName: 'Cherry Tomato', status: 'SPROUTED' },
metadata: { source: 'web_app' },
},
}),
prisma.auditLog.create({
data: {
userId: users[1].id,
action: 'CREATE',
entityType: 'VerticalFarm',
entityId: farm.id,
newValue: { name: 'Oakland Urban Greens' },
metadata: { source: 'web_app' },
},
}),
prisma.auditLog.create({
data: {
userId: users[1].id,
action: 'UPDATE',
entityType: 'CropBatch',
entityId: cropBatch.id,
previousValue: { status: 'GERMINATING' },
newValue: { status: 'GROWING' },
metadata: { source: 'automated_agent' },
},
}),
]);
console.log('Created audit logs');
// Create blockchain blocks
console.log('Creating blockchain blocks...');
await Promise.all([
prisma.blockchainBlock.create({
data: {
index: 0,
timestamp: new Date('2024-01-01'),
previousHash: '0',
hash: '0000000000000000000000000000000000000000000000000000000000000000',
nonce: 0,
blockType: 'genesis',
content: { message: 'LocalGreenChain Genesis Block' },
},
}),
prisma.blockchainBlock.create({
data: {
index: 1,
timestamp: new Date('2024-03-15'),
previousHash: '0000000000000000000000000000000000000000000000000000000000000000',
hash: '1a2b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef1234567890',
nonce: 42,
blockType: 'plant',
plantId: plants[0].id,
content: {
plantId: plants[0].id,
commonName: 'Cherry Tomato',
action: 'REGISTERED',
},
},
}),
]);
console.log('Created blockchain blocks');
// Create resource usage records
console.log('Creating resource usage records...');
await prisma.resourceUsage.create({
data: {
farmId: farm.id,
periodStart: new Date('2024-05-01'),
periodEnd: new Date('2024-05-15'),
electricityKwh: 1500,
electricityCostUsd: 225,
renewablePercent: 60,
peakDemandKw: 45,
waterUsageL: 3000,
waterCostUsd: 15,
waterRecycledPercent: 85,
co2UsedKg: 20,
co2CostUsd: 40,
nutrientsUsedL: 50,
nutrientCostUsd: 75,
kwhPerKgProduce: 25,
litersPerKgProduce: 50,
costPerKgProduce: 5.92,
},
});
console.log('Created resource usage records');
// Summary
console.log('\n=== Seed Complete ===');
console.log(`Users: ${users.length}`);
console.log(`Plants: ${plants.length + 1}`);
console.log(`Seed Batches: ${seedBatches.length}`);
console.log(`Vertical Farms: 1`);
console.log(`Growing Zones: ${zones.length}`);
console.log(`Growing Recipes: 1`);
console.log(`Crop Batches: 1`);
console.log(`Transport Events: ${transportEvents.length}`);
console.log('\nDatabase seeded successfully!');
}
main()
.catch((e) => {
console.error('Seed error:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});