GameSentenceMiner 2.17.7__py3-none-any.whl → 2.18.1__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.

Files changed (76) hide show
  1. GameSentenceMiner/ai/ai_prompting.py +6 -6
  2. GameSentenceMiner/anki.py +236 -152
  3. GameSentenceMiner/gametext.py +7 -4
  4. GameSentenceMiner/gsm.py +49 -10
  5. GameSentenceMiner/locales/en_us.json +7 -3
  6. GameSentenceMiner/locales/ja_jp.json +8 -4
  7. GameSentenceMiner/locales/zh_cn.json +8 -4
  8. GameSentenceMiner/obs.py +238 -59
  9. GameSentenceMiner/ocr/owocr_helper.py +1 -1
  10. GameSentenceMiner/tools/ss_selector.py +7 -8
  11. GameSentenceMiner/ui/__init__.py +0 -0
  12. GameSentenceMiner/ui/anki_confirmation.py +187 -0
  13. GameSentenceMiner/{config_gui.py → ui/config_gui.py} +100 -35
  14. GameSentenceMiner/ui/screenshot_selector.py +215 -0
  15. GameSentenceMiner/util/configuration.py +124 -22
  16. GameSentenceMiner/util/db.py +22 -13
  17. GameSentenceMiner/util/downloader/download_tools.py +2 -2
  18. GameSentenceMiner/util/ffmpeg.py +24 -30
  19. GameSentenceMiner/util/get_overlay_coords.py +34 -34
  20. GameSentenceMiner/util/gsm_utils.py +31 -1
  21. GameSentenceMiner/util/text_log.py +11 -9
  22. GameSentenceMiner/vad.py +31 -12
  23. GameSentenceMiner/web/database_api.py +742 -123
  24. GameSentenceMiner/web/static/css/dashboard-shared.css +241 -0
  25. GameSentenceMiner/web/static/css/kanji-grid.css +94 -2
  26. GameSentenceMiner/web/static/css/overview.css +850 -0
  27. GameSentenceMiner/web/static/css/popups-shared.css +126 -0
  28. GameSentenceMiner/web/static/css/shared.css +97 -0
  29. GameSentenceMiner/web/static/css/stats.css +192 -597
  30. GameSentenceMiner/web/static/js/anki_stats.js +6 -4
  31. GameSentenceMiner/web/static/js/database.js +209 -5
  32. GameSentenceMiner/web/static/js/goals.js +610 -0
  33. GameSentenceMiner/web/static/js/kanji-grid.js +267 -4
  34. GameSentenceMiner/web/static/js/overview.js +1176 -0
  35. GameSentenceMiner/web/static/js/shared.js +25 -0
  36. GameSentenceMiner/web/static/js/stats.js +154 -1459
  37. GameSentenceMiner/web/stats.py +2 -2
  38. GameSentenceMiner/web/templates/anki_stats.html +5 -0
  39. GameSentenceMiner/web/templates/components/kanji_grid/basic_kanji_book_bkb_v1_v2.json +17 -0
  40. GameSentenceMiner/web/templates/components/kanji_grid/duolingo_kanji.json +29 -0
  41. GameSentenceMiner/web/templates/components/kanji_grid/grade.json +17 -0
  42. GameSentenceMiner/web/templates/components/kanji_grid/hk_primary_learning.json +17 -0
  43. GameSentenceMiner/web/templates/components/kanji_grid/hkscs2016.json +13 -0
  44. GameSentenceMiner/web/templates/components/kanji_grid/hsk_levels.json +33 -0
  45. GameSentenceMiner/web/templates/components/kanji_grid/humanum_frequency_list.json +41 -0
  46. GameSentenceMiner/web/templates/components/kanji_grid/jis_levels.json +25 -0
  47. GameSentenceMiner/web/templates/components/kanji_grid/jlpt_level.json +29 -0
  48. GameSentenceMiner/web/templates/components/kanji_grid/jpdb_kanji_frequency_list.json +37 -0
  49. GameSentenceMiner/web/templates/components/kanji_grid/jpdbv2_kanji_frequency_list.json +161 -0
  50. GameSentenceMiner/web/templates/components/kanji_grid/jun_das_modern_chinese_character_frequency_list.json +13 -0
  51. GameSentenceMiner/web/templates/components/kanji_grid/kanji_in_context_revised_edition.json +37 -0
  52. GameSentenceMiner/web/templates/components/kanji_grid/kanji_kentei_level.json +61 -0
  53. GameSentenceMiner/web/templates/components/kanji_grid/mainland_china_elementary_textbook_characters.json +33 -0
  54. GameSentenceMiner/web/templates/components/kanji_grid/moe_way_quiz.json +47 -0
  55. GameSentenceMiner/web/templates/components/kanji_grid/official_kanji.json +25 -0
  56. GameSentenceMiner/web/templates/components/kanji_grid/remembering_the_kanji.json +25 -0
  57. GameSentenceMiner/web/templates/components/kanji_grid/standard_form_of_national_characters.json +25 -0
  58. GameSentenceMiner/web/templates/components/kanji_grid/table_of_general_standard_chinese_characters.json +21 -0
  59. GameSentenceMiner/web/templates/components/kanji_grid/the_kodansha_kanji_learners_course_klc.json +45 -0
  60. GameSentenceMiner/web/templates/components/kanji_grid/thousand_character_classic.json +13 -0
  61. GameSentenceMiner/web/templates/components/kanji_grid/wanikani_levels.json +249 -0
  62. GameSentenceMiner/web/templates/components/kanji_grid/words_hk_frequency_list.json +33 -0
  63. GameSentenceMiner/web/templates/components/navigation.html +3 -1
  64. GameSentenceMiner/web/templates/database.html +73 -1
  65. GameSentenceMiner/web/templates/goals.html +376 -0
  66. GameSentenceMiner/web/templates/index.html +13 -11
  67. GameSentenceMiner/web/templates/overview.html +416 -0
  68. GameSentenceMiner/web/templates/stats.html +46 -251
  69. GameSentenceMiner/web/texthooking_page.py +18 -0
  70. {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.1.dist-info}/METADATA +5 -1
  71. gamesentenceminer-2.18.1.dist-info/RECORD +132 -0
  72. gamesentenceminer-2.17.7.dist-info/RECORD +0 -98
  73. {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.1.dist-info}/WHEEL +0 -0
  74. {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.1.dist-info}/entry_points.txt +0 -0
  75. {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.1.dist-info}/licenses/LICENSE +0 -0
  76. {gamesentenceminer-2.17.7.dist-info → gamesentenceminer-2.18.1.dist-info}/top_level.txt +0 -0
@@ -14,6 +14,7 @@ class KanjiGridRenderer {
14
14
  emptyMessage: 'No kanji data available',
15
15
  showCounter: true,
16
16
  showLegend: true,
17
+ enableSorting: true, // Enable sorting dropdown
17
18
  ...options
18
19
  };
19
20
 
@@ -25,18 +26,145 @@ class KanjiGridRenderer {
25
26
  console.error(`KanjiGridRenderer: Container not found with selector: ${this.config.containerSelector}`);
26
27
  return;
27
28
  }
29
+
30
+ // Sorting state
31
+ this.currentKanjiData = null;
32
+ this.sortingConfigs = [];
33
+ this.currentSortMode = 'frequency'; // 'frequency' or config filename
34
+
35
+ // Initialize sorting if enabled
36
+ if (this.config.enableSorting) {
37
+ this.initializeSorting();
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Initialize sorting functionality
43
+ */
44
+ async initializeSorting() {
45
+ try {
46
+ // Load saved preference
47
+ this.currentSortMode = this.loadSortPreference();
48
+
49
+ // Fetch available sorting configs
50
+ await this.loadSortingConfigs();
51
+
52
+ // Create dropdown UI
53
+ this.createSortDropdown();
54
+ } catch (error) {
55
+ console.error('Failed to initialize sorting:', error);
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Load available sorting configurations from API
61
+ */
62
+ async loadSortingConfigs() {
63
+ try {
64
+ const response = await fetch('/api/kanji-sorting-configs');
65
+ const data = await response.json();
66
+ this.sortingConfigs = data.configs || [];
67
+ } catch (error) {
68
+ console.error('Failed to load sorting configs:', error);
69
+ this.sortingConfigs = [];
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Create and insert the sort dropdown UI
75
+ */
76
+ createSortDropdown() {
77
+ // Create dropdown container
78
+ const dropdownContainer = document.createElement('div');
79
+ dropdownContainer.className = 'kanji-sort-dropdown-container';
80
+
81
+ const label = document.createElement('label');
82
+ label.textContent = 'Sort by: ';
83
+ label.className = 'kanji-sort-label';
84
+
85
+ const select = document.createElement('select');
86
+ select.className = 'kanji-sort-dropdown';
87
+ select.id = 'kanjiSortDropdown';
88
+
89
+ // Add "Frequency (default)" option
90
+ const freqOption = document.createElement('option');
91
+ freqOption.value = 'frequency';
92
+ freqOption.textContent = 'Frequency (default)';
93
+ select.appendChild(freqOption);
94
+
95
+ // Add options from loaded configs
96
+ this.sortingConfigs.forEach(config => {
97
+ const option = document.createElement('option');
98
+ option.value = config.filename;
99
+ option.textContent = config.name;
100
+ select.appendChild(option);
101
+ });
102
+
103
+ // Set current value
104
+ select.value = this.currentSortMode;
105
+
106
+ // Add change handler
107
+ select.addEventListener('change', (e) => this.handleSortChange(e.target.value));
108
+
109
+ dropdownContainer.appendChild(label);
110
+ dropdownContainer.appendChild(select);
111
+
112
+ // Insert at the top of the container
113
+ this.container.parentElement.insertBefore(dropdownContainer, this.container);
114
+ }
115
+
116
+ /**
117
+ * Handle sort mode change
118
+ * @param {string} sortMode - Selected sort mode
119
+ */
120
+ async handleSortChange(sortMode) {
121
+ this.currentSortMode = sortMode;
122
+ this.saveSortPreference(sortMode);
123
+
124
+ // Re-render with current data
125
+ if (this.currentKanjiData) {
126
+ await this.render(this.currentKanjiData);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Save sort preference to localStorage
132
+ * @param {string} sortMode - Sort mode to save
133
+ */
134
+ saveSortPreference(sortMode) {
135
+ try {
136
+ localStorage.setItem('kanjiGridSortMode', sortMode);
137
+ } catch (error) {
138
+ console.error('Failed to save sort preference:', error);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Load sort preference from localStorage
144
+ * @returns {string} Saved sort mode or 'frequency' as default
145
+ */
146
+ loadSortPreference() {
147
+ try {
148
+ return localStorage.getItem('kanjiGridSortMode') || 'frequency';
149
+ } catch (error) {
150
+ console.error('Failed to load sort preference:', error);
151
+ return 'frequency';
152
+ }
28
153
  }
29
154
 
30
155
  /**
31
156
  * Render the kanji grid with provided data
32
157
  * @param {Object|Array} kanjiData - Kanji data to render
33
158
  */
34
- render(kanjiData) {
159
+ async render(kanjiData) {
35
160
  if (!kanjiData) {
36
161
  this.renderEmpty();
37
162
  return;
38
163
  }
39
164
 
165
+ // Store current data for re-rendering
166
+ this.currentKanjiData = kanjiData;
167
+
40
168
  // Handle different data formats
41
169
  let kanjiList = [];
42
170
  if (Array.isArray(kanjiData)) {
@@ -62,16 +190,151 @@ class KanjiGridRenderer {
62
190
  this.counter.textContent = kanjiList.length;
63
191
  }
64
192
 
65
- // Clear existing grid
193
+ // Clear existing grid and reset classes
66
194
  this.container.innerHTML = '';
195
+ this.container.classList.remove('flat-mode');
196
+
197
+ // Render based on current sort mode
198
+ if (this.currentSortMode === 'frequency') {
199
+ this.renderFlat(kanjiList);
200
+ } else {
201
+ await this.renderGrouped(kanjiList, this.currentSortMode);
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Render kanji in flat/frequency mode
207
+ * @param {Array} kanjiList - List of kanji items
208
+ */
209
+ renderFlat(kanjiList) {
210
+ // Add flat-mode class for CSS Grid layout
211
+ this.container.classList.add('flat-mode');
67
212
 
68
- // Render kanji cells
69
213
  kanjiList.forEach(item => {
70
214
  const cell = this.createKanjiCell(item);
71
215
  this.container.appendChild(cell);
72
216
  });
73
217
  }
74
218
 
219
+ /**
220
+ * Render kanji in grouped mode based on JSON configuration
221
+ * @param {Array} kanjiList - List of kanji items
222
+ * @param {string} configFilename - Configuration filename
223
+ */
224
+ async renderGrouped(kanjiList, configFilename) {
225
+ // Remove flat-mode class for block layout
226
+ this.container.classList.remove('flat-mode');
227
+
228
+ try {
229
+ // Fetch the sorting configuration
230
+ const response = await fetch(`/api/kanji-sorting-config/${configFilename}`);
231
+ if (!response.ok) {
232
+ console.error('Failed to load sorting config, falling back to frequency');
233
+ this.renderFlat(kanjiList);
234
+ return;
235
+ }
236
+
237
+ const config = await response.json();
238
+ let groups = config.groups || [];
239
+
240
+ // Create a map for quick kanji lookup
241
+ const kanjiMap = new Map();
242
+ kanjiList.forEach(item => {
243
+ kanjiMap.set(item.kanji, item);
244
+ });
245
+
246
+ // Handle leftover kanji (add as additional group if exists)
247
+ if (config.leftover_group) {
248
+ const leftoverKanji = Array.from(kanjiMap.values()).filter(item => {
249
+ return !groups.some(group =>
250
+ group.characters && group.characters.includes(item.kanji)
251
+ );
252
+ });
253
+
254
+ if (leftoverKanji.length > 0) {
255
+ groups = [...groups, {
256
+ name: config.leftover_group,
257
+ characters: leftoverKanji.map(item => item.kanji).join('')
258
+ }];
259
+ }
260
+ }
261
+
262
+ // Render each group in stacked layout
263
+ groups.forEach(group => {
264
+ const groupSection = this.createGroupSection(group, kanjiMap);
265
+ this.container.appendChild(groupSection);
266
+ });
267
+
268
+ } catch (error) {
269
+ console.error('Error rendering grouped kanji:', error);
270
+ this.renderFlat(kanjiList);
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Create a group section with header and kanji grid
276
+ * @param {Object} group - Group configuration {name, characters}
277
+ * @param {Map} kanjiMap - Map of kanji character to kanji data
278
+ * @returns {HTMLElement} Group section element
279
+ */
280
+ createGroupSection(group, kanjiMap) {
281
+ const section = document.createElement('div');
282
+ section.className = 'kanji-group-section';
283
+
284
+ const characters = group.characters || '';
285
+
286
+ // Calculate statistics
287
+ const totalInGroup = characters.length;
288
+ let knownInGroup = 0;
289
+ let foundInGroup = 0;
290
+
291
+ for (const char of characters) {
292
+ if (kanjiMap.has(char)) {
293
+ foundInGroup++;
294
+ knownInGroup++; // For now, found = known
295
+ }
296
+ }
297
+
298
+ const foundPercentage = totalInGroup > 0 ? ((foundInGroup / totalInGroup) * 100).toFixed(2) : 0;
299
+ const knownPercentage = totalInGroup > 0 ? ((knownInGroup / totalInGroup) * 100).toFixed(2) : 0;
300
+
301
+ // Create header
302
+ const header = document.createElement('h3');
303
+ header.className = 'kanji-group-header';
304
+ header.textContent = group.name;
305
+ section.appendChild(header);
306
+
307
+ // Create stats line
308
+ const stats = document.createElement('div');
309
+ stats.className = 'kanji-group-stats';
310
+ stats.textContent = `${foundInGroup} of ${totalInGroup} Found - ${foundPercentage}%, ${knownInGroup} of ${totalInGroup} Known - ${knownPercentage}%`;
311
+ section.appendChild(stats);
312
+
313
+ // Create grid for kanji
314
+ const grid = document.createElement('div');
315
+ grid.className = 'kanji-group-grid';
316
+
317
+ // Render all characters in the group
318
+ for (const char of characters) {
319
+ const kanjiData = kanjiMap.get(char);
320
+ if (kanjiData) {
321
+ const cell = this.createKanjiCell(kanjiData);
322
+ grid.appendChild(cell);
323
+ } else {
324
+ const cell = this.createKanjiCell({
325
+ kanji: char,
326
+ frequency: 0,
327
+ color: '#ebedf0'
328
+ });
329
+ grid.appendChild(cell);
330
+ }
331
+ }
332
+
333
+ section.appendChild(grid);
334
+
335
+ return section;
336
+ }
337
+
75
338
  /**
76
339
  * Create individual kanji cell element
77
340
  * @param {Object} item - Kanji item with kanji character and frequency
@@ -126,7 +389,7 @@ class KanjiGridRenderer {
126
389
  * @returns {string} - CSS color value
127
390
  */
128
391
  getFrequencyColor(frequency) {
129
- if (frequency > 500) return '#2ee6e0'; // Cyan for very frequent
392
+ if (frequency > 300) return '#2ee6e0'; // Cyan for very frequent
130
393
  else if (frequency > 100) return '#3be62f'; // Green for frequent
131
394
  else if (frequency > 30) return '#e6dc2e'; // Yellow for moderate
132
395
  else if (frequency > 10) return '#e6342e'; // Red for occasional