Files
pdga-rating/src/db.js
T

203 lines
7.2 KiB
JavaScript

const sqlite3 = require('sqlite3').verbose();
const logger = require('./logger');
const dbPath = process.env.DB_PATH || './ratings.db';
const db = new sqlite3.Database(dbPath);
function initializeDatabase() {
return new Promise((resolve, reject) => {
db.serialize(() => {
db.run(`
CREATE TABLE IF NOT EXISTS players (
id INTEGER PRIMARY KEY AUTOINCREMENT,
pdga_number INTEGER UNIQUE NOT NULL,
name TEXT NOT NULL,
current_rating INTEGER,
rating_change INTEGER,
last_updated DATETIME DEFAULT CURRENT_TIMESTAMP,
last_round_update DATETIME DEFAULT NULL
)
`);
db.get("PRAGMA table_info(players)", (err, info) => {
if (err) {
logger.error('Error checking table schema:', err);
return;
}
db.all("PRAGMA table_info(players)", (err, columns) => {
if (err) {
logger.error('Error getting table info:', err);
return;
}
const hasLastRoundUpdate = columns.some(col => col.name === 'last_round_update');
const hasPredictedRating = columns.some(col => col.name === 'predicted_rating');
const hasStdDev = columns.some(col => col.name === 'std_dev');
const hasExcludedRoundsCount = columns.some(col => col.name === 'excluded_rounds_count');
const hasCutoffRating = columns.some(col => col.name === 'cutoff_rating');
if (!hasLastRoundUpdate) {
logger.info('Adding last_round_update column to players table...');
db.run(`ALTER TABLE players ADD COLUMN last_round_update DATETIME DEFAULT NULL`, (err) => {
if (err) logger.error('Error adding last_round_update column:', err.message);
else logger.info('Successfully added last_round_update column');
});
}
if (!hasPredictedRating) {
logger.info('Adding predicted_rating column to players table...');
db.run(`ALTER TABLE players ADD COLUMN predicted_rating INTEGER DEFAULT NULL`, (err) => {
if (err) logger.error('Error adding predicted_rating column:', err.message);
else logger.info('Successfully added predicted_rating column');
});
}
if (!hasStdDev) {
logger.info('Adding std_dev column to players table...');
db.run(`ALTER TABLE players ADD COLUMN std_dev INTEGER DEFAULT NULL`, (err) => {
if (err) logger.error('Error adding std_dev column:', err.message);
else logger.info('Successfully added std_dev column');
});
}
if (!hasExcludedRoundsCount) {
logger.info('Adding excluded_rounds_count column to players table...');
db.run(`ALTER TABLE players ADD COLUMN excluded_rounds_count INTEGER DEFAULT NULL`, (err) => {
if (err) logger.error('Error adding excluded_rounds_count column:', err.message);
else logger.info('Successfully added excluded_rounds_count column');
});
}
if (!hasCutoffRating) {
logger.info('Adding cutoff_rating column to players table...');
db.run(`ALTER TABLE players ADD COLUMN cutoff_rating INTEGER DEFAULT NULL`, (err) => {
if (err) logger.error('Error adding cutoff_rating column:', err.message);
else logger.info('Successfully added cutoff_rating column');
});
}
});
});
db.run(`
CREATE TABLE IF NOT EXISTS round_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
player_id INTEGER NOT NULL,
date DATE NOT NULL,
competition_name TEXT NOT NULL,
rating INTEGER NOT NULL,
FOREIGN KEY (player_id) REFERENCES players (id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS rating_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
player_id INTEGER NOT NULL,
date DATE NOT NULL,
rating INTEGER NOT NULL,
FOREIGN KEY (player_id) REFERENCES players (id)
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS courses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
link TEXT UNIQUE NOT NULL,
city TEXT,
last_updated DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
db.run(`
CREATE TABLE IF NOT EXISTS layouts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
course_id INTEGER NOT NULL,
name TEXT NOT NULL,
par INTEGER NOT NULL,
mean_rating INTEGER,
rating_count INTEGER DEFAULT 0,
last_calculated DATETIME,
FOREIGN KEY (course_id) REFERENCES courses (id),
UNIQUE(course_id, name, par)
)
`, (err) => {
if (err) {
reject(err);
} else {
db.run(`ALTER TABLE layouts ADD COLUMN mean_rating INTEGER`, () => {
db.run(`ALTER TABLE layouts ADD COLUMN rating_count INTEGER DEFAULT 0`, () => {
db.run(`ALTER TABLE layouts ADD COLUMN last_calculated DATETIME`, () => {
db.run(`ALTER TABLE layouts ADD COLUMN last_played DATE`, () => {
logger.info('Database initialized successfully');
resolve();
});
});
});
});
}
});
});
});
}
async function checkAndPopulateDatabase() {
const fs = require('fs');
const { scrapePDGARating } = require('./services/player-service');
try {
const playerCount = await new Promise((resolve, reject) => {
db.get('SELECT COUNT(*) as count FROM players', [], (err, row) => {
if (err) reject(err);
else resolve(row.count);
});
});
if (playerCount > 0) {
logger.info(`✓ Database already has ${playerCount} players - skipping text file import`);
logger.info('📝 Note: pdga-numbers.txt is only used when database is empty');
return;
}
logger.info('=== Database is empty - populating from PDGA numbers file ===');
const pdgaNumbers = fs.readFileSync('pdga-numbers.txt', 'utf-8')
.split('\n')
.map(num => num.trim())
.filter(num => num);
logger.info(`Found ${pdgaNumbers.length} PDGA numbers in file`);
if (pdgaNumbers.length === 0) {
logger.info('⚠ No PDGA numbers found in file');
return;
}
logger.info('Populating database with players from file...');
for (let i = 0; i < pdgaNumbers.length; i++) {
const pdgaNumber = pdgaNumbers[i];
logger.info(`[${i + 1}/${pdgaNumbers.length}] Adding PDGA ${pdgaNumber}...`);
try {
const playerData = await scrapePDGARating(pdgaNumber);
logger.info(` ✓ Added ${playerData.name}`);
if (i < pdgaNumbers.length - 1) {
await new Promise(resolve => setTimeout(resolve, 2000));
}
} catch (error) {
logger.error(` ✗ Failed to add PDGA ${pdgaNumber}:`, error.message);
}
}
logger.info('=== Database population complete ===');
} catch (error) {
logger.error('Error during database population check:', error.message);
}
}
module.exports = { db, initializeDatabase, checkAndPopulateDatabase };