GameSentenceMiner 2.19.16__py3-none-any.whl → 2.20.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of GameSentenceMiner might be problematic. Click here for more details.
- GameSentenceMiner/__init__.py +39 -0
- GameSentenceMiner/anki.py +6 -3
- GameSentenceMiner/gametext.py +13 -2
- GameSentenceMiner/gsm.py +40 -3
- GameSentenceMiner/locales/en_us.json +4 -0
- GameSentenceMiner/locales/ja_jp.json +4 -0
- GameSentenceMiner/locales/zh_cn.json +4 -0
- GameSentenceMiner/obs.py +4 -1
- GameSentenceMiner/owocr/owocr/ocr.py +304 -134
- GameSentenceMiner/owocr/owocr/run.py +1 -1
- GameSentenceMiner/ui/anki_confirmation.py +4 -2
- GameSentenceMiner/ui/config_gui.py +12 -0
- GameSentenceMiner/util/configuration.py +6 -2
- GameSentenceMiner/util/cron/__init__.py +12 -0
- GameSentenceMiner/util/cron/daily_rollup.py +613 -0
- GameSentenceMiner/util/cron/jiten_update.py +397 -0
- GameSentenceMiner/util/cron/populate_games.py +154 -0
- GameSentenceMiner/util/cron/run_crons.py +148 -0
- GameSentenceMiner/util/cron/setup_populate_games_cron.py +118 -0
- GameSentenceMiner/util/cron_table.py +334 -0
- GameSentenceMiner/util/db.py +236 -49
- GameSentenceMiner/util/ffmpeg.py +23 -4
- GameSentenceMiner/util/games_table.py +340 -93
- GameSentenceMiner/util/jiten_api_client.py +188 -0
- GameSentenceMiner/util/stats_rollup_table.py +216 -0
- GameSentenceMiner/web/anki_api_endpoints.py +438 -220
- GameSentenceMiner/web/database_api.py +955 -1259
- GameSentenceMiner/web/jiten_database_api.py +1015 -0
- GameSentenceMiner/web/rollup_stats.py +672 -0
- GameSentenceMiner/web/static/css/dashboard-shared.css +75 -13
- GameSentenceMiner/web/static/css/overview.css +604 -47
- GameSentenceMiner/web/static/css/search.css +226 -0
- GameSentenceMiner/web/static/css/shared.css +762 -0
- GameSentenceMiner/web/static/css/stats.css +221 -0
- GameSentenceMiner/web/static/js/components/bar-chart.js +339 -0
- GameSentenceMiner/web/static/js/database-bulk-operations.js +320 -0
- GameSentenceMiner/web/static/js/database-game-data.js +390 -0
- GameSentenceMiner/web/static/js/database-game-operations.js +213 -0
- GameSentenceMiner/web/static/js/database-helpers.js +44 -0
- GameSentenceMiner/web/static/js/database-jiten-integration.js +750 -0
- GameSentenceMiner/web/static/js/database-popups.js +89 -0
- GameSentenceMiner/web/static/js/database-tabs.js +64 -0
- GameSentenceMiner/web/static/js/database-text-management.js +371 -0
- GameSentenceMiner/web/static/js/database.js +86 -718
- GameSentenceMiner/web/static/js/goals.js +79 -18
- GameSentenceMiner/web/static/js/heatmap.js +29 -23
- GameSentenceMiner/web/static/js/overview.js +1205 -339
- GameSentenceMiner/web/static/js/regex-patterns.js +100 -0
- GameSentenceMiner/web/static/js/search.js +215 -18
- GameSentenceMiner/web/static/js/shared.js +193 -39
- GameSentenceMiner/web/static/js/stats.js +1536 -179
- GameSentenceMiner/web/stats.py +1142 -269
- GameSentenceMiner/web/stats_api.py +2104 -0
- GameSentenceMiner/web/templates/anki_stats.html +4 -18
- GameSentenceMiner/web/templates/components/date-range.html +118 -3
- GameSentenceMiner/web/templates/components/html-head.html +40 -6
- GameSentenceMiner/web/templates/components/js-config.html +8 -8
- GameSentenceMiner/web/templates/components/regex-input.html +160 -0
- GameSentenceMiner/web/templates/database.html +564 -117
- GameSentenceMiner/web/templates/goals.html +41 -5
- GameSentenceMiner/web/templates/overview.html +159 -129
- GameSentenceMiner/web/templates/search.html +78 -9
- GameSentenceMiner/web/templates/stats.html +159 -5
- GameSentenceMiner/web/texthooking_page.py +280 -111
- {gamesentenceminer-2.19.16.dist-info → gamesentenceminer-2.20.0.dist-info}/METADATA +43 -2
- {gamesentenceminer-2.19.16.dist-info → gamesentenceminer-2.20.0.dist-info}/RECORD +70 -47
- {gamesentenceminer-2.19.16.dist-info → gamesentenceminer-2.20.0.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.19.16.dist-info → gamesentenceminer-2.20.0.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.19.16.dist-info → gamesentenceminer-2.20.0.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.19.16.dist-info → gamesentenceminer-2.20.0.dist-info}/top_level.txt +0 -0
|
@@ -74,7 +74,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
74
74
|
// Calculate current progress
|
|
75
75
|
const currentHours = allGamesStats.total_time_hours || 0;
|
|
76
76
|
const currentCharacters = allGamesStats.total_characters || 0;
|
|
77
|
-
const currentGames = allGamesStats.
|
|
77
|
+
const currentGames = allGamesStats.completed_games || 0;
|
|
78
78
|
|
|
79
79
|
// Calculate daily averages for projections using 90-day lookback period (reusing logic from stats.js)
|
|
80
80
|
const dailyHoursAvg = calculateDailyAverage(allLinesData, 'hours');
|
|
@@ -299,6 +299,25 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
299
299
|
document.getElementById('charsDaysRemaining').style.display = 'none';
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
+
// Update cards mined goal
|
|
303
|
+
const cardsGoalItem = document.getElementById('cardsGoalItem');
|
|
304
|
+
if (data.cards && data.cards.has_target) {
|
|
305
|
+
hasAnyTarget = true;
|
|
306
|
+
cardsGoalItem.style.display = 'block';
|
|
307
|
+
|
|
308
|
+
document.getElementById('todayCardsProgress').textContent = data.cards.progress;
|
|
309
|
+
document.getElementById('todayCardsRequired').textContent = data.cards.required;
|
|
310
|
+
|
|
311
|
+
// Add green highlight if goal is met
|
|
312
|
+
if (data.cards.progress >= data.cards.required) {
|
|
313
|
+
cardsGoalItem.classList.add('goal-met');
|
|
314
|
+
} else {
|
|
315
|
+
cardsGoalItem.classList.remove('goal-met');
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
cardsGoalItem.style.display = 'none';
|
|
319
|
+
}
|
|
320
|
+
|
|
302
321
|
// Show/hide sections based on whether any targets are set
|
|
303
322
|
if (hasAnyTarget) {
|
|
304
323
|
document.getElementById('noTargetsMessage').style.display = 'none';
|
|
@@ -333,33 +352,48 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
333
352
|
document.getElementById('projectionHoursValue').textContent =
|
|
334
353
|
Math.floor(data.hours.projection).toLocaleString() + 'h';
|
|
335
354
|
|
|
355
|
+
// Update label with target date (formatted in user's locale)
|
|
356
|
+
const hoursLabel = document.getElementById('hoursProjectionLabel');
|
|
357
|
+
if (hoursLabel) {
|
|
358
|
+
const targetDate = new Date(data.hours.target_date);
|
|
359
|
+
const formattedTargetDate = targetDate.toLocaleDateString(navigator.language);
|
|
360
|
+
hoursLabel.textContent = `Total Hours by ${formattedTargetDate}`;
|
|
361
|
+
}
|
|
362
|
+
|
|
336
363
|
// Calculate percentage difference
|
|
337
364
|
const hoursPercentDiff = ((data.hours.projection - data.hours.target) / data.hours.target) * 100;
|
|
338
365
|
|
|
339
|
-
//
|
|
366
|
+
// Calculate projected completion date
|
|
367
|
+
const hoursRemaining = Math.max(0, data.hours.target - data.hours.current);
|
|
368
|
+
const hoursDaysToComplete = data.hours.daily_average > 0 ? Math.ceil(hoursRemaining / data.hours.daily_average) : 0;
|
|
369
|
+
const hoursCompletionDate = new Date();
|
|
370
|
+
hoursCompletionDate.setDate(hoursCompletionDate.getDate() + hoursDaysToComplete);
|
|
371
|
+
const hoursCompletionDateStr = hoursCompletionDate.toLocaleDateString(navigator.language);
|
|
372
|
+
|
|
373
|
+
// Status message with pace badge and completion date
|
|
340
374
|
const hoursStatus = document.getElementById('hoursProjectionStatus');
|
|
341
375
|
if (hoursPercentDiff >= 5) {
|
|
342
376
|
// Over-achieving by 5% or more
|
|
343
377
|
const badge = `<span class="pace-badge pace-ahead">+${Math.floor(hoursPercentDiff)}%</span>`;
|
|
344
|
-
hoursStatus.innerHTML = `On Track! 🎉 ${badge}
|
|
378
|
+
hoursStatus.innerHTML = `On Track! 🎉 ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${hoursCompletionDateStr}</small>`;
|
|
345
379
|
hoursStatus.className = 'dashboard-progress-value positive';
|
|
346
380
|
} else if (hoursPercentDiff >= -5) {
|
|
347
381
|
// Within ±5% - perfect pace
|
|
348
382
|
const badge = `<span class="pace-badge pace-perfect">±${Math.abs(Math.floor(hoursPercentDiff))}%</span>`;
|
|
349
|
-
hoursStatus.innerHTML = `Perfect Pace! ✅ ${badge}
|
|
383
|
+
hoursStatus.innerHTML = `Perfect Pace! ✅ ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${hoursCompletionDateStr}</small>`;
|
|
350
384
|
hoursStatus.className = 'dashboard-progress-value positive';
|
|
351
385
|
} else if (hoursPercentDiff >= -15) {
|
|
352
386
|
// Slightly behind (-5% to -15%)
|
|
353
387
|
const shortfall = data.hours.target - data.hours.projection;
|
|
354
388
|
const badge = `<span class="pace-badge pace-behind-mild">${Math.floor(hoursPercentDiff)}%</span>`;
|
|
355
|
-
hoursStatus.innerHTML = `${Math.floor(shortfall)}h short ${badge}
|
|
389
|
+
hoursStatus.innerHTML = `${Math.floor(shortfall)}h short ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${hoursCompletionDateStr}</small>`;
|
|
356
390
|
hoursStatus.className = 'dashboard-progress-value';
|
|
357
391
|
hoursStatus.style.color = 'var(--warning-color)';
|
|
358
392
|
} else {
|
|
359
393
|
// Significantly behind (< -15%)
|
|
360
394
|
const shortfall = data.hours.target - data.hours.projection;
|
|
361
395
|
const badge = `<span class="pace-badge pace-behind">${Math.floor(hoursPercentDiff)}%</span>`;
|
|
362
|
-
hoursStatus.innerHTML = `${Math.floor(shortfall)}h short ${badge}
|
|
396
|
+
hoursStatus.innerHTML = `${Math.floor(shortfall)}h short ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${hoursCompletionDateStr}</small>`;
|
|
363
397
|
hoursStatus.className = 'dashboard-progress-value';
|
|
364
398
|
hoursStatus.style.color = 'var(--danger-color)';
|
|
365
399
|
}
|
|
@@ -376,33 +410,48 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
376
410
|
|
|
377
411
|
document.getElementById('projectionCharsValue').textContent = formatGoalNumber(data.characters.projection);
|
|
378
412
|
|
|
413
|
+
// Update label with target date (formatted in user's locale)
|
|
414
|
+
const charsLabel = document.getElementById('charsProjectionLabel');
|
|
415
|
+
if (charsLabel) {
|
|
416
|
+
const targetDate = new Date(data.characters.target_date);
|
|
417
|
+
const formattedTargetDate = targetDate.toLocaleDateString(navigator.language);
|
|
418
|
+
charsLabel.textContent = `Total Characters by ${formattedTargetDate}`;
|
|
419
|
+
}
|
|
420
|
+
|
|
379
421
|
// Calculate percentage difference
|
|
380
422
|
const charsPercentDiff = ((data.characters.projection - data.characters.target) / data.characters.target) * 100;
|
|
381
423
|
|
|
382
|
-
//
|
|
424
|
+
// Calculate projected completion date
|
|
425
|
+
const charsRemaining = Math.max(0, data.characters.target - data.characters.current);
|
|
426
|
+
const charsDaysToComplete = data.characters.daily_average > 0 ? Math.ceil(charsRemaining / data.characters.daily_average) : 0;
|
|
427
|
+
const charsCompletionDate = new Date();
|
|
428
|
+
charsCompletionDate.setDate(charsCompletionDate.getDate() + charsDaysToComplete);
|
|
429
|
+
const charsCompletionDateStr = charsCompletionDate.toLocaleDateString(navigator.language);
|
|
430
|
+
|
|
431
|
+
// Status message with pace badge and completion date
|
|
383
432
|
const charsStatus = document.getElementById('charsProjectionStatus');
|
|
384
433
|
if (charsPercentDiff >= 5) {
|
|
385
434
|
// Over-achieving by 5% or more
|
|
386
435
|
const badge = `<span class="pace-badge pace-ahead">+${Math.floor(charsPercentDiff)}%</span>`;
|
|
387
|
-
charsStatus.innerHTML = `On Track! 🎉 ${badge}
|
|
436
|
+
charsStatus.innerHTML = `On Track! 🎉 ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${charsCompletionDateStr}</small>`;
|
|
388
437
|
charsStatus.className = 'dashboard-progress-value positive';
|
|
389
438
|
} else if (charsPercentDiff >= -5) {
|
|
390
439
|
// Within ±5% - perfect pace
|
|
391
440
|
const badge = `<span class="pace-badge pace-perfect">±${Math.abs(Math.floor(charsPercentDiff))}%</span>`;
|
|
392
|
-
charsStatus.innerHTML = `Perfect Pace! ✅ ${badge}
|
|
441
|
+
charsStatus.innerHTML = `Perfect Pace! ✅ ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${charsCompletionDateStr}</small>`;
|
|
393
442
|
charsStatus.className = 'dashboard-progress-value positive';
|
|
394
443
|
} else if (charsPercentDiff >= -15) {
|
|
395
444
|
// Slightly behind (-5% to -15%)
|
|
396
445
|
const shortfall = data.characters.target - data.characters.projection;
|
|
397
446
|
const badge = `<span class="pace-badge pace-behind-mild">${Math.floor(charsPercentDiff)}%</span>`;
|
|
398
|
-
charsStatus.innerHTML = `${formatGoalNumber(shortfall)} short ${badge}
|
|
447
|
+
charsStatus.innerHTML = `${formatGoalNumber(shortfall)} short ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${charsCompletionDateStr}</small>`;
|
|
399
448
|
charsStatus.className = 'dashboard-progress-value';
|
|
400
449
|
charsStatus.style.color = 'var(--warning-color)';
|
|
401
450
|
} else {
|
|
402
451
|
// Significantly behind (< -15%)
|
|
403
452
|
const shortfall = data.characters.target - data.characters.projection;
|
|
404
453
|
const badge = `<span class="pace-badge pace-behind">${Math.floor(charsPercentDiff)}%</span>`;
|
|
405
|
-
charsStatus.innerHTML = `${formatGoalNumber(shortfall)} short ${badge}
|
|
454
|
+
charsStatus.innerHTML = `${formatGoalNumber(shortfall)} short ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${charsCompletionDateStr}</small>`;
|
|
406
455
|
charsStatus.className = 'dashboard-progress-value';
|
|
407
456
|
charsStatus.style.color = 'var(--danger-color)';
|
|
408
457
|
}
|
|
@@ -422,30 +471,37 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
422
471
|
// Calculate percentage difference
|
|
423
472
|
const gamesPercentDiff = ((data.games.projection - data.games.target) / data.games.target) * 100;
|
|
424
473
|
|
|
425
|
-
//
|
|
474
|
+
// Calculate projected completion date
|
|
475
|
+
const gamesRemaining = Math.max(0, data.games.target - data.games.current);
|
|
476
|
+
const gamesDaysToComplete = data.games.daily_average > 0 ? Math.ceil(gamesRemaining / data.games.daily_average) : 0;
|
|
477
|
+
const gamesCompletionDate = new Date();
|
|
478
|
+
gamesCompletionDate.setDate(gamesCompletionDate.getDate() + gamesDaysToComplete);
|
|
479
|
+
const gamesCompletionDateStr = gamesCompletionDate.toLocaleDateString(navigator.language);
|
|
480
|
+
|
|
481
|
+
// Status message with pace badge and completion date
|
|
426
482
|
const gamesStatus = document.getElementById('gamesProjectionStatus');
|
|
427
483
|
if (gamesPercentDiff >= 5) {
|
|
428
484
|
// Over-achieving by 5% or more
|
|
429
485
|
const badge = `<span class="pace-badge pace-ahead">+${Math.floor(gamesPercentDiff)}%</span>`;
|
|
430
|
-
gamesStatus.innerHTML = `On Track! 🎉 ${badge}
|
|
486
|
+
gamesStatus.innerHTML = `On Track! 🎉 ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${gamesCompletionDateStr}</small>`;
|
|
431
487
|
gamesStatus.className = 'dashboard-progress-value positive';
|
|
432
488
|
} else if (gamesPercentDiff >= -5) {
|
|
433
489
|
// Within ±5% - perfect pace
|
|
434
490
|
const badge = `<span class="pace-badge pace-perfect">±${Math.abs(Math.floor(gamesPercentDiff))}%</span>`;
|
|
435
|
-
gamesStatus.innerHTML = `Perfect Pace! ✅ ${badge}
|
|
491
|
+
gamesStatus.innerHTML = `Perfect Pace! ✅ ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${gamesCompletionDateStr}</small>`;
|
|
436
492
|
gamesStatus.className = 'dashboard-progress-value positive';
|
|
437
493
|
} else if (gamesPercentDiff >= -15) {
|
|
438
494
|
// Slightly behind (-5% to -15%)
|
|
439
495
|
const shortfall = data.games.target - data.games.projection;
|
|
440
496
|
const badge = `<span class="pace-badge pace-behind-mild">${Math.floor(gamesPercentDiff)}%</span>`;
|
|
441
|
-
gamesStatus.innerHTML = `${shortfall} short ${badge}
|
|
497
|
+
gamesStatus.innerHTML = `${shortfall} short ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${gamesCompletionDateStr}</small>`;
|
|
442
498
|
gamesStatus.className = 'dashboard-progress-value';
|
|
443
499
|
gamesStatus.style.color = 'var(--warning-color)';
|
|
444
500
|
} else {
|
|
445
501
|
// Significantly behind (< -15%)
|
|
446
502
|
const shortfall = data.games.target - data.games.projection;
|
|
447
503
|
const badge = `<span class="pace-badge pace-behind">${Math.floor(gamesPercentDiff)}%</span>`;
|
|
448
|
-
gamesStatus.innerHTML = `${shortfall} short ${badge}
|
|
504
|
+
gamesStatus.innerHTML = `${shortfall} short ${badge}<br><small style="font-size: 0.85em; opacity: 0.9;">Est. completion: ${gamesCompletionDateStr}</small>`;
|
|
449
505
|
gamesStatus.className = 'dashboard-progress-value';
|
|
450
506
|
gamesStatus.style.color = 'var(--danger-color)';
|
|
451
507
|
}
|
|
@@ -502,6 +558,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
502
558
|
|
|
503
559
|
const gamesDateInput = document.getElementById('gamesTargetDate');
|
|
504
560
|
if (gamesDateInput) gamesDateInput.value = window.statsConfig.gamesTargetDate || '';
|
|
561
|
+
|
|
562
|
+
const cardsDailyTargetInput = document.getElementById('cardsMinedDailyTarget');
|
|
563
|
+
if (cardsDailyTargetInput) cardsDailyTargetInput.value = window.statsConfig.cardsMinedDailyTarget || 10;
|
|
505
564
|
}
|
|
506
565
|
|
|
507
566
|
// Open settings modal
|
|
@@ -543,7 +602,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
543
602
|
games_target: parseInt(document.getElementById('gamesTarget').value),
|
|
544
603
|
reading_hours_target_date: document.getElementById('readingHoursTargetDate').value || '',
|
|
545
604
|
character_count_target_date: document.getElementById('characterCountTargetDate').value || '',
|
|
546
|
-
games_target_date: document.getElementById('gamesTargetDate').value || ''
|
|
605
|
+
games_target_date: document.getElementById('gamesTargetDate').value || '',
|
|
606
|
+
cards_mined_daily_target: parseInt(document.getElementById('cardsMinedDailyTarget').value) || 10
|
|
547
607
|
};
|
|
548
608
|
|
|
549
609
|
const response = await fetch('/api/settings', {
|
|
@@ -565,7 +625,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
565
625
|
gamesTarget: formData.games_target,
|
|
566
626
|
readingHoursTargetDate: formData.reading_hours_target_date,
|
|
567
627
|
characterCountTargetDate: formData.character_count_target_date,
|
|
568
|
-
gamesTargetDate: formData.games_target_date
|
|
628
|
+
gamesTargetDate: formData.games_target_date,
|
|
629
|
+
cardsMinedDailyTarget: formData.cards_mined_daily_target
|
|
569
630
|
};
|
|
570
631
|
|
|
571
632
|
// Show success message
|
|
@@ -222,34 +222,40 @@ class HeatmapRenderer {
|
|
|
222
222
|
const activity = yearData[dateStr] || 0;
|
|
223
223
|
|
|
224
224
|
if (activity > 0 && maxActivity > 0) {
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
// Assign discrete color levels based on percentage thresholds
|
|
229
|
-
let colorLevel;
|
|
230
|
-
if (percentage <= 25) {
|
|
231
|
-
colorLevel = 1; // Light green
|
|
232
|
-
} else if (percentage <= 50) {
|
|
233
|
-
colorLevel = 2; // Medium green
|
|
234
|
-
} else if (percentage <= 75) {
|
|
235
|
-
colorLevel = 3; // Dark green
|
|
225
|
+
// Check if custom color function is provided
|
|
226
|
+
if (this.customColorFunction) {
|
|
227
|
+
cell.style.backgroundColor = this.customColorFunction(activity, maxActivity);
|
|
236
228
|
} else {
|
|
237
|
-
|
|
229
|
+
// Default color calculation
|
|
230
|
+
// Calculate percentage of maximum activity
|
|
231
|
+
const percentage = (activity / maxActivity) * 100;
|
|
232
|
+
|
|
233
|
+
// Assign discrete color levels based on percentage thresholds
|
|
234
|
+
let colorLevel;
|
|
235
|
+
if (percentage <= 25) {
|
|
236
|
+
colorLevel = 1; // Light green
|
|
237
|
+
} else if (percentage <= 50) {
|
|
238
|
+
colorLevel = 2; // Medium green
|
|
239
|
+
} else if (percentage <= 75) {
|
|
240
|
+
colorLevel = 3; // Dark green
|
|
241
|
+
} else {
|
|
242
|
+
colorLevel = 4; // Darkest green
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Define discrete colors for each level
|
|
246
|
+
const colors = {
|
|
247
|
+
1: '#c6e48b', // Light green (1-25%)
|
|
248
|
+
2: '#7bc96f', // Medium green (26-50%)
|
|
249
|
+
3: '#239a3b', // Dark green (51-75%)
|
|
250
|
+
4: '#196127' // Darkest green (76-100%)
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
cell.style.backgroundColor = colors[colorLevel];
|
|
238
254
|
}
|
|
239
|
-
|
|
240
|
-
// Define discrete colors for each level
|
|
241
|
-
const colors = {
|
|
242
|
-
1: '#c6e48b', // Light green (1-25%)
|
|
243
|
-
2: '#7bc96f', // Medium green (26-50%)
|
|
244
|
-
3: '#239a3b', // Dark green (51-75%)
|
|
245
|
-
4: '#196127' // Darkest green (76-100%)
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
cell.style.backgroundColor = colors[colorLevel];
|
|
249
255
|
}
|
|
250
256
|
|
|
251
257
|
// Format tooltip based on metric type
|
|
252
|
-
const activityLabel = this.metricName === 'sentences'
|
|
258
|
+
const activityLabel = this.metricName === 'sentences'
|
|
253
259
|
? `sentence${activity !== 1 ? 's' : ''} mined`
|
|
254
260
|
: `${this.metricLabel}`;
|
|
255
261
|
cell.title = `${dateStr}: ${activity} ${activityLabel}`;
|