chore: Commit current state before refactoring

Includes deleted sql/ files, seeds, and documentation files.
Prepares master for refactoring branch.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
richardtekula
2026-01-28 07:19:23 +01:00
parent 95688be45b
commit 883d3fa533
14 changed files with 1 additions and 3398 deletions

View File

@@ -1,303 +0,0 @@
import dotenv from 'dotenv';
dotenv.config();
import { eq, sql } from 'drizzle-orm';
const { db } = await import('../../config/database.js');
const { kurzy, ucastnici, registracie } = await import('../schema.js');
// Clear existing data
async function clearData() {
console.log('Clearing existing data...');
await db.delete(registracie);
await db.delete(ucastnici);
await db.delete(kurzy);
// Reset sequences
await db.execute(sql`ALTER SEQUENCE kurzy_id_seq RESTART WITH 1`);
await db.execute(sql`ALTER SEQUENCE ucastnici_id_seq RESTART WITH 1`);
await db.execute(sql`ALTER SEQUENCE registracie_id_seq RESTART WITH 1`);
console.log('Data cleared.');
}
// Course data - now without dates (dates are per-registration)
const coursesData = [
{
nazov: 'AI 1+2 (2 dni) - 290€',
typKurzu: 'AI',
cena: '290',
},
{
nazov: 'AI 1 (1 deň) - 150€',
typKurzu: 'AI',
cena: '150',
},
{
nazov: 'AI 2 (1 deň) - 150€',
typKurzu: 'AI',
cena: '150',
},
{
nazov: 'AI v SEO (1 deň) - 150€',
typKurzu: 'SEO',
cena: '150',
},
{
nazov: 'AI I+II Marec 2026',
typKurzu: 'AI',
cena: '290',
},
{
nazov: 'AI I+II Apríl 2026',
typKurzu: 'AI',
cena: '290',
},
];
// Participants data from CSV - dates are now on registration level
const participantsData = [
// Umelá Inteligencia I+II 2. - 3. Február 2026
{
meno: 'Martin',
priezvisko: 'Sovák',
telefon: '0918986172',
email: 'info@energium.sk',
firma: 'energium sro',
formaKurzu: 'prezencne',
kurz: 'AI 1+2 (2 dni) - 290€',
datumOd: new Date('2026-02-02'),
datumDo: new Date('2026-02-03'),
pocetUcastnikov: 1,
mesto: 'Bratislava',
ulica: 'Topolcianska 5',
psc: '85105',
fakturaVystavena: true,
zaplatene: false,
poznamka: 'FA 2026020',
stav: 'registrovany',
},
{
meno: 'Michal',
priezvisko: 'Farkaš',
telefon: '0911209122',
email: 'michal.farkas83@gmail.com',
firma: 'SLOVWELD',
formaKurzu: 'online',
kurz: 'AI 1 (1 deň) - 150€',
datumOd: new Date('2026-02-02'),
datumDo: new Date('2026-02-02'),
pocetUcastnikov: 1,
mesto: 'Dunajska Lužná',
ulica: 'Mandlova 30',
psc: '90042',
fakturaVystavena: true,
zaplatene: true,
poznamka: 'Fa 2025 338, Súhlasil so zmeneným termínom',
stav: 'registrovany',
},
{
meno: 'Alena',
priezvisko: 'Šranková',
telefon: '0917352580',
email: 'alena.srankova@gmail.com',
formaKurzu: 'online',
kurz: 'AI 1+2 (2 dni) - 290€',
datumOd: new Date('2026-02-02'),
datumDo: new Date('2026-02-03'),
pocetUcastnikov: 1,
mesto: 'Bratislava',
ulica: 'Šándorova 1',
psc: '82103',
fakturaVystavena: true,
zaplatene: true,
stav: 'registrovany',
},
{
meno: 'Katarina',
priezvisko: 'Tomaníková',
telefon: '0948 070 611',
email: 'k.tomanikova@riseday.net',
firma: 'Classica Shipping Limited',
formaKurzu: 'prezencne',
kurz: 'AI 1+2 (2 dni) - 290€',
datumOd: new Date('2026-02-02'),
datumDo: new Date('2026-02-03'),
pocetUcastnikov: 1,
mesto: 'Bratislava',
ulica: 'Keltska 104',
psc: '85110',
fakturaVystavena: true,
zaplatene: true,
poznamka: 'presunuta z oktobra, chce až január',
stav: 'registrovany',
},
{
meno: 'Róbert',
priezvisko: 'Brišák',
telefon: '0910583883',
email: 'robert.brisak@ss-nizna.sk',
firma: 'Spojená škola, Hattalova 471, 02743 Nižná',
formaKurzu: 'prezencne',
kurz: 'AI 1+2 (2 dni) - 290€',
datumOd: new Date('2026-02-02'),
datumDo: new Date('2026-02-03'),
pocetUcastnikov: 1,
mesto: 'Nižná',
ulica: 'Hattalova 471',
psc: '02743',
fakturaVystavena: true,
zaplatene: false,
poznamka: 'FA 2026019',
stav: 'registrovany',
},
{
meno: 'Marián',
priezvisko: 'Bača',
telefon: '0907994126',
email: 'baca.marian@gmail.com',
formaKurzu: 'prezencne',
kurz: 'AI 2 (1 deň) - 150€',
datumOd: new Date('2026-02-03'),
datumDo: new Date('2026-02-03'),
pocetUcastnikov: 1,
mesto: 'Petrovany',
ulica: '8',
psc: '08253',
fakturaVystavena: true,
zaplatene: false,
poznamka: 'Fa Gablasova',
stav: 'registrovany',
},
{
titul: 'Mgr. MBA',
meno: 'Nikola',
priezvisko: 'Horáčková',
telefon: '0918482184',
email: 'nikolahorackova11@gmail.com',
kurz: 'AI 1+2 (2 dni) - 290€',
datumOd: new Date('2026-02-02'),
datumDo: new Date('2026-02-03'),
pocetUcastnikov: 1,
mesto: 'Zákopčie',
ulica: 'Zákopčie stred 12',
psc: '023 11',
fakturaVystavena: false,
zaplatene: false,
poznamka: 'vzdelávací poukaz',
stav: 'potencialny',
},
// AI v SEO 13.2.2026
{
meno: 'Tomáš',
priezvisko: 'Kupec',
telefon: '0911030190',
email: 'kupec.tom@gmail.com',
firma: 'Jamajka',
formaKurzu: 'prezencne',
kurz: 'AI v SEO (1 deň) - 150€',
datumOd: new Date('2026-02-13'),
datumDo: new Date('2026-02-13'),
pocetUcastnikov: 1,
mesto: 'Liptovská Sielnica',
psc: '032 23',
fakturaVystavena: true,
zaplatene: false,
poznamka: 'FA 2026021',
stav: 'registrovany',
},
{
meno: 'Anton',
priezvisko: 'Považský',
email: 'anton.povazsky@example.com', // No email in CSV, using placeholder
formaKurzu: 'prezencne',
kurz: 'AI v SEO (1 deň) - 150€',
datumOd: new Date('2026-02-13'),
datumDo: new Date('2026-02-13'),
pocetUcastnikov: 1,
fakturaVystavena: true,
zaplatene: false,
stav: 'registrovany',
},
];
async function importData() {
console.log('Starting import...');
// Create courses (now without dates)
console.log('\nCreating courses...');
const createdKurzy = {};
for (const course of coursesData) {
const [created] = await db.insert(kurzy).values({
nazov: course.nazov,
typKurzu: course.typKurzu,
cena: course.cena,
aktivny: true,
}).returning();
createdKurzy[course.nazov] = created.id;
console.log(` Created course: ${course.nazov} (ID: ${created.id})`);
}
// Create participants and registrations (with dates)
console.log('\nCreating participants and registrations...');
for (const p of participantsData) {
// Check if participant already exists by email
let [existingUcastnik] = await db.select().from(ucastnici).where(eq(ucastnici.email, p.email)).limit(1);
let ucastnikId;
if (existingUcastnik) {
ucastnikId = existingUcastnik.id;
console.log(` Using existing participant: ${p.email}`);
} else {
const [created] = await db.insert(ucastnici).values({
titul: p.titul || null,
meno: p.meno,
priezvisko: p.priezvisko,
email: p.email,
telefon: p.telefon || null,
firma: p.firma || null,
mesto: p.mesto || null,
ulica: p.ulica || null,
psc: p.psc || null,
}).returning();
ucastnikId = created.id;
console.log(` Created participant: ${p.meno} ${p.priezvisko} (${p.email})`);
}
// Get kurz ID
const kurzId = createdKurzy[p.kurz];
if (!kurzId) {
console.error(` ERROR: Course not found: ${p.kurz}`);
continue;
}
// Create registration with dates
await db.insert(registracie).values({
kurzId: kurzId,
ucastnikId: ucastnikId,
datumOd: p.datumOd || null,
datumDo: p.datumDo || null,
formaKurzu: p.formaKurzu || 'prezencne',
pocetUcastnikov: p.pocetUcastnikov || 1,
fakturaVystavena: p.fakturaVystavena || false,
zaplatene: p.zaplatene || false,
stav: p.stav || 'registrovany',
poznamka: p.poznamka || null,
});
console.log(` Created registration for ${p.email} -> ${p.kurz} (${p.datumOd?.toLocaleDateString('sk-SK')} - ${p.datumDo?.toLocaleDateString('sk-SK')})`);
}
console.log('\n=== Import completed ===');
console.log(`Courses: ${coursesData.length}`);
console.log(`Participants: ${participantsData.length}`);
}
// Run
clearData()
.then(() => importData())
.then(() => {
console.log('Done!');
process.exit(0);
})
.catch((error) => {
console.error('Import failed:', error);
process.exit(1);
});

View File

@@ -1,292 +0,0 @@
import dotenv from 'dotenv';
dotenv.config();
import ExcelJS from 'exceljs';
import { eq, and } from 'drizzle-orm';
import path from 'path';
// Dynamic imports to ensure env is loaded first
const { db } = await import('../../config/database.js');
const { kurzy, ucastnici, registracie } = await import('../schema.js');
const EXCEL_FILE = '/home/richardtekula/Downloads/Copy of AI školenie študenti.xlsx';
// Helper to parse dates from various formats
const parseDate = (value) => {
if (!value) return null;
if (value instanceof Date) return value;
if (typeof value === 'number') {
// Excel serial date number
const date = new Date((value - 25569) * 86400 * 1000);
return date;
}
if (typeof value === 'string') {
const parsed = new Date(value);
return isNaN(parsed.getTime()) ? null : parsed;
}
return null;
};
// Helper to clean string values
const cleanString = (value) => {
if (value === null || value === undefined) return null;
const str = String(value).trim();
return str === '' ? null : str;
};
// Helper to parse numeric value
const parseNumber = (value) => {
if (value === null || value === undefined) return null;
const num = parseFloat(value);
return isNaN(num) ? null : num;
};
// Map stav from Excel to our enum values
const mapStav = (value) => {
if (!value) return 'registrovany';
const v = String(value).toLowerCase().trim();
if (v.includes('absolvoval')) return 'absolvoval';
if (v.includes('potvrden')) return 'potvrdeny';
if (v.includes('zrusen')) return 'zruseny';
if (v.includes('potencial')) return 'potencialny';
return 'registrovany';
};
// Map forma kurzu
const mapForma = (value) => {
if (!value) return 'prezencne';
const v = String(value).toLowerCase().trim();
if (v.includes('online')) return 'online';
if (v.includes('hybrid')) return 'hybridne';
return 'prezencne';
};
async function importAiKurzy() {
console.log('Reading Excel file:', EXCEL_FILE);
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.readFile(EXCEL_FILE);
console.log('Sheets in workbook:', workbook.worksheets.map(ws => ws.name));
// Process each sheet
for (const worksheet of workbook.worksheets) {
console.log(`\n=== Processing sheet: ${worksheet.name} ===`);
console.log(`Rows: ${worksheet.rowCount}, Columns: ${worksheet.columnCount}`);
// Get headers from first row
const headerRow = worksheet.getRow(1);
const headers = [];
headerRow.eachCell((cell, colNum) => {
headers[colNum] = cleanString(cell.value);
});
console.log('Headers:', headers.filter(Boolean));
// Collect data rows
const dataRows = [];
worksheet.eachRow((row, rowNum) => {
if (rowNum === 1) return; // Skip header
const rowData = {};
row.eachCell((cell, colNum) => {
const header = headers[colNum];
if (header) {
rowData[header] = cell.value;
}
});
// Only add if row has some data
if (Object.values(rowData).some(v => v !== null && v !== undefined && v !== '')) {
dataRows.push(rowData);
}
});
console.log(`Found ${dataRows.length} data rows`);
// Log first few rows to understand structure
if (dataRows.length > 0) {
console.log('Sample row:', JSON.stringify(dataRows[0], null, 2));
}
// Try to import data based on headers
await importSheetData(worksheet.name, headers, dataRows);
}
console.log('\n=== Import completed ===');
}
async function importSheetData(sheetName, headers, rows) {
// Detect what kind of data this is based on headers
const headerLower = headers.map(h => h?.toLowerCase() || '');
const hasKurzFields = headerLower.some(h => h.includes('kurz') || h.includes('datum') || h.includes('cena'));
const hasUcastnikFields = headerLower.some(h => h.includes('meno') || h.includes('email') || h.includes('priezvisko'));
if (rows.length === 0) {
console.log('No data to import');
return;
}
// Import participants and registrations
if (hasUcastnikFields) {
await importParticipantsAndRegistrations(sheetName, headers, rows);
}
}
async function importParticipantsAndRegistrations(sheetName, headers, rows) {
console.log(`\nImporting participants from sheet: ${sheetName}`);
// First, ensure we have a course for this sheet
const courseName = sheetName;
let course = await db.select().from(kurzy).where(eq(kurzy.nazov, courseName)).limit(1);
if (course.length === 0) {
// Create course from sheet name
const [newCourse] = await db.insert(kurzy).values({
nazov: courseName,
typKurzu: extractCourseType(sheetName),
cena: '0', // Will need to update manually
datumOd: new Date(),
datumDo: new Date(),
aktivny: true,
}).returning();
course = [newCourse];
console.log(`Created course: ${courseName} (ID: ${newCourse.id})`);
} else {
console.log(`Using existing course: ${courseName} (ID: ${course[0].id})`);
}
const kurzId = course[0].id;
// Map headers to our fields
const headerMap = {};
headers.forEach((header, idx) => {
if (!header) return;
const h = header.toLowerCase();
if (h.includes('titul') || h === 'titul') headerMap.titul = idx;
if (h.includes('meno') && !h.includes('priezvisko')) headerMap.meno = idx;
if (h.includes('priezvisko') || h === 'surname' || h === 'priezvisko') headerMap.priezvisko = idx;
if (h.includes('email') || h.includes('e-mail')) headerMap.email = idx;
if (h.includes('telefon') || h.includes('phone') || h.includes('tel')) headerMap.telefon = idx;
if (h.includes('firma') || h.includes('company') || h.includes('spolocnost')) headerMap.firma = idx;
if (h.includes('mesto') || h.includes('city')) headerMap.mesto = idx;
if (h.includes('ulica') || h.includes('street') || h.includes('adresa')) headerMap.ulica = idx;
if (h.includes('psc') || h.includes('zip') || h.includes('postal')) headerMap.psc = idx;
if (h.includes('stav') || h.includes('status')) headerMap.stav = idx;
if (h.includes('forma') || h.includes('form')) headerMap.forma = idx;
if (h.includes('faktur') && h.includes('cislo')) headerMap.fakturaCislo = idx;
if (h.includes('faktur') && h.includes('vystaven')) headerMap.fakturaVystavena = idx;
if (h.includes('zaplaten') || h.includes('paid')) headerMap.zaplatene = idx;
if (h.includes('poznam') || h.includes('note')) headerMap.poznamka = idx;
if (h.includes('pocet') || h.includes('count')) headerMap.pocetUcastnikov = idx;
});
console.log('Field mapping:', headerMap);
let importedCount = 0;
let skippedCount = 0;
for (const row of rows) {
try {
// Get email - required field
const email = cleanString(row[headers[headerMap.email]] || Object.values(row).find(v => String(v).includes('@')));
if (!email || !email.includes('@')) {
skippedCount++;
continue;
}
// Check if participant exists
let participant = await db.select().from(ucastnici).where(eq(ucastnici.email, email)).limit(1);
if (participant.length === 0) {
// Try to find name fields
let meno = cleanString(row[headers[headerMap.meno]]);
let priezvisko = cleanString(row[headers[headerMap.priezvisko]]);
// If no separate fields, try to split full name
if (!meno && !priezvisko) {
// Look for a name-like field
for (const [key, value] of Object.entries(row)) {
const val = cleanString(value);
if (val && !val.includes('@') && !val.includes('http') && val.length < 50) {
const parts = val.split(/\s+/);
if (parts.length >= 2) {
meno = parts[0];
priezvisko = parts.slice(1).join(' ');
break;
}
}
}
}
// Create participant
const [newParticipant] = await db.insert(ucastnici).values({
titul: cleanString(row[headers[headerMap.titul]]),
meno: meno || 'N/A',
priezvisko: priezvisko || 'N/A',
email: email,
telefon: cleanString(row[headers[headerMap.telefon]]),
firma: cleanString(row[headers[headerMap.firma]]),
mesto: cleanString(row[headers[headerMap.mesto]]),
ulica: cleanString(row[headers[headerMap.ulica]]),
psc: cleanString(row[headers[headerMap.psc]]),
}).returning();
participant = [newParticipant];
console.log(`Created participant: ${email}`);
}
const ucastnikId = participant[0].id;
// Check if registration exists
const existingReg = await db.select()
.from(registracie)
.where(and(eq(registracie.kurzId, kurzId), eq(registracie.ucastnikId, ucastnikId)))
.limit(1);
if (existingReg.length === 0) {
// Create registration
await db.insert(registracie).values({
kurzId: kurzId,
ucastnikId: ucastnikId,
formaKurzu: mapForma(row[headers[headerMap.forma]]),
pocetUcastnikov: parseInt(row[headers[headerMap.pocetUcastnikov]]) || 1,
fakturaCislo: cleanString(row[headers[headerMap.fakturaCislo]]),
fakturaVystavena: Boolean(row[headers[headerMap.fakturaVystavena]]),
zaplatene: Boolean(row[headers[headerMap.zaplatene]]),
stav: mapStav(row[headers[headerMap.stav]]),
poznamka: cleanString(row[headers[headerMap.poznamka]]),
});
importedCount++;
} else {
console.log(`Registration already exists for ${email} in ${sheetName}`);
skippedCount++;
}
} catch (error) {
console.error(`Error processing row:`, error.message);
skippedCount++;
}
}
console.log(`Imported ${importedCount} registrations, skipped ${skippedCount}`);
}
function extractCourseType(sheetName) {
const name = sheetName.toLowerCase();
if (name.includes('ai 1') || name.includes('ai1')) return 'AI 1';
if (name.includes('ai 2') || name.includes('ai2')) return 'AI 2';
if (name.includes('seo')) return 'SEO';
if (name.includes('marketing')) return 'Marketing';
return 'AI';
}
// Run the import
importAiKurzy()
.then(() => {
console.log('Import finished successfully');
process.exit(0);
})
.catch((error) => {
console.error('Import failed:', error);
process.exit(1);
});