Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions src/main/ipc/apps.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
const { ipcMain } = require('electron');
const { spawn } = require('child_process');
const fsPromises = require('fs').promises;
const path = require('path');

const {
getAllApps,
Expand All @@ -12,7 +10,6 @@ const {
restoreApp,
deleteAppAndSessions,
addToBlacklist,
moveAppToCategory
} = require('../services/data-access');

const { formatTime, formatLastUsed } = require('../utils/utils');
Expand Down
1 change: 0 additions & 1 deletion src/main/services/app-tracking.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const activeWin = require('active-win');
const path = require('path');
const fs = require('fs');
const { getDb } = require('./database');

Expand Down
3 changes: 1 addition & 2 deletions src/main/services/data-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,10 @@ module.exports = {
saveBlacklistedApps,
loadBlacklistedApps,
removeAppSessions,
saveSessionsData,

get appData() { return appData; },
get sessionsData() { return sessionsData; },
set appData(value) { appData = value; },
set sessionsData(value) { sessionsData = value; },
get categoriesData() { return categoriesData; },
}
};
213 changes: 0 additions & 213 deletions src/main/services/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,14 @@ async function initDatabase() {
: path.join(app.getPath('userData'), 'data');
const dbPath = path.join(dataDir, 'tracker.db');

console.log('Data directory path:', dataDir);
console.log('Database path:', dbPath);

// Create data directory if it doesn't exist
try {
if (!fs.existsSync(dataDir)) {
console.log('Data directory does not exist, creating...');
fs.mkdirSync(dataDir, { recursive: true });
console.log('Data directory created successfully');
} else {
console.log('Data directory exists, checking if it is a directory...');
// Check if it's actually a directory and not a file
const stats = fs.statSync(dataDir);
if (!stats.isDirectory()) {
console.log('Data path is a file, not a directory. Removing and creating directory...');
// If it's a file, remove it and create directory
fs.unlinkSync(dataDir);
fs.mkdirSync(dataDir, { recursive: true });
Expand All @@ -41,9 +34,7 @@ async function initDatabase() {
throw new Error(`Failed to create data directory: ${err.message}`);
}

console.log('Creating database at:', dbPath);
db = new Database(dbPath);
console.log('Database created successfully');

// Enable foreign keys
db.exec('PRAGMA foreign_keys = ON');
Expand Down Expand Up @@ -108,212 +99,10 @@ async function initDatabase() {
CREATE INDEX IF NOT EXISTS idx_apps_hidden ON apps(hidden);
`);

// Run migrations for productivity levels
migrateProductivityLevels();

console.log('Database initialized successfully');
return db;
}

function migrateFromJSON() {
console.log('Starting migration from JSON to SQLite...');

const isDev = !app.isPackaged;
const dataDir = isDev
? path.join(__dirname, '../../../data')
: path.join(app.getPath('userData'), 'data');
const appsFile = path.join(dataDir, 'apps.json');
const sessionsFile = path.join(dataDir, 'sessions.json');
const categoriesFile = path.join(dataDir, 'categories.json');
const favoritesFile = path.join(dataDir, 'favorites.json');
const blacklistFile = path.join(dataDir, 'blacklist.json');

db.exec('BEGIN TRANSACTION');

try {
// Migrate categories first
if (fs.existsSync(categoriesFile)) {
const categories = JSON.parse(fs.readFileSync(categoriesFile, 'utf8'));
const stmt = db.prepare(`
INSERT OR IGNORE INTO categories (id, name, color, icon, is_default, created_at)
VALUES (?, ?, ?, ?, ?, ?)
`);

for (const cat of categories) {
stmt.run(
cat.id,
cat.name,
cat.color || '#092442',
cat.icon || '📁',
cat.isDefault ? 1 : 0,
cat.createdAt ? new Date(cat.createdAt).getTime() : Date.now()
);
}
console.log(`Migrated ${categories.length} categories`);
}

// Migrate apps
if (fs.existsSync(appsFile)) {
const apps = JSON.parse(fs.readFileSync(appsFile, 'utf8'));
const stmt = db.prepare(`
INSERT OR REPLACE INTO apps
(id, name, path, executable, category, icon_path, hidden, first_used, last_used, total_time, launch_count)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);

let appCount = 0;
for (const [id, app] of Object.entries(apps)) {
stmt.run(
id,
app.name,
app.path || null,
app.executable || null,
app.category || 'Uncategorized',
app.iconPath || null,
app.hidden ? 1 : 0,
app.firstUsed ? new Date(app.firstUsed).getTime() : null,
app.lastUsed ? new Date(app.lastUsed).getTime() : null,
app.totalTime || 0,
app.launchCount || 0
);
appCount++;
}
console.log(`Migrated ${appCount} apps`);
}

// Migrate sessions
if (fs.existsSync(sessionsFile)) {
const sessions = JSON.parse(fs.readFileSync(sessionsFile, 'utf8'));
const stmt = db.prepare(`
INSERT OR IGNORE INTO sessions (id, app_id, start_time, end_time, duration)
VALUES (?, ?, ?, ?, ?)
`);

let sessionCount = 0;
for (const session of sessions) {
const startTime = typeof session.startTime === 'string'
? new Date(session.startTime).getTime()
: session.startTime;

const endTime = session.endTime
? (typeof session.endTime === 'string'
? new Date(session.endTime).getTime()
: session.endTime)
: null;

stmt.run(
session.id,
session.appId,
startTime,
endTime,
session.duration || 0
);
sessionCount++;
}
console.log(`Migrated ${sessionCount} sessions`);
}

// Migrate favorites
if (fs.existsSync(favoritesFile)) {
const favorites = JSON.parse(fs.readFileSync(favoritesFile, 'utf8'));
const stmt = db.prepare(`
INSERT OR IGNORE INTO favorites (app_id) VALUES (?)
`);

for (const appId of favorites) {
stmt.run(appId);
}
console.log(`Migrated ${favorites.length} favorites`);
}

// Migrate blacklist
if (fs.existsSync(blacklistFile)) {
const blacklist = JSON.parse(fs.readFileSync(blacklistFile, 'utf8'));
const stmt = db.prepare(`
INSERT OR IGNORE INTO blacklist (id, name, path, executable, blacklisted_at, reason)
VALUES (?, ?, ?, ?, ?, ?)
`);

for (const item of blacklist) {
stmt.run(
item.id || `blacklist_${Date.now()}`,
item.name,
item.path,
item.executable,
item.blacklistedAt ? new Date(item.blacklistedAt).getTime() : Date.now(),
item.reason || 'user_removed_permanently'
);
}
console.log(`Migrated ${blacklist.length} blacklisted items`);
}

db.exec('COMMIT');
console.log('Migration completed successfully!');

// Backup JSON files
const backupDir = path.join(dataDir, 'json_backup_' + Date.now());
fs.mkdirSync(backupDir, { recursive: true });

if (fs.existsSync(appsFile)) fs.renameSync(appsFile, path.join(backupDir, 'apps.json'));
if (fs.existsSync(sessionsFile)) fs.renameSync(sessionsFile, path.join(backupDir, 'sessions.json'));
if (fs.existsSync(categoriesFile)) fs.renameSync(categoriesFile, path.join(backupDir, 'categories.json'));
if (fs.existsSync(favoritesFile)) fs.renameSync(favoritesFile, path.join(backupDir, 'favorites.json'));
if (fs.existsSync(blacklistFile)) fs.renameSync(blacklistFile, path.join(backupDir, 'blacklist.json'));

console.log(`JSON files backed up to: ${backupDir}`);

} catch (error) {
db.exec('ROLLBACK');
console.error('Migration failed:', error);
throw error;
}
}

// Check if migration is needed
function checkMigration() {
const isDev = !app.isPackaged;
const dataDir = isDev
? path.join(__dirname, '../../../data')
: path.join(app.getPath('userData'), 'data');
const appsFile = path.join(dataDir, 'apps.json');
const dbFile = path.join(dataDir, 'tracker.db');

// If database doesn't exist but JSON files do, run migration
if (!fs.existsSync(dbFile) && fs.existsSync(appsFile)) {
migrateFromJSON();
}
}

// Migration for adding productivity level columns
function migrateProductivityLevels() {
try {
// Check if columns already exist
const categoriesInfo = db.pragma('table_info(categories)');
const appsInfo = db.pragma('table_info(apps)');

const hasProductivityInCategories = categoriesInfo.some(col => col.name === 'productivity_level');
const hasProductivityInApps = appsInfo.some(col => col.name === 'productivity_level_override');

// Add productivity_level to categories if it doesn't exist
if (!hasProductivityInCategories) {
db.exec(`
ALTER TABLE categories ADD COLUMN productivity_level TEXT DEFAULT 'neutral';
`);
console.log('Added productivity_level column to categories table');
}

// Add productivity_level_override to apps if it doesn't exist
if (!hasProductivityInApps) {
db.exec(`
ALTER TABLE apps ADD COLUMN productivity_level_override TEXT;
`);
console.log('Added productivity_level_override column to apps table');
}
} catch (error) {
console.error('Error migrating productivity levels:', error);
}
}

function getDb() {
if (!db) {
throw new Error('Database not initialized. Call initDatabase() first.');
Expand All @@ -323,7 +112,5 @@ function getDb() {

module.exports = {
initDatabase,
migrateFromJSON,
checkMigration,
getDb
};
2 changes: 1 addition & 1 deletion src/preload/preload.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { contextBridge, ipcRenderer, createCollection } = require('electron');
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
// Window controls
Expand Down
22 changes: 9 additions & 13 deletions src/renderer/js/analytics/analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,12 @@ function updateCustomDatesForPeriod(period, dateInputs) {

// Listen for data from parent window
window.addEventListener('message', (event) => {
// Verify message comes from parent window
if (event.source !== window.parent) {
return;
}
if (event.data.type === 'ANALYTICS_DATA_RESPONSE') {
// Received analytics data from parent window
console.log('Received analytics data:', event.data.data);
updateAnalyticsUI(event.data.data);

// Load heatmap data after main UI renders
Expand All @@ -90,22 +93,19 @@ window.addEventListener('message', (event) => {
}, 50);
} else if (event.data.type === 'HEATMAP_DATA_RESPONSE') {
// Received heatmap data
console.log('Received heatmap data');
if (currentAnalyticsData) {
updateHourlyHeatmap(event.data.data, currentAnalyticsData.topApps);
}
} else if (event.data.type === 'CATEGORIES_RESPONSE') {
// Received categories from parent window
console.log('Received categories:', event.data.categories);
// Store categories in cache
event.data.categories.forEach(cat => {
categoriesCache[cat.name] = cat.color || '#092442';
categoriesCache.set(cat.name, cat.color || '#092442');
});
}
});

let currentPeriod = 'today';
let categoriesCache = {}; // Cache for category colors
let categoriesCache = new Map(); // Cache for category colors
let heatmapAppCount = 5; // Default to top 5 apps
let currentAnalyticsData = null; // Cache analytics data for heatmap updates

Expand Down Expand Up @@ -134,7 +134,7 @@ function calculateDateRange(period) {
endDate = today;
break;
case 'alltime':
startDate = new Date(2020, 0, 1); // Jan 1, 2020
startDate = new Date(2025, 7, 1);
endDate = today;
break;
}
Expand Down Expand Up @@ -224,7 +224,6 @@ function updateStatsCards(data) {
}

function updateTopAppsList(topApps) {
console.log(topApps)
const top_apps_list = document.querySelector('.top-apps-list');
if (!top_apps_list) return;

Expand Down Expand Up @@ -255,7 +254,6 @@ function updateTopAppsList(topApps) {
}

function updateAllApplications(allApps){
console.log(allApps)
const top_apps_list = document.querySelector('.apps-used-grid');
if (!top_apps_list) return;

Expand Down Expand Up @@ -291,7 +289,7 @@ function updateAllApplications(allApps){

top_apps_list.innerHTML += `
<div class="recent-item" data-app-name="${escapeHtml(app.name)}">
<div class="recent-item-bg" style="background: ${categoriesCache[app.category] || '#092442'};">
<div class="recent-item-bg" style="background: ${categoriesCache.get(app.category) || '#092442'};">
${iconHtml}
</div>
<div class="recent-item-info">
Expand All @@ -304,8 +302,6 @@ function updateAllApplications(allApps){
}

function updateCategoryBreakdown(categoryBreakdown) {
console.log('Category breakdown:', categoryBreakdown);

const category_grid = document.querySelector('.category-grid');
if (!category_grid) return;

Expand Down Expand Up @@ -345,7 +341,7 @@ function updateCategoryBreakdown(categoryBreakdown) {
const percentage = Math.round((category.total_time / totalTime) * 100);

category_grid.innerHTML += `
<div class="category-item" style="border-left-color: ${categoriesCache[category.category] || '#66c0f4'}">
<div class="category-item" style="border-left-color: ${categoriesCache.get(category.category) || '#66c0f4'}">
<div class="category-name">${escapeHtml(category.category)}</div>
<div class="category-time">${formatTime(category.total_time)}</div>
<div class="category-percentage">${percentage}% of total time</div>
Expand Down
1 change: 0 additions & 1 deletion src/renderer/js/analytics/daily-usage-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ function renderSingleDayCanvasChart(ctx, canvas, dayData) {
}

function updateDailyUsageChart(dailyBreakdown) {
console.log('Daily breakdown:', dailyBreakdown);
const canvas = document.getElementById('daily-usage-canvas');
if (!canvas) return;

Expand Down
Loading