GameSentenceMiner 2.17.1__py3-none-any.whl → 2.17.2__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 (28) hide show
  1. GameSentenceMiner/anki.py +25 -1
  2. GameSentenceMiner/config_gui.py +19 -1
  3. GameSentenceMiner/gsm.py +13 -18
  4. GameSentenceMiner/obs.py +4 -2
  5. GameSentenceMiner/ocr/owocr_helper.py +1 -1
  6. GameSentenceMiner/owocr/owocr/run.py +2 -2
  7. GameSentenceMiner/util/configuration.py +7 -5
  8. GameSentenceMiner/util/db.py +176 -8
  9. GameSentenceMiner/util/downloader/download_tools.py +57 -24
  10. GameSentenceMiner/util/get_overlay_coords.py +3 -0
  11. GameSentenceMiner/util/gsm_utils.py +0 -54
  12. GameSentenceMiner/web/database_api.py +12 -1
  13. GameSentenceMiner/web/gsm_websocket.py +1 -1
  14. GameSentenceMiner/web/static/css/shared.css +20 -0
  15. GameSentenceMiner/web/static/css/stats.css +496 -1
  16. GameSentenceMiner/web/static/js/anki_stats.js +87 -3
  17. GameSentenceMiner/web/static/js/shared.js +2 -49
  18. GameSentenceMiner/web/static/js/stats.js +274 -39
  19. GameSentenceMiner/web/templates/anki_stats.html +36 -0
  20. GameSentenceMiner/web/templates/index.html +1 -1
  21. GameSentenceMiner/web/templates/stats.html +35 -15
  22. GameSentenceMiner/web/texthooking_page.py +31 -8
  23. {gamesentenceminer-2.17.1.dist-info → gamesentenceminer-2.17.2.dist-info}/METADATA +1 -1
  24. {gamesentenceminer-2.17.1.dist-info → gamesentenceminer-2.17.2.dist-info}/RECORD +28 -28
  25. {gamesentenceminer-2.17.1.dist-info → gamesentenceminer-2.17.2.dist-info}/WHEEL +0 -0
  26. {gamesentenceminer-2.17.1.dist-info → gamesentenceminer-2.17.2.dist-info}/entry_points.txt +0 -0
  27. {gamesentenceminer-2.17.1.dist-info → gamesentenceminer-2.17.2.dist-info}/licenses/LICENSE +0 -0
  28. {gamesentenceminer-2.17.1.dist-info → gamesentenceminer-2.17.2.dist-info}/top_level.txt +0 -0
@@ -267,57 +267,3 @@ os.makedirs(os.path.dirname(TEXT_REPLACEMENTS_FILE), exist_ok=True)
267
267
  # f.write(data)
268
268
  # except Exception as e:
269
269
  # logger.error(f"Failed to fetch JSON from {url}: {e}")
270
-
271
-
272
- # Remove GitHub replacements from local OCR replacements file, these replacements are not needed
273
- def remove_github_replacements_from_local_ocr():
274
- github_url = "https://raw.githubusercontent.com/bpwhelan/GameSentenceMiner/main/electron-src/assets/ocr_replacements.json"
275
-
276
- github_replacements = {}
277
- try:
278
- response = requests.get(github_url)
279
- response.raise_for_status()
280
- github_data = response.json()
281
- github_replacements = github_data.get('args', {}).get('replacements', {})
282
- logger.debug(f"Successfully fetched {len(github_replacements)} replacements from GitHub.")
283
- except requests.exceptions.RequestException as e:
284
- logger.debug(f"Failed to fetch GitHub replacements from {github_url}: {e}")
285
- return
286
- except json.JSONDecodeError as e:
287
- logger.debug(f"Error decoding JSON from GitHub response: {e}")
288
- return
289
-
290
- if not os.path.exists(OCR_REPLACEMENTS_FILE):
291
- logger.debug(f"Local file {OCR_REPLACEMENTS_FILE} does not exist. No replacements to remove.")
292
- return
293
-
294
- try:
295
- with open(OCR_REPLACEMENTS_FILE, 'r', encoding='utf-8') as f:
296
- local_ocr_data = json.load(f)
297
-
298
- local_replacements = local_ocr_data.get('args', {}).get('replacements', {})
299
- original_count = len(local_replacements)
300
- logger.debug(f"Loaded {original_count} replacements from local file.")
301
-
302
- removed_count = 0
303
- for key_to_remove in github_replacements.keys():
304
- if key_to_remove in local_replacements:
305
- del local_replacements[key_to_remove]
306
- removed_count += 1
307
-
308
- if removed_count > 0:
309
- local_ocr_data['args']['replacements'] = local_replacements
310
- with open(OCR_REPLACEMENTS_FILE, 'w', encoding='utf-8') as f:
311
- json.dump(local_ocr_data, f, ensure_ascii=False, indent=4)
312
- logger.debug(f"Successfully removed {removed_count} replacements from {OCR_REPLACEMENTS_FILE}.")
313
- logger.debug(f"Remaining replacements in local file: {len(local_replacements)}")
314
- else:
315
- logger.debug("No matching replacements from GitHub found in your local file to remove.")
316
-
317
- except json.JSONDecodeError as e:
318
- logger.debug(f"Error decoding JSON from {OCR_REPLACEMENTS_FILE}: {e}. Please ensure it's valid JSON.")
319
- except Exception as e:
320
- logger.debug(f"An unexpected error occurred while processing {OCR_REPLACEMENTS_FILE}: {e}")
321
-
322
-
323
- remove_github_replacements_from_local_ocr()
@@ -795,9 +795,20 @@ def register_database_api_routes(app):
795
795
  punctionation_regex = regex.compile(r'[\p{P}\p{S}\p{Z}]')
796
796
  # Get optional year filter parameter
797
797
  filter_year = request.args.get('year', None)
798
+
799
+ # Get Start and End time as unix timestamp
800
+ start_timestamp = request.args.get('start', None)
801
+ end_timestamp = request.args.get('end', None)
798
802
 
803
+ # Convert timestamps to float if provided
804
+ start_timestamp = float(start_timestamp) if start_timestamp else None
805
+ end_timestamp = float(end_timestamp) if end_timestamp else None
806
+
807
+ # # 1. Fetch all lines and sort them chronologically
808
+ # all_lines = sorted(GameLinesTable.all(), key=lambda line: line.timestamp)
809
+
799
810
  # 1. Fetch all lines and sort them chronologically
800
- all_lines = sorted(GameLinesTable.all(), key=lambda line: line.timestamp)
811
+ all_lines = GameLinesTable.get_lines_filtered_by_timestamp(start=start_timestamp, end=end_timestamp)
801
812
 
802
813
  if not all_lines:
803
814
  return jsonify({"labels": [], "datasets": []})
@@ -78,7 +78,7 @@ class WebsocketServerThread(threading.Thread):
78
78
  while True:
79
79
  try:
80
80
  self.server = start_server = websockets.serve(self.server_handler,
81
- "0.0.0.0",
81
+ get_config().advanced.localhost_bind_address,
82
82
  self.get_ws_port_func(),
83
83
  max_size=1000000000)
84
84
  async with start_server:
@@ -929,4 +929,24 @@ h1 {
929
929
  .games-table td:nth-child(6) {
930
930
  display: none;
931
931
  }
932
+ }
933
+
934
+ input[type="date"].dashboard-date-input {
935
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
936
+ transition: all 0.2s ease; /* smooth animation */
937
+ border-radius: 8px;
938
+ border: 1px solid var(--border-color);
939
+ padding: 8px 12px;
940
+ font-size: 14px;
941
+ font-weight: 500;
942
+ color: var(--text-primary);
943
+ background: var(--bg-tertiary);
944
+ cursor: pointer;
945
+ }
946
+
947
+ input[type="date"].dashboard-date-input:focus,
948
+ input[type="date"].dashboard-date-input:hover {
949
+ border-color: var(--accent-color);
950
+ box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.15);
951
+ outline: none;
932
952
  }
@@ -657,6 +657,440 @@
657
657
  margin-bottom: 16px;
658
658
  }
659
659
 
660
+ /* ================================
661
+ Dashboard Cards Styles
662
+ ================================ */
663
+ .dashboard-container {
664
+ display: grid;
665
+ grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
666
+ gap: 30px;
667
+ margin-bottom: 40px;
668
+ }
669
+
670
+ .dashboard-card {
671
+ background: var(--bg-secondary);
672
+ border-radius: 12px;
673
+ box-shadow: 0 4px 16px var(--shadow-color);
674
+ border: 1px solid var(--border-color);
675
+ padding: 24px;
676
+ transition: all 0.3s ease;
677
+ position: relative;
678
+ overflow: hidden;
679
+ }
680
+
681
+ .dashboard-card:hover {
682
+ transform: translateY(-2px);
683
+ box-shadow: 0 8px 24px var(--shadow-color);
684
+ }
685
+
686
+ .dashboard-card::before {
687
+ content: '';
688
+ position: absolute;
689
+ top: 0;
690
+ left: 0;
691
+ right: 0;
692
+ height: 4px;
693
+ background: linear-gradient(90deg, var(--accent-color), var(--success-color));
694
+ border-radius: 12px 12px 0 0;
695
+ }
696
+
697
+ .dashboard-card.current-game::before {
698
+ background: linear-gradient(90deg, var(--accent-color), var(--info-color));
699
+ }
700
+
701
+ .dashboard-card.all-games::before {
702
+ background: linear-gradient(90deg, var(--success-color), var(--warning-color));
703
+ }
704
+
705
+ .dashboard-card.date-range::before {
706
+ background: linear-gradient(90deg, var(--info-color), var(--accent-color));
707
+ }
708
+
709
+ .dashboard-card.date-range {
710
+ margin-bottom: 30px;
711
+ }
712
+
713
+ .dashboard-card-header {
714
+ display: flex;
715
+ align-items: center;
716
+ justify-content: space-between;
717
+ margin-bottom: 20px;
718
+ }
719
+
720
+ .dashboard-card-title {
721
+ font-size: 20px;
722
+ font-weight: 600;
723
+ color: var(--text-primary);
724
+ margin: 0;
725
+ display: flex;
726
+ align-items: center;
727
+ gap: 10px;
728
+ }
729
+
730
+ .dashboard-card-icon {
731
+ font-size: 24px;
732
+ opacity: 0.8;
733
+ }
734
+
735
+ .dashboard-card-subtitle {
736
+ font-size: 14px;
737
+ color: var(--text-tertiary);
738
+ margin: 4px 0 0 0;
739
+ }
740
+
741
+ /* ================================
742
+ Stats Grid
743
+ ================================ */
744
+ .dashboard-stats-grid {
745
+ display: grid;
746
+ grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
747
+ gap: 16px;
748
+ margin-bottom: 20px;
749
+ }
750
+
751
+ .dashboard-stat-item {
752
+ text-align: center;
753
+ padding: 12px;
754
+ background: var(--bg-tertiary);
755
+ border-radius: 8px;
756
+ transition: all 0.2s ease;
757
+ position: relative;
758
+ cursor: pointer;
759
+ }
760
+
761
+ .dashboard-stat-item:hover {
762
+ background: var(--border-color);
763
+ transform: scale(1.02);
764
+ }
765
+
766
+ .dashboard-stat-value {
767
+ font-size: 24px;
768
+ font-weight: bold;
769
+ color: var(--text-primary);
770
+ margin-bottom: 4px;
771
+ display: block;
772
+ }
773
+
774
+ .dashboard-stat-label {
775
+ font-size: 12px;
776
+ color: var(--text-tertiary);
777
+ text-transform: uppercase;
778
+ letter-spacing: 0.5px;
779
+ font-weight: 500;
780
+ }
781
+
782
+ /* ================================
783
+ Progress Section
784
+ ================================ */
785
+ .dashboard-progress-section {
786
+ margin-top: 20px;
787
+ padding-top: 20px;
788
+ border-top: 1px solid var(--border-color);
789
+ }
790
+
791
+ .dashboard-progress-title {
792
+ font-size: 14px;
793
+ font-weight: 600;
794
+ color: var(--text-secondary);
795
+ margin-bottom: 12px;
796
+ }
797
+
798
+ .dashboard-progress-items {
799
+ display: flex;
800
+ justify-content: space-between;
801
+ gap: 16px;
802
+ }
803
+
804
+ .dashboard-progress-item {
805
+ text-align: center;
806
+ flex: 1;
807
+ }
808
+
809
+ .dashboard-progress-value {
810
+ font-size: 18px;
811
+ font-weight: bold;
812
+ margin-bottom: 4px;
813
+ }
814
+
815
+ .dashboard-progress-value.positive {
816
+ color: var(--success-color);
817
+ }
818
+
819
+ .dashboard-progress-value.neutral {
820
+ color: var(--text-secondary);
821
+ }
822
+
823
+ .dashboard-progress-label {
824
+ font-size: 11px;
825
+ color: var(--text-tertiary);
826
+ text-transform: uppercase;
827
+ letter-spacing: 0.5px;
828
+ }
829
+
830
+ /* ================================
831
+ Streak Indicator
832
+ ================================ */
833
+ .dashboard-streak-indicator {
834
+ display: inline-flex;
835
+ align-items: center;
836
+ gap: 4px;
837
+ font-size: 12px;
838
+ color: var(--success-color);
839
+ background: rgba(40, 167, 69, 0.1);
840
+ padding: 4px 8px;
841
+ border-radius: 12px;
842
+ font-weight: 500;
843
+ }
844
+
845
+ .dashboard-streak-indicator::before {
846
+ content: '🔥';
847
+ font-size: 14px;
848
+ }
849
+
850
+ /* ================================
851
+ Date Range Card
852
+ ================================ */
853
+ .dashboard-date-range {
854
+ display: flex;
855
+ gap: 20px;
856
+ }
857
+
858
+ .dashboard-date-item {
859
+ flex: 1;
860
+ display: flex;
861
+ flex-direction: column;
862
+ gap: 6px;
863
+ }
864
+
865
+ .dashboard-date-item label {
866
+ font-size: 13px;
867
+ color: var(--text-secondary);
868
+ font-weight: 500;
869
+ }
870
+
871
+ .dashboard-date-input {
872
+ padding: 8px 12px;
873
+ border: 1px solid var(--border-color);
874
+ border-radius: 8px;
875
+ background: var(--bg-tertiary);
876
+ color: var(--text-primary);
877
+ font-size: 14px;
878
+ transition: all 0.2s ease;
879
+ }
880
+
881
+ .dashboard-date-input:focus {
882
+ outline: none;
883
+ border-color: var(--accent-color);
884
+ box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.15);
885
+ }
886
+
887
+ /* ================================
888
+ Tooltip Styles
889
+ ================================ */
890
+ .tooltip {
891
+ position: relative;
892
+ cursor: help;
893
+ }
894
+
895
+ .tooltip::before {
896
+ content: attr(data-tooltip);
897
+ position: absolute;
898
+ bottom: 125%;
899
+ left: 50%;
900
+ transform: translateX(-50%);
901
+ background: rgba(0, 0, 0, 0.9);
902
+ color: white;
903
+ padding: 8px 12px;
904
+ border-radius: 6px;
905
+ font-size: 12px;
906
+ white-space: nowrap;
907
+ opacity: 0;
908
+ visibility: hidden;
909
+ transition: all 0.3s ease;
910
+ z-index: 1000;
911
+ pointer-events: none;
912
+ }
913
+
914
+ .tooltip::after {
915
+ content: '';
916
+ position: absolute;
917
+ bottom: 120%;
918
+ left: 50%;
919
+ transform: translateX(-50%);
920
+ border: 4px solid transparent;
921
+ border-top-color: rgba(0, 0, 0, 0.9);
922
+ opacity: 0;
923
+ visibility: hidden;
924
+ transition: all 0.3s ease;
925
+ z-index: 1000;
926
+ }
927
+
928
+ .tooltip:hover::before,
929
+ .tooltip:hover::after {
930
+ opacity: 1;
931
+ visibility: visible;
932
+ }
933
+
934
+ /* ================================
935
+ Loading State
936
+ ================================ */
937
+ .dashboard-loading {
938
+ display: flex;
939
+ align-items: center;
940
+ justify-content: center;
941
+ min-height: 200px;
942
+ color: var(--text-tertiary);
943
+ }
944
+
945
+ .dashboard-loading .spinner {
946
+ margin-right: 10px;
947
+ }
948
+
949
+ /* ================================
950
+ Error State
951
+ ================================ */
952
+ .dashboard-error {
953
+ text-align: center;
954
+ padding: 40px 20px;
955
+ color: var(--danger-color);
956
+ }
957
+
958
+ .dashboard-error-icon {
959
+ font-size: 48px;
960
+ margin-bottom: 16px;
961
+ opacity: 0.7;
962
+ }
963
+
964
+ .dashboard-error-message {
965
+ font-size: 16px;
966
+ margin-bottom: 16px;
967
+ }
968
+
969
+ .dashboard-retry-btn {
970
+ background-color: var(--accent-color);
971
+ color: white;
972
+ border: none;
973
+ padding: 10px 20px;
974
+ border-radius: 6px;
975
+ cursor: pointer;
976
+ font-size: 14px;
977
+ transition: all 0.3s ease;
978
+ }
979
+
980
+ .dashboard-retry-btn:hover {
981
+ background-color: #0056b3;
982
+ transform: translateY(-1px);
983
+ }
984
+
985
+ /* ================================
986
+ Popups
987
+ ================================ */
988
+ .dashboard-popup,
989
+ .no-data-popup {
990
+ position: fixed;
991
+ top: 0;
992
+ left: 0;
993
+ right: 0;
994
+ bottom: 0;
995
+ display: flex;
996
+ align-items: center;
997
+ justify-content: center;
998
+ background: rgba(0, 0, 0, 0.4);
999
+ z-index: 2000;
1000
+ }
1001
+
1002
+ .hidden {
1003
+ display: none;
1004
+ }
1005
+
1006
+ .dashboard-popup-content,
1007
+ .no-data-content {
1008
+ background: var(--bg-secondary);
1009
+ border: 1px solid var(--border-color);
1010
+ border-radius: 12px;
1011
+ padding: 24px;
1012
+ box-shadow: 0 8px 24px var(--shadow-color);
1013
+ text-align: center;
1014
+ max-width: 400px;
1015
+ width: 90%;
1016
+ }
1017
+
1018
+ .dashboard-popup-icon {
1019
+ font-size: 32px;
1020
+ margin-bottom: 12px;
1021
+ }
1022
+
1023
+ .dashboard-popup-message,
1024
+ .no-data-content p {
1025
+ font-size: 14px;
1026
+ color: var(--text-primary);
1027
+ margin-bottom: 20px;
1028
+ }
1029
+
1030
+ .dashboard-popup-btn,
1031
+ #closeNoDataPopup {
1032
+ background: var(--accent-color);
1033
+ color: white;
1034
+ border: none;
1035
+ padding: 10px 18px;
1036
+ border-radius: 8px;
1037
+ font-size: 14px;
1038
+ cursor: pointer;
1039
+ transition: all 0.2s ease;
1040
+ }
1041
+
1042
+ .dashboard-popup-btn:hover,
1043
+ #closeNoDataPopup:hover {
1044
+ background: #0056b3;
1045
+ transform: translateY(-1px);
1046
+ }
1047
+
1048
+ /* Popup box */
1049
+ .dashboard-popup-content {
1050
+ background: var(--bg-secondary);
1051
+ border-radius: 12px;
1052
+ box-shadow: 0 8px 24px var(--shadow-color);
1053
+ border: 1px solid var(--border-color);
1054
+ padding: 24px;
1055
+ max-width: 320px;
1056
+ text-align: center;
1057
+ animation: popupFadeIn 0.3s ease;
1058
+ }
1059
+
1060
+ .dashboard-popup-icon {
1061
+ font-size: 36px;
1062
+ margin-bottom: 12px;
1063
+ }
1064
+
1065
+ .dashboard-popup-message {
1066
+ font-size: 14px;
1067
+ color: var(--danger-color);
1068
+ margin-bottom: 20px;
1069
+ font-weight: 500;
1070
+ }
1071
+
1072
+ .dashboard-popup-btn {
1073
+ padding: 10px 16px;
1074
+ border-radius: 6px;
1075
+ border: none;
1076
+ background: var(--accent-color);
1077
+ color: #fff;
1078
+ font-size: 14px;
1079
+ font-weight: 600;
1080
+ cursor: pointer;
1081
+ transition: all 0.2s ease;
1082
+ }
1083
+
1084
+ .dashboard-popup-btn:hover {
1085
+ background: var(--accent-color-hover, #0056b3);
1086
+ transform: translateY(-1px);
1087
+ }
1088
+
1089
+ @keyframes popupFadeIn {
1090
+ from { transform: scale(0.95); opacity: 0; }
1091
+ to { transform: scale(1); opacity: 1; }
1092
+ }
1093
+
660
1094
  /* Responsive Design for Goal Progress Chart */
661
1095
  @media (max-width: 768px) {
662
1096
  .goal-progress-grid {
@@ -739,4 +1173,65 @@
739
1173
  margin-left: 0;
740
1174
  align-self: center;
741
1175
  }
742
- }
1176
+ }
1177
+
1178
+ /* Responsive adjustments */
1179
+ @media (max-width: 768px) {
1180
+ .dashboard-date-range {
1181
+ flex-direction: column;
1182
+ gap: 12px;
1183
+ padding: 12px 16px;
1184
+ }
1185
+
1186
+ .dashboard-date-item {
1187
+ width: 100%;
1188
+ }
1189
+
1190
+ .dashboard-fetch-btn {
1191
+ width: 100%;
1192
+ }
1193
+ }
1194
+
1195
+
1196
+ .no-data-popup {
1197
+ position: fixed;
1198
+ inset: 0;
1199
+ background: rgba(0,0,0,0.6);
1200
+ display: flex;
1201
+ justify-content: center;
1202
+ align-items: center;
1203
+ z-index: 2000;
1204
+ }
1205
+
1206
+ .no-data-popup.hidden {
1207
+ display: none;
1208
+ }
1209
+
1210
+ .no-data-content {
1211
+ background: var(--bg-secondary);
1212
+ padding: 20px 30px;
1213
+ border-radius: 10px;
1214
+ text-align: center;
1215
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
1216
+ }
1217
+
1218
+ .no-data-content p {
1219
+ margin-bottom: 16px;
1220
+ color: var(--text-primary);
1221
+ font-size: 15px;
1222
+ }
1223
+
1224
+ .no-data-content button {
1225
+ padding: 8px 16px;
1226
+ background: var(--accent-color);
1227
+ border: none;
1228
+ border-radius: 6px;
1229
+ color: #fff;
1230
+ font-weight: 600;
1231
+ cursor: pointer;
1232
+ transition: all 0.2s ease;
1233
+ }
1234
+
1235
+ .no-data-content button:hover {
1236
+ background: #0056b3;
1237
+ }