draftomen 0.3.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.
Files changed (59) hide show
  1. draftomen/__init__.py +17 -0
  2. draftomen/assets/draftomen.icns +0 -0
  3. draftomen/assets/draftomen.ico +0 -0
  4. draftomen/assets/draftomen_logo.png +0 -0
  5. draftomen/audit.py +606 -0
  6. draftomen/backtest.py +449 -0
  7. draftomen/benchmark.py +834 -0
  8. draftomen/carddb.py +1869 -0
  9. draftomen/cardimages.py +313 -0
  10. draftomen/cli.py +891 -0
  11. draftomen/config.py +133 -0
  12. draftomen/deckbuilder.py +2723 -0
  13. draftomen/events.py +772 -0
  14. draftomen/logfollow.py +451 -0
  15. draftomen/mock_session.py +745 -0
  16. draftomen/paths.py +120 -0
  17. draftomen/pickengine.py +1117 -0
  18. draftomen/pool.py +1259 -0
  19. draftomen/preferences.py +289 -0
  20. draftomen/qml/AboutDialog.qml +156 -0
  21. draftomen/qml/AppBar.qml +120 -0
  22. draftomen/qml/BacktestView.qml +316 -0
  23. draftomen/qml/BuildView.qml +1151 -0
  24. draftomen/qml/CardPreview.qml +434 -0
  25. draftomen/qml/DimensionalButton.qml +42 -0
  26. draftomen/qml/DimensionalComboBox.qml +182 -0
  27. draftomen/qml/DimensionalSurface.qml +118 -0
  28. draftomen/qml/DimensionalTabButton.qml +41 -0
  29. draftomen/qml/LiveDraftView.qml +468 -0
  30. draftomen/qml/Main.qml +175 -0
  31. draftomen/qml/NavigationRail.qml +226 -0
  32. draftomen/qml/PoolSummaryPanel.qml +322 -0
  33. draftomen/qml/PrivacyDialog.qml +96 -0
  34. draftomen/qml/RecentPickThumbnail.qml +83 -0
  35. draftomen/qml/RecentPicksGallery.qml +333 -0
  36. draftomen/qml/RecommendationRow.qml +404 -0
  37. draftomen/qml/SettingsSwitch.qml +141 -0
  38. draftomen/qml/SettingsView.qml +531 -0
  39. draftomen/qml/StateBanner.qml +189 -0
  40. draftomen/qml/StatusStrip.qml +84 -0
  41. draftomen/qml/Theme.qml +71 -0
  42. draftomen/qml/qmldir +23 -0
  43. draftomen/qt_adapter.py +884 -0
  44. draftomen/qt_gui.py +338 -0
  45. draftomen/qt_mock.py +63 -0
  46. draftomen/ranking.py +126 -0
  47. draftomen/replay.py +480 -0
  48. draftomen/session.py +3668 -0
  49. draftomen/setinfo.py +26 -0
  50. draftomen/seventeen.py +2766 -0
  51. draftomen/splash.py +617 -0
  52. draftomen/tui.py +4007 -0
  53. draftomen/watch.py +336 -0
  54. draftomen-0.3.0.dist-info/METADATA +156 -0
  55. draftomen-0.3.0.dist-info/RECORD +59 -0
  56. draftomen-0.3.0.dist-info/WHEEL +5 -0
  57. draftomen-0.3.0.dist-info/entry_points.txt +6 -0
  58. draftomen-0.3.0.dist-info/licenses/LICENSE +21 -0
  59. draftomen-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,404 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls 2.15
3
+ import QtQuick.Layouts 1.15
4
+
5
+ FocusScope {
6
+ id: root
7
+
8
+ required property var recommendation
9
+ required property bool selected
10
+ required property bool wide
11
+ required property bool secondaryStats
12
+ signal chosen(int grpId)
13
+
14
+ readonly property bool recommended: recommendation.rank === 1
15
+ readonly property bool keyboardFocused: activeFocus
16
+ readonly property string colorsText: recommendation.card.colors.length > 0
17
+ ? recommendation.card.colors.join(" · ") : "Colorless"
18
+ readonly property string alsaText: recommendation.average_last_seen_at !== null
19
+ && recommendation.average_last_seen_at !== undefined
20
+ ? Number(recommendation.average_last_seen_at).toFixed(2) : "—"
21
+ readonly property string manaValueText: recommendation.card.mana_value !== null
22
+ && recommendation.card.mana_value !== undefined
23
+ ? Number(recommendation.card.mana_value).toFixed(0) : "—"
24
+ readonly property string winRateText: recommendation.win_rate !== null
25
+ && recommendation.win_rate !== undefined
26
+ ? (Number(recommendation.win_rate) * 100).toFixed(1) + "%" : "—"
27
+ readonly property string stateText: {
28
+ if (keyboardFocused)
29
+ return "Keyboard focused"
30
+ if (selected)
31
+ return "Selected"
32
+ if (recommended)
33
+ return "Recommended"
34
+ return ""
35
+ }
36
+ readonly property color stateColor: {
37
+ if (keyboardFocused)
38
+ return Theme.focus
39
+ if (selected)
40
+ return Theme.warning
41
+ if (recommended)
42
+ return Theme.primary
43
+ return Theme.outline
44
+ }
45
+
46
+ implicitHeight: wide
47
+ ? Math.max(84, wideCardDetails.implicitHeight + 16)
48
+ : Math.max(112, narrowCardDetails.implicitHeight + 16)
49
+
50
+ Accessible.role: Accessible.ListItem
51
+ Accessible.name: "Rank " + recommendation.rank + ", "
52
+ + recommendation.card.name + ", DO score " + recommendation.score
53
+ Accessible.description: stateText + ". Press Enter or Space to choose this card."
54
+ activeFocusOnTab: true
55
+
56
+ Keys.onReturnPressed: chosen(recommendation.card.grp_id)
57
+ Keys.onSpacePressed: chosen(recommendation.card.grp_id)
58
+
59
+ Rectangle {
60
+ anchors.fill: parent
61
+ color: {
62
+ if (root.selected)
63
+ return Theme.surfaceHighest
64
+ if (root.recommended)
65
+ return Theme.primaryDark
66
+ return Theme.surface
67
+ }
68
+ border.color: root.stateColor
69
+ border.width: root.keyboardFocused || root.selected || root.recommended ? 2 : 1
70
+ radius: Theme.radius
71
+ }
72
+
73
+ Rectangle {
74
+ visible: root.recommended || root.selected || root.keyboardFocused
75
+ anchors.left: parent.left
76
+ anchors.top: parent.top
77
+ anchors.bottom: parent.bottom
78
+ width: 3
79
+ color: root.stateColor
80
+ radius: Theme.radius
81
+ }
82
+
83
+ MouseArea {
84
+ anchors.fill: parent
85
+ cursorShape: Qt.PointingHandCursor
86
+ onClicked: {
87
+ root.forceActiveFocus()
88
+ root.chosen(root.recommendation.card.grp_id)
89
+ }
90
+ }
91
+
92
+ RowLayout {
93
+ anchors.fill: parent
94
+ anchors.leftMargin: 14
95
+ anchors.rightMargin: 12
96
+ anchors.topMargin: 8
97
+ anchors.bottomMargin: 8
98
+ spacing: 8
99
+
100
+ Label {
101
+ id: rankLabel
102
+ objectName: "recommendationRank"
103
+ Layout.preferredWidth: 30
104
+ Layout.minimumWidth: 30
105
+ Layout.maximumWidth: 30
106
+ text: ("0" + String(root.recommendation.rank)).slice(-2)
107
+ color: root.recommended ? Theme.primary : Theme.textMuted
108
+ font.family: fixedFontFamily
109
+ font.bold: true
110
+ horizontalAlignment: Text.AlignHCenter
111
+ verticalAlignment: Text.AlignVCenter
112
+ }
113
+
114
+ RowLayout {
115
+ id: cardCell
116
+ objectName: "recommendationCardCell"
117
+ Layout.fillWidth: true
118
+ Layout.minimumWidth: 0
119
+ Layout.fillHeight: true
120
+ spacing: 8
121
+
122
+ Rectangle {
123
+ id: thumbnailFrame
124
+ objectName: "recommendationThumbnailFrame"
125
+ Layout.preferredWidth: 50
126
+ Layout.minimumWidth: 50
127
+ Layout.maximumWidth: 50
128
+ Layout.minimumHeight: 0
129
+ Layout.fillHeight: true
130
+ color: Theme.background
131
+ border.color: Theme.outline
132
+ border.width: 1
133
+ radius: 6
134
+ clip: true
135
+
136
+ Image {
137
+ id: thumbnailImage
138
+ objectName: "recommendationThumbnailImage"
139
+ anchors.fill: parent
140
+ anchors.margins: 2
141
+ source: root.recommendation.card.image_path || ""
142
+ fillMode: Image.PreserveAspectFit
143
+ asynchronous: true
144
+ visible: status === Image.Ready
145
+ }
146
+
147
+ ColumnLayout {
148
+ id: thumbnailFallback
149
+ objectName: "recommendationThumbnailFallback"
150
+ anchors.fill: parent
151
+ anchors.margins: 4
152
+ spacing: 3
153
+ visible: !thumbnailImage.visible
154
+
155
+ Label {
156
+ objectName: "recommendationThumbnailFallbackLabel"
157
+ Layout.fillWidth: true
158
+ text: {
159
+ if (thumbnailImage.status === Image.Error)
160
+ return "Image failed to load"
161
+ if (thumbnailImage.source.toString().length > 0)
162
+ return "Loading image"
163
+ return "No image available"
164
+ }
165
+ color: Theme.textMuted
166
+ font.pixelSize: Theme.textPixelSize(9)
167
+ font.bold: true
168
+ horizontalAlignment: Text.AlignHCenter
169
+ verticalAlignment: Text.AlignVCenter
170
+ wrapMode: Text.WrapAnywhere
171
+ }
172
+ }
173
+ }
174
+
175
+ ColumnLayout {
176
+ id: wideCardDetails
177
+ objectName: "recommendationWideCardDetails"
178
+ visible: root.wide
179
+ Layout.fillWidth: true
180
+ Layout.minimumWidth: 0
181
+ spacing: 1
182
+
183
+ Label {
184
+ objectName: "recommendationName"
185
+ Layout.fillWidth: true
186
+ Layout.minimumWidth: 0
187
+ text: root.recommendation.card.name
188
+ color: Theme.text
189
+ font.pixelSize: Theme.textPixelSize(14)
190
+ font.bold: root.recommended || root.selected
191
+ wrapMode: Text.WordWrap
192
+ elide: Text.ElideNone
193
+ }
194
+
195
+ RowLayout {
196
+ Layout.fillWidth: true
197
+ Layout.minimumWidth: 0
198
+ spacing: 8
199
+
200
+ Label {
201
+ objectName: "recommendationMetadata"
202
+ Layout.fillWidth: true
203
+ Layout.minimumWidth: 0
204
+ text: root.recommendation.card.types.join(" · ")
205
+ + " · ALSA " + root.alsaText
206
+ + " · MV " + root.manaValueText
207
+ + " · " + (root.recommendation.source_label
208
+ || "Source unavailable")
209
+ color: Theme.textMuted
210
+ font.pixelSize: Theme.textPixelSize(11)
211
+ elide: Text.ElideRight
212
+ }
213
+
214
+ Label {
215
+ objectName: "recommendationStateBadge"
216
+ visible: root.stateText.length > 0
217
+ Layout.alignment: Qt.AlignVCenter
218
+ Layout.preferredWidth: 102
219
+ Layout.minimumWidth: 102
220
+ Layout.maximumWidth: 102
221
+ text: root.stateText
222
+ color: root.stateColor
223
+ font.pixelSize: Theme.textPixelSize(10)
224
+ font.bold: true
225
+ font.letterSpacing: 0.8
226
+ horizontalAlignment: Text.AlignRight
227
+ }
228
+ }
229
+ }
230
+
231
+ ColumnLayout {
232
+ id: narrowCardDetails
233
+ objectName: "recommendationNarrowCardDetails"
234
+ visible: !root.wide
235
+ Layout.fillWidth: true
236
+ Layout.minimumWidth: 0
237
+ spacing: 4
238
+
239
+ RowLayout {
240
+ Layout.fillWidth: true
241
+ Layout.minimumWidth: 0
242
+ spacing: 8
243
+
244
+ Label {
245
+ objectName: "recommendationName"
246
+ Layout.fillWidth: true
247
+ Layout.minimumWidth: 0
248
+ text: root.recommendation.card.name
249
+ color: Theme.text
250
+ font.pixelSize: Theme.textPixelSize(14)
251
+ font.bold: root.recommended || root.selected
252
+ wrapMode: Text.WrapAnywhere
253
+ elide: Text.ElideNone
254
+ }
255
+
256
+ Label {
257
+ objectName: "recommendationStateBadge"
258
+ visible: root.stateText.length > 0
259
+ Layout.alignment: Qt.AlignVCenter
260
+ Layout.preferredWidth: 102
261
+ Layout.minimumWidth: 102
262
+ Layout.maximumWidth: 102
263
+ text: root.stateText
264
+ color: root.stateColor
265
+ font.pixelSize: Theme.textPixelSize(10)
266
+ font.bold: true
267
+ font.letterSpacing: 0.8
268
+ horizontalAlignment: Text.AlignRight
269
+ }
270
+ }
271
+
272
+ RowLayout {
273
+ Layout.fillWidth: true
274
+ spacing: 12
275
+
276
+ Label {
277
+ Layout.fillWidth: true
278
+ text: root.colorsText
279
+ color: root.recommendation.card.colors.length > 0
280
+ ? Theme.colorForMana(root.recommendation.card.colors[0])
281
+ : Theme.textMuted
282
+ elide: Text.ElideRight
283
+ }
284
+
285
+ Label {
286
+ text: root.recommendation.color_fit || "Open"
287
+ color: root.recommendation.color_fit === "Off color"
288
+ ? Theme.warning : Theme.textMuted
289
+ horizontalAlignment: Text.AlignRight
290
+ }
291
+ }
292
+
293
+ RowLayout {
294
+ Layout.fillWidth: true
295
+ spacing: 12
296
+
297
+ Label {
298
+ text: "DO " + root.recommendation.score
299
+ color: root.recommended ? Theme.primary : Theme.text
300
+ font.family: fixedFontFamily
301
+ font.pixelSize: Theme.textPixelSize(12)
302
+ font.bold: true
303
+ }
304
+
305
+ Label {
306
+ text: "17L " + root.winRateText
307
+ color: Theme.text
308
+ font.family: fixedFontFamily
309
+ font.pixelSize: Theme.textPixelSize(12)
310
+ }
311
+
312
+ Label {
313
+ text: "Grade " + (root.recommendation.letter_grade || "—")
314
+ color: Theme.warning
315
+ font.pixelSize: Theme.textPixelSize(12)
316
+ font.bold: true
317
+ }
318
+
319
+ Label {
320
+ visible: root.secondaryStats
321
+ Layout.fillWidth: true
322
+ text: "ALSA " + root.alsaText
323
+ + " · MV " + root.manaValueText
324
+ + " · " + (root.recommendation.source_label
325
+ || "Source unavailable")
326
+ color: Theme.textMuted
327
+ font.pixelSize: Theme.textPixelSize(11)
328
+ horizontalAlignment: Text.AlignRight
329
+ elide: Text.ElideRight
330
+ }
331
+ }
332
+ }
333
+ }
334
+
335
+ Label {
336
+ objectName: "recommendationColors"
337
+ visible: root.wide
338
+ Layout.preferredWidth: 70
339
+ Layout.minimumWidth: 70
340
+ Layout.maximumWidth: 70
341
+ text: root.colorsText
342
+ color: root.recommendation.card.colors.length > 0
343
+ ? Theme.colorForMana(root.recommendation.card.colors[0])
344
+ : Theme.textMuted
345
+ horizontalAlignment: Text.AlignHCenter
346
+ verticalAlignment: Text.AlignVCenter
347
+ elide: Text.ElideRight
348
+ }
349
+
350
+ Label {
351
+ objectName: "recommendationScore"
352
+ visible: root.wide
353
+ Layout.preferredWidth: 58
354
+ Layout.minimumWidth: 58
355
+ Layout.maximumWidth: 58
356
+ text: root.recommendation.score
357
+ color: root.recommended ? Theme.primary : Theme.text
358
+ font.family: fixedFontFamily
359
+ font.bold: true
360
+ horizontalAlignment: Text.AlignRight
361
+ verticalAlignment: Text.AlignVCenter
362
+ }
363
+
364
+ Label {
365
+ objectName: "recommendationWinRate"
366
+ visible: root.wide
367
+ Layout.preferredWidth: 68
368
+ Layout.minimumWidth: 68
369
+ Layout.maximumWidth: 68
370
+ text: root.winRateText
371
+ color: Theme.text
372
+ font.family: fixedFontFamily
373
+ horizontalAlignment: Text.AlignRight
374
+ verticalAlignment: Text.AlignVCenter
375
+ }
376
+
377
+ Label {
378
+ objectName: "recommendationGrade"
379
+ visible: root.wide
380
+ Layout.preferredWidth: 44
381
+ Layout.minimumWidth: 44
382
+ Layout.maximumWidth: 44
383
+ text: root.recommendation.letter_grade || "—"
384
+ color: Theme.warning
385
+ font.bold: true
386
+ horizontalAlignment: Text.AlignHCenter
387
+ verticalAlignment: Text.AlignVCenter
388
+ }
389
+
390
+ Label {
391
+ objectName: "recommendationFit"
392
+ visible: root.wide
393
+ Layout.preferredWidth: 82
394
+ Layout.minimumWidth: 82
395
+ Layout.maximumWidth: 82
396
+ text: root.recommendation.color_fit || "Open"
397
+ color: root.recommendation.color_fit === "Off color"
398
+ ? Theme.warning : Theme.textMuted
399
+ horizontalAlignment: Text.AlignRight
400
+ verticalAlignment: Text.AlignVCenter
401
+ elide: Text.ElideRight
402
+ }
403
+ }
404
+ }
@@ -0,0 +1,141 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls 2.15
3
+
4
+ Switch {
5
+ id: control
6
+
7
+ // Keep the complete switch target large enough for mouse and touch input.
8
+ implicitWidth: 68
9
+ implicitHeight: Theme.targetHeight
10
+ padding: 0
11
+ activeFocusOnTab: true
12
+ focusPolicy: Qt.StrongFocus
13
+
14
+ readonly property bool visualChecked: control.checked
15
+ readonly property bool visualUnchecked: !control.visualChecked
16
+ readonly property bool visualDisabled: !control.enabled
17
+ readonly property bool visualFocused: control.activeFocus
18
+ readonly property string visualState: {
19
+ if (control.visualDisabled)
20
+ return "disabled"
21
+ return control.visualChecked ? "checked" : "unchecked"
22
+ }
23
+
24
+ // The checked track is intentionally the darkest state. The bright thumb
25
+ // and state glyph keep the on state legible against it.
26
+ readonly property color checkedTrackColor: Theme.background
27
+ readonly property color uncheckedTrackColor: Theme.neutral
28
+ readonly property color disabledTrackColor: Theme.controlDisabled
29
+ readonly property color disabledThumbColor: Theme.controlDisabledContent
30
+ readonly property color visualTrackColor: {
31
+ if (control.visualDisabled)
32
+ return control.disabledTrackColor
33
+ return control.visualChecked ? control.checkedTrackColor : control.uncheckedTrackColor
34
+ }
35
+ readonly property color visualTrackBorderColor: {
36
+ if (control.visualDisabled)
37
+ return Theme.outlineDisabled
38
+ return control.visualChecked ? Theme.primary : Theme.controlNeutralBorder
39
+ }
40
+ readonly property color visualThumbColor: {
41
+ if (control.visualDisabled)
42
+ return control.disabledThumbColor
43
+ return control.visualChecked ? Theme.primary : Theme.controlHighlight
44
+ }
45
+ readonly property color visualThumbBorderColor: {
46
+ if (control.visualDisabled)
47
+ return Theme.outlineDisabled
48
+ return control.visualChecked ? Theme.controlHighlight : Theme.controlNeutralBorder
49
+ }
50
+ readonly property color visualContentColor: {
51
+ if (control.visualDisabled)
52
+ return Theme.controlDisabledContent
53
+ return control.visualChecked ? Theme.controlHighlight : Theme.neutralContent
54
+ }
55
+ readonly property color visualThumbContentColor: {
56
+ if (control.visualDisabled)
57
+ return Theme.controlDisabled
58
+ return control.visualChecked ? Theme.primaryContent : Theme.neutral
59
+ }
60
+ readonly property color visualFocusColor: Theme.focus
61
+
62
+ background: Item { }
63
+ contentItem: Item { }
64
+
65
+ indicator: Item {
66
+ id: switchIndicator
67
+ objectName: "settingsSwitchIndicator"
68
+ x: (control.width - width) / 2
69
+ y: (control.height - height) / 2
70
+ width: 68
71
+ height: Theme.targetHeight
72
+
73
+ Rectangle {
74
+ id: track
75
+ objectName: "settingsSwitchTrack"
76
+ x: (switchIndicator.width - width) / 2
77
+ y: (switchIndicator.height - height) / 2
78
+ width: 58
79
+ height: 28
80
+ radius: height / 2
81
+ color: control.visualTrackColor
82
+ border.color: control.visualTrackBorderColor
83
+ border.width: 1
84
+
85
+ Text {
86
+ objectName: "settingsSwitchTrackState"
87
+ anchors.fill: parent
88
+ anchors.leftMargin: control.visualChecked ? 6 : 0
89
+ anchors.rightMargin: control.visualChecked ? 0 : 6
90
+ text: control.visualChecked ? "ON" : "OFF"
91
+ color: control.visualContentColor
92
+ font.pixelSize: Theme.textPixelSize(8)
93
+ font.bold: true
94
+ horizontalAlignment: control.visualChecked ? Text.AlignLeft : Text.AlignRight
95
+ verticalAlignment: Text.AlignVCenter
96
+ opacity: 0.92
97
+ }
98
+ }
99
+
100
+ Rectangle {
101
+ id: thumb
102
+ objectName: "settingsSwitchThumb"
103
+ x: control.visualChecked ? track.x + track.width - width : track.x
104
+ y: track.y
105
+ width: 28
106
+ height: 28
107
+ radius: width / 2
108
+ color: control.visualThumbColor
109
+ border.color: control.visualThumbBorderColor
110
+ border.width: 1
111
+
112
+ Text {
113
+ objectName: "settingsSwitchStateText"
114
+ anchors.fill: parent
115
+ text: control.visualChecked ? "ON" : "OFF"
116
+ color: control.visualThumbContentColor
117
+ font.pixelSize: Theme.textPixelSize(8)
118
+ font.bold: true
119
+ horizontalAlignment: Text.AlignHCenter
120
+ verticalAlignment: Text.AlignVCenter
121
+ }
122
+
123
+ Behavior on x {
124
+ NumberAnimation { duration: 120; easing.type: Easing.OutCubic }
125
+ }
126
+ }
127
+
128
+ Rectangle {
129
+ objectName: "settingsSwitchFocusRing"
130
+ x: track.x - Theme.controlFocusGap
131
+ y: track.y - Theme.controlFocusGap
132
+ width: track.width + Theme.controlFocusGap * 2
133
+ height: track.height + Theme.controlFocusGap * 2
134
+ radius: height / 2
135
+ color: "transparent"
136
+ border.color: control.visualFocusColor
137
+ border.width: 2
138
+ visible: !control.visualDisabled && control.visualFocused
139
+ }
140
+ }
141
+ }