ankigammon 1.0.6__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.
Files changed (61) hide show
  1. ankigammon/__init__.py +7 -0
  2. ankigammon/__main__.py +6 -0
  3. ankigammon/analysis/__init__.py +13 -0
  4. ankigammon/analysis/score_matrix.py +391 -0
  5. ankigammon/anki/__init__.py +6 -0
  6. ankigammon/anki/ankiconnect.py +216 -0
  7. ankigammon/anki/apkg_exporter.py +111 -0
  8. ankigammon/anki/card_generator.py +1325 -0
  9. ankigammon/anki/card_styles.py +1054 -0
  10. ankigammon/gui/__init__.py +8 -0
  11. ankigammon/gui/app.py +192 -0
  12. ankigammon/gui/dialogs/__init__.py +10 -0
  13. ankigammon/gui/dialogs/export_dialog.py +594 -0
  14. ankigammon/gui/dialogs/import_options_dialog.py +201 -0
  15. ankigammon/gui/dialogs/input_dialog.py +762 -0
  16. ankigammon/gui/dialogs/note_dialog.py +93 -0
  17. ankigammon/gui/dialogs/settings_dialog.py +420 -0
  18. ankigammon/gui/dialogs/update_dialog.py +373 -0
  19. ankigammon/gui/format_detector.py +377 -0
  20. ankigammon/gui/main_window.py +1611 -0
  21. ankigammon/gui/resources/down-arrow.svg +3 -0
  22. ankigammon/gui/resources/icon.icns +0 -0
  23. ankigammon/gui/resources/icon.ico +0 -0
  24. ankigammon/gui/resources/icon.png +0 -0
  25. ankigammon/gui/resources/style.qss +402 -0
  26. ankigammon/gui/resources.py +26 -0
  27. ankigammon/gui/update_checker.py +259 -0
  28. ankigammon/gui/widgets/__init__.py +8 -0
  29. ankigammon/gui/widgets/position_list.py +166 -0
  30. ankigammon/gui/widgets/smart_input.py +268 -0
  31. ankigammon/models.py +356 -0
  32. ankigammon/parsers/__init__.py +7 -0
  33. ankigammon/parsers/gnubg_match_parser.py +1094 -0
  34. ankigammon/parsers/gnubg_parser.py +468 -0
  35. ankigammon/parsers/sgf_parser.py +290 -0
  36. ankigammon/parsers/xg_binary_parser.py +1097 -0
  37. ankigammon/parsers/xg_text_parser.py +688 -0
  38. ankigammon/renderer/__init__.py +5 -0
  39. ankigammon/renderer/animation_controller.py +391 -0
  40. ankigammon/renderer/animation_helper.py +191 -0
  41. ankigammon/renderer/color_schemes.py +145 -0
  42. ankigammon/renderer/svg_board_renderer.py +791 -0
  43. ankigammon/settings.py +315 -0
  44. ankigammon/thirdparty/__init__.py +7 -0
  45. ankigammon/thirdparty/xgdatatools/__init__.py +17 -0
  46. ankigammon/thirdparty/xgdatatools/xgimport.py +160 -0
  47. ankigammon/thirdparty/xgdatatools/xgstruct.py +1032 -0
  48. ankigammon/thirdparty/xgdatatools/xgutils.py +118 -0
  49. ankigammon/thirdparty/xgdatatools/xgzarc.py +260 -0
  50. ankigammon/utils/__init__.py +13 -0
  51. ankigammon/utils/gnubg_analyzer.py +590 -0
  52. ankigammon/utils/gnuid.py +577 -0
  53. ankigammon/utils/move_parser.py +204 -0
  54. ankigammon/utils/ogid.py +326 -0
  55. ankigammon/utils/xgid.py +387 -0
  56. ankigammon-1.0.6.dist-info/METADATA +352 -0
  57. ankigammon-1.0.6.dist-info/RECORD +61 -0
  58. ankigammon-1.0.6.dist-info/WHEEL +5 -0
  59. ankigammon-1.0.6.dist-info/entry_points.txt +2 -0
  60. ankigammon-1.0.6.dist-info/licenses/LICENSE +21 -0
  61. ankigammon-1.0.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,373 @@
1
+ """Dialog for displaying version update information."""
2
+
3
+ import webbrowser
4
+ from datetime import datetime, timedelta
5
+ from typing import Dict
6
+
7
+ from PySide6.QtCore import Qt
8
+ from PySide6.QtGui import QFont
9
+ from PySide6.QtWidgets import (
10
+ QDialog,
11
+ QVBoxLayout,
12
+ QHBoxLayout,
13
+ QLabel,
14
+ QPushButton,
15
+ QTextEdit,
16
+ )
17
+
18
+
19
+ class UpdateDialog(QDialog):
20
+ """Dialog to notify user about available updates."""
21
+
22
+ def __init__(self, parent, release_info: Dict, current_version: str):
23
+ """Initialize update dialog.
24
+
25
+ Args:
26
+ parent: Parent widget
27
+ release_info: Release information from GitHub API
28
+ current_version: Current application version
29
+ """
30
+ super().__init__(parent)
31
+ self.release_info = release_info
32
+ self.current_version = current_version
33
+ self.snooze_hours = 24
34
+ self.user_action = None # 'update', 'snooze', or 'skip'
35
+
36
+ self.setWindowTitle("Update Available")
37
+ self.setMinimumWidth(500)
38
+ self.setMinimumHeight(400)
39
+
40
+ self._setup_ui()
41
+
42
+ def _setup_ui(self):
43
+ """Set up the dialog UI."""
44
+ # Set dialog background to match app theme
45
+ self.setStyleSheet("""
46
+ QDialog {
47
+ background-color: #1e1e2e;
48
+ color: #cdd6f4;
49
+ }
50
+ """)
51
+
52
+ layout = QVBoxLayout()
53
+ layout.setSpacing(15)
54
+ layout.setContentsMargins(20, 20, 20, 20)
55
+
56
+ # Header
57
+ header = QLabel(f"AnkiGammon {self.release_info['version']} is available")
58
+ header.setFont(QFont('Arial', 14, QFont.Bold))
59
+ header.setStyleSheet("color: #cdd6f4;")
60
+ layout.addWidget(header)
61
+
62
+ # Version comparison
63
+ version_label = QLabel(
64
+ f"You have: <b>{self.current_version}</b><br>"
65
+ f"Available: <b>{self.release_info['version']}</b>"
66
+ )
67
+ version_label.setStyleSheet("color: #a6adc8; margin-bottom: 10px;")
68
+ layout.addWidget(version_label)
69
+
70
+ # Release date (if available)
71
+ published = self.release_info.get('published_at', '')
72
+ if published:
73
+ try:
74
+ # Parse ISO date
75
+ pub_date = datetime.fromisoformat(published.replace('Z', '+00:00'))
76
+ date_str = pub_date.strftime('%B %d, %Y')
77
+ date_label = QLabel(f"Released: {date_str}")
78
+ date_label.setStyleSheet("color: #6c7086; font-size: 11px;")
79
+ layout.addWidget(date_label)
80
+ except (ValueError, AttributeError):
81
+ pass
82
+
83
+ # Release notes section
84
+ notes_label = QLabel("What's new:")
85
+ notes_label.setFont(QFont('Arial', 11, QFont.Bold))
86
+ notes_label.setStyleSheet("margin-top: 10px; color: #cdd6f4;")
87
+ layout.addWidget(notes_label)
88
+
89
+ # Release notes text
90
+ notes_text = QTextEdit()
91
+ notes_text.setReadOnly(True)
92
+ notes_text.setPlainText(
93
+ self.release_info.get('release_notes', 'No release notes available.')
94
+ )
95
+ notes_text.setMinimumHeight(150)
96
+ notes_text.setStyleSheet("""
97
+ QTextEdit {
98
+ background-color: #313244;
99
+ color: #cdd6f4;
100
+ border: 1px solid #45475a;
101
+ border-radius: 4px;
102
+ padding: 10px;
103
+ font-family: 'Consolas', 'Courier New', monospace;
104
+ font-size: 11px;
105
+ }
106
+ """)
107
+ layout.addWidget(notes_text)
108
+
109
+ layout.addStretch()
110
+
111
+ # Button layout
112
+ button_layout = QHBoxLayout()
113
+ button_layout.setSpacing(10)
114
+
115
+ # Skip button
116
+ skip_btn = QPushButton("Skip This Version")
117
+ skip_btn.clicked.connect(self._on_skip)
118
+ skip_btn.setCursor(Qt.PointingHandCursor)
119
+ skip_btn.setStyleSheet("""
120
+ QPushButton {
121
+ background-color: #45475a;
122
+ color: #cdd6f4;
123
+ border: 1px solid #585b70;
124
+ padding: 8px 16px;
125
+ border-radius: 4px;
126
+ font-weight: bold;
127
+ }
128
+ QPushButton:hover {
129
+ background-color: #585b70;
130
+ border: 1px solid #6c7086;
131
+ }
132
+ """)
133
+ button_layout.addWidget(skip_btn)
134
+
135
+ # Snooze button
136
+ snooze_btn = QPushButton("Remind Me Tomorrow")
137
+ snooze_btn.clicked.connect(self._on_snooze)
138
+ snooze_btn.setCursor(Qt.PointingHandCursor)
139
+ snooze_btn.setStyleSheet("""
140
+ QPushButton {
141
+ background-color: #45475a;
142
+ color: #cdd6f4;
143
+ border: 1px solid #585b70;
144
+ padding: 8px 16px;
145
+ border-radius: 4px;
146
+ font-weight: bold;
147
+ }
148
+ QPushButton:hover {
149
+ background-color: #585b70;
150
+ border: 1px solid #6c7086;
151
+ }
152
+ """)
153
+ button_layout.addWidget(snooze_btn)
154
+
155
+ button_layout.addStretch()
156
+
157
+ # Update button (prominent)
158
+ update_btn = QPushButton("Download Update")
159
+ update_btn.clicked.connect(self._on_update)
160
+ update_btn.setDefault(True)
161
+ update_btn.setCursor(Qt.PointingHandCursor)
162
+ update_btn.setStyleSheet("""
163
+ QPushButton {
164
+ background-color: #a6e3a1;
165
+ color: #1e1e2e;
166
+ border: none;
167
+ padding: 8px 20px;
168
+ border-radius: 4px;
169
+ font-weight: bold;
170
+ font-size: 12px;
171
+ }
172
+ QPushButton:hover {
173
+ background-color: #94e2d5;
174
+ }
175
+ QPushButton:pressed {
176
+ background-color: #89dceb;
177
+ }
178
+ """)
179
+ button_layout.addWidget(update_btn)
180
+
181
+ layout.addLayout(button_layout)
182
+
183
+ self.setLayout(layout)
184
+
185
+ def _on_update(self):
186
+ """Handle download update button click."""
187
+ self.user_action = 'update'
188
+ download_url = self.release_info.get('download_url', '')
189
+ if download_url:
190
+ webbrowser.open(download_url)
191
+ self.accept()
192
+
193
+ def _on_snooze(self):
194
+ """Handle snooze button click."""
195
+ self.user_action = 'snooze'
196
+ self.accept()
197
+
198
+ def _on_skip(self):
199
+ """Handle skip version button click."""
200
+ self.user_action = 'skip'
201
+ self.accept()
202
+
203
+ def get_snooze_until(self) -> str:
204
+ """Get the snooze-until timestamp.
205
+
206
+ Returns:
207
+ ISO format timestamp
208
+ """
209
+ return (datetime.now() + timedelta(hours=self.snooze_hours)).isoformat()
210
+
211
+
212
+ class CheckingUpdateDialog(QDialog):
213
+ """Simple dialog shown while checking for updates."""
214
+
215
+ def __init__(self, parent):
216
+ """Initialize checking dialog.
217
+
218
+ Args:
219
+ parent: Parent widget
220
+ """
221
+ super().__init__(parent)
222
+ self.setWindowTitle("Checking for Updates")
223
+ self.setModal(True)
224
+ self.setMinimumWidth(300)
225
+
226
+ # Remove close button
227
+ self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint)
228
+
229
+ # Set dialog background to match app theme
230
+ self.setStyleSheet("""
231
+ QDialog {
232
+ background-color: #1e1e2e;
233
+ color: #cdd6f4;
234
+ }
235
+ """)
236
+
237
+ layout = QVBoxLayout()
238
+ layout.setContentsMargins(30, 20, 30, 20)
239
+
240
+ label = QLabel("Checking for updates...")
241
+ label.setAlignment(Qt.AlignCenter)
242
+ label.setStyleSheet("color: #cdd6f4;")
243
+ layout.addWidget(label)
244
+
245
+ self.setLayout(layout)
246
+
247
+
248
+ class NoUpdateDialog(QDialog):
249
+ """Dialog shown when no update is available."""
250
+
251
+ def __init__(self, parent, current_version: str):
252
+ """Initialize no-update dialog.
253
+
254
+ Args:
255
+ parent: Parent widget
256
+ current_version: Current application version
257
+ """
258
+ super().__init__(parent)
259
+ self.setWindowTitle("No Updates Available")
260
+ self.setMinimumWidth(350)
261
+
262
+ # Set dialog background to match app theme
263
+ self.setStyleSheet("""
264
+ QDialog {
265
+ background-color: #1e1e2e;
266
+ color: #cdd6f4;
267
+ }
268
+ """)
269
+
270
+ layout = QVBoxLayout()
271
+ layout.setContentsMargins(20, 20, 20, 20)
272
+
273
+ # Success icon and message
274
+ message = QLabel(f"✓ You're up to date!\n\nVersion {current_version} is the latest version.")
275
+ message.setAlignment(Qt.AlignCenter)
276
+ message.setFont(QFont('Arial', 11))
277
+ message.setStyleSheet("color: #a6e3a1; margin: 20px 0;")
278
+ layout.addWidget(message)
279
+
280
+ # OK button
281
+ button_layout = QHBoxLayout()
282
+ button_layout.addStretch()
283
+
284
+ ok_btn = QPushButton("OK")
285
+ ok_btn.clicked.connect(self.accept)
286
+ ok_btn.setDefault(True)
287
+ ok_btn.setMinimumWidth(100)
288
+ ok_btn.setCursor(Qt.PointingHandCursor)
289
+ ok_btn.setStyleSheet("""
290
+ QPushButton {
291
+ background-color: #89b4fa;
292
+ color: #1e1e2e;
293
+ border: none;
294
+ padding: 8px 16px;
295
+ border-radius: 4px;
296
+ font-weight: bold;
297
+ }
298
+ QPushButton:hover {
299
+ background-color: #74c7ec;
300
+ }
301
+ """)
302
+ button_layout.addWidget(ok_btn)
303
+ button_layout.addStretch()
304
+
305
+ layout.addLayout(button_layout)
306
+
307
+ self.setLayout(layout)
308
+
309
+
310
+ class UpdateCheckFailedDialog(QDialog):
311
+ """Dialog shown when update check fails due to network error."""
312
+
313
+ def __init__(self, parent, current_version: str):
314
+ """Initialize failed check dialog.
315
+
316
+ Args:
317
+ parent: Parent widget
318
+ current_version: Current application version
319
+ """
320
+ super().__init__(parent)
321
+ self.setWindowTitle("Update Check Failed")
322
+ self.setMinimumWidth(400)
323
+
324
+ # Set dialog background to match app theme
325
+ self.setStyleSheet("""
326
+ QDialog {
327
+ background-color: #1e1e2e;
328
+ color: #cdd6f4;
329
+ }
330
+ """)
331
+
332
+ layout = QVBoxLayout()
333
+ layout.setContentsMargins(20, 20, 20, 20)
334
+
335
+ # Error icon and message
336
+ message = QLabel(
337
+ f"⚠ Couldn't check for updates\n\n"
338
+ f"Unable to connect to the internet.\n\n"
339
+ f"Current version: {current_version}"
340
+ )
341
+ message.setAlignment(Qt.AlignCenter)
342
+ message.setFont(QFont('Arial', 11))
343
+ message.setStyleSheet("color: #f38ba8; margin: 20px 0;")
344
+ layout.addWidget(message)
345
+
346
+ # OK button
347
+ button_layout = QHBoxLayout()
348
+ button_layout.addStretch()
349
+
350
+ ok_btn = QPushButton("OK")
351
+ ok_btn.clicked.connect(self.accept)
352
+ ok_btn.setDefault(True)
353
+ ok_btn.setMinimumWidth(100)
354
+ ok_btn.setCursor(Qt.PointingHandCursor)
355
+ ok_btn.setStyleSheet("""
356
+ QPushButton {
357
+ background-color: #89b4fa;
358
+ color: #1e1e2e;
359
+ border: none;
360
+ padding: 8px 16px;
361
+ border-radius: 4px;
362
+ font-weight: bold;
363
+ }
364
+ QPushButton:hover {
365
+ background-color: #74c7ec;
366
+ }
367
+ """)
368
+ button_layout.addWidget(ok_btn)
369
+ button_layout.addStretch()
370
+
371
+ layout.addLayout(button_layout)
372
+
373
+ self.setLayout(layout)