import { PrismaClient, crime_rates, events, session_status, users, } from '@prisma/client'; import fs from 'fs'; import path from 'path'; import { parse } from 'csv-parse/sync'; import { generateId } from '../../app/_utils/common'; export class CrimesSeeder { constructor(private prisma: PrismaClient) {} async run(): Promise { console.log('🌱 Seeding crimes data...'); try { // Create test user const user = await this.createUsers(); // Create 5 events const events = await this.createEvents(user); // Create 5 sessions with completed status await this.createSessions(user, events); // Import monthly crime data await this.importMonthlyCrimeData(); // Import yearly crime data from CSV file await this.importYearlyCrimeData(); console.log('✅ Crime seeding completed successfully.'); } catch (error) { console.error('❌ Error seeding crimes:', error); throw error; } } private async createUsers() { // Check if test users already exist const existingUser = await this.prisma.users.findFirst({ where: { email: 'admin@sigap.id' }, }); if (existingUser) { console.log('Users already exist, skipping creation.'); return existingUser; } // Get admin role ID let roleId = await this.prisma.roles.findFirst({ where: { name: 'admin' }, }); if (!roleId) { roleId = await this.prisma.roles.create({ data: { name: 'admin', description: 'Administrator role', }, }); } // Create test users const user = await this.prisma.users.create({ data: { email: `admin@sigap.id`, roles_id: roleId.id, encrypted_password: 'sigap123', confirmed_at: new Date(), email_confirmed_at: new Date(), is_anonymous: false, }, }); return user; } private async createEvents(user: users) { // Check if events already exist const existingEvent = await this.prisma.events.findFirst({ where: { user_id: user.id, }, }); if (existingEvent) { console.log('Events already exist, skipping creation.'); return existingEvent; } const event = await this.prisma.events.create({ data: { name: `Crime Inserting Event For Crime 2020 - 2024`, description: `Event for inserting crimes in region Jember`, user_id: user.id, }, }); return event; } private async createSessions(user: users, events: events) { // Check if sessions already exist const existingSession = await this.prisma.sessions.findFirst(); if (existingSession) { console.log('Sessions already exist, skipping creation.'); return; } const newSession = await this.prisma.sessions.create({ data: { user_id: user.id, event_id: events.id, status: session_status.completed, }, }); return newSession; } private async importMonthlyCrimeData() { console.log('Importing monthly crime data...'); // Check if crimes already exist const existingCrimes = await this.prisma.crimes.findFirst(); if (existingCrimes) { console.log('Crimes data already exists, skipping import.'); return; } // Read CSV file const csvFilePath = path.resolve( __dirname, '../data/excels/crimes/crime_monthly.csv' ); const fileContent = fs.readFileSync(csvFilePath, { encoding: 'utf-8' }); // Parse CSV const records = parse(fileContent, { columns: true, skip_empty_lines: true, }); // Store unique district IDs to avoid duplicates const processedDistricts = new Set(); // Import records for (const record of records) { const crimeRate = record.level.toLowerCase() as crime_rates; const city = await this.prisma.cities.findFirst({ where: { name: 'Jember', }, }); if (!city) { console.error('City not found: Jember'); return; } const year = parseInt(record.year); // Create a unique ID for monthly crime data const crimeId = generateId({ prefix: 'CR', segments: { codes: [city.id], sequentialDigits: 4, year, }, format: '{prefix}-{codes}-{sequence}-{year}', separator: '-', randomSequence: false, uniquenessStrategy: 'counter', }); await this.prisma.crimes.create({ data: { id: crimeId, district_id: record.district_id, level: crimeRate, method: record.method || null, month: parseInt(record.month_num), year: parseInt(record.year), number_of_crime: parseInt(record.number_of_crime), score: parseFloat(record.score), }, }); // Keep track of unique districts for later creation of crime incidents processedDistricts.add(record.district_id); } console.log(`Imported ${records.length} monthly crime records.`); } private async importYearlyCrimeData() { console.log('Importing yearly crime data...'); // Check if yearly summaries already exist (records with null month) const existingYearlySummary = await this.prisma.crimes.findFirst({ where: { month: null }, }); if (existingYearlySummary) { console.log('Yearly crime data already exists, skipping import.'); return; } // Read CSV file const csvFilePath = path.resolve( __dirname, '../data/excels/crimes/crime_yearly.csv' ); const fileContent = fs.readFileSync(csvFilePath, { encoding: 'utf-8' }); // Parse CSV const records = parse(fileContent, { columns: true, skip_empty_lines: true, }); // Import records for (const record of records) { const crimeRate = record.level.toLowerCase() as crime_rates; const year = parseInt(record.year); const city = await this.prisma.cities.findFirst({ where: { districts: { some: { id: record.district_id, }, }, }, }); if (!city) { console.error(`City not found for district ${record.district_id}`); continue; } // Create a unique ID for yearly crime data const crimeId = generateId({ prefix: 'CR', segments: { codes: [city.id], sequentialDigits: 4, year, }, format: '{prefix}-{codes}-{sequence}-{year}', separator: '-', randomSequence: false, uniquenessStrategy: 'counter', }); await this.prisma.crimes.create({ data: { id: crimeId, district_id: record.district_id, level: crimeRate, method: record.method || 'kmeans', month: null, year: year, number_of_crime: parseInt(record.number_of_crime), score: parseInt(record.score), }, }); } console.log(`Imported ${records.length} yearly crime records.`); } // private async generateYearlyCrimeSummaries() { // console.log('Generating yearly crime summaries...'); // // Check if yearly summaries already exist (records with null month) // const existingYearlySummary = await this.prisma.crimes.findFirst({ // where: { month: null }, // }); // if (existingYearlySummary) { // console.log('Yearly summaries already exist, skipping generation.'); // return; // } // // Get all districts and years combinations // const districtsYears = await this.prisma.crimes.findMany({ // select: { // district_id: true, // year: true, // }, // distinct: ['district_id', 'year'], // }); // // For each district and year, calculate yearly summary // for (const { district_id, year } of districtsYears) { // // Calculate sum of crimes for the district and year // const result = await this.prisma.crimes.aggregate({ // _sum: { // number_of_crime: true, // }, // where: { // district_id: district_id, // year: year, // month: { // not: null, // }, // }, // }); // if (!result || !result._sum.number_of_crime) { // console.log( // `No monthly data found for district ${district_id} in year ${year}. Skipping...` // ); // continue; // } // const totalCrimes = result._sum.number_of_crime; // // Get average level based on monthly data (use the most common level) // const levelResult = await this.prisma.crimes // .groupBy({ // by: ['level'], // where: { // district_id: district_id, // year: year, // month: { // not: null, // }, // }, // _count: { // level: true, // }, // orderBy: { // _count: { // level: 'desc', // }, // }, // take: 1, // }) // .then((results) => // results.map((result) => ({ // level: result.level, // count: result._count.level, // })) // ); // if (levelResult.length === 0) { // console.log( // `No level data found for district ${district_id} in year ${year}. Skipping...` // ); // continue; // } // const level = levelResult[0].level || 'low'; // const city = await this.prisma.cities.findFirst({ // where: { // districts: { // some: { // id: district_id, // }, // }, // }, // }); // if (!city) { // console.error(`City not found for district ${district_id}`); // continue; // } // // Create yearly summary record // const newCrimeId = generateId({ // prefix: 'CR', // segments: { // codes: [city.id], // sequentialDigits: 4, // year, // }, // format: '{prefix}-{codes}-{sequence}-{year}', // separator: '-', // randomSequence: false, // uniquenessStrategy: 'counter', // }); // await this.prisma.crimes.create({ // data: { // id: newCrimeId, // district_id: district_id as string, // level: level as crime_rates, // method: 'kmeans', // month: null, // year: year as number, // number_of_crime: totalCrimes, // score: 100 - Math.min(totalCrimes, 100), // Simple score calculation // }, // }); // } // console.log( // `Generated yearly summaries for ${districtsYears.length} district-year combinations.` // ); // } } // This allows the file to be executed standalone for testing if (require.main === module) { const testSeeder = async () => { const prisma = new PrismaClient(); const seeder = new CrimesSeeder(prisma); try { await seeder.run(); } catch (e) { console.error('Error during seeding:', e); process.exit(1); } finally { await prisma.$disconnect(); } }; testSeeder(); }