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...'); // Clear existing 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(); // Import all-year crime summaries (2020-2024) await this.importAllYearSummaries(); 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@gmail.com`, roles_id: roleId.id, confirmed_at: new Date(), email_confirmed_at: new Date(), is_anonymous: false, profile: { create: { first_name: 'Admin', last_name: 'Sigap', username: 'admin', }, }, }, }); 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 importAllYearSummaries() { console.log('Importing all-year (2020-2024) crime summaries...'); // Check if all-year summaries already exist (records with null month and null year) const existingAllYearSummaries = await this.prisma.crimes.findFirst({ where: { month: null, year: null }, }); if (existingAllYearSummaries) { console.log('All-year crime summaries already exist, skipping import.'); return; } // Read CSV file const csvFilePath = path.resolve( __dirname, '../data/excels/crimes/district_summary_2020_2024.csv' ); const fileContent = fs.readFileSync(csvFilePath, { encoding: 'utf-8' }); // Parse CSV const records = parse(fileContent, { columns: true, skip_empty_lines: true, }); for (const record of records) { const crimeRate = record.level.toLowerCase() as crime_rates; const districtId = record.district_id; const city = await this.prisma.cities.findFirst({ where: { districts: { some: { id: districtId, }, }, }, }); if (!city) { console.error(`City not found for district ${districtId}`); continue; } // Create a unique ID for all-year summary data const crimeId = generateId({ prefix: 'CR', segments: { codes: [city.id], sequentialDigits: 4, }, format: '{prefix}-{codes}-{sequence}', separator: '-', randomSequence: false, uniquenessStrategy: 'counter', }); await this.prisma.crimes.create({ data: { id: crimeId, district_id: districtId, level: crimeRate, method: 'kmeans', month: null, year: null, number_of_crime: parseInt(record.crime_total), score: parseFloat(record.avg_score), }, }); } console.log(`Imported ${records.length} all-year crime summaries.`); } } // 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(); }