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,322 @@
1
+ pragma ComponentBehavior: Bound
2
+
3
+ import QtQuick 2.15
4
+ import QtQuick.Controls 2.15
5
+ import QtQuick.Layouts 1.15
6
+
7
+ Rectangle {
8
+ id: root
9
+
10
+ required property var pool
11
+ required property bool narrow
12
+
13
+ readonly property bool hasPool: root.pool && root.pool.total_cards > 0
14
+ readonly property var colorDistribution: root.pool
15
+ && root.pool.color_distribution ? root.pool.color_distribution : []
16
+ readonly property var manaCurve: root.pool
17
+ && root.pool.mana_curve ? root.pool.mana_curve : []
18
+ readonly property var recentPicks: root.pool && root.pool.recent_picks
19
+ ? root.pool.recent_picks : []
20
+ readonly property int maxManaCount: {
21
+ let largest = 0
22
+ for (let index = 0; index < root.manaCurve.length; index++)
23
+ largest = Math.max(largest, Number(root.manaCurve[index]))
24
+ return largest
25
+ }
26
+
27
+ color: Theme.surfaceLow
28
+ border.color: Theme.outline
29
+ border.width: 1
30
+ radius: Theme.radius
31
+ Accessible.role: Accessible.Pane
32
+ Accessible.name: "Draft pool summary"
33
+ Accessible.description: "Scrollable pool details. Use Up and Down, Page Up and Page Down, Home, or End to move through the summary."
34
+ function averageManaValueText() {
35
+ const average = root.pool ? root.pool.average_mana_value : null
36
+ if (average === null || average === undefined)
37
+ return "Average mana value: —"
38
+ return "Average mana value: " + Number(average).toFixed(2)
39
+ }
40
+
41
+ function manaLabel(index) {
42
+ return index === 6 ? "6+" : String(index)
43
+ }
44
+
45
+ function colorLabel(symbol) {
46
+ if (symbol === "W") return "White"
47
+ if (symbol === "U") return "Blue"
48
+ if (symbol === "B") return "Black"
49
+ if (symbol === "R") return "Red"
50
+ if (symbol === "G") return "Green"
51
+ return "Colorless"
52
+ }
53
+
54
+ Flickable {
55
+ id: poolFlickable
56
+ objectName: "poolSummaryFlickable"
57
+ anchors.fill: parent
58
+ anchors.margins: Theme.panelPadding
59
+ contentWidth: width
60
+ contentHeight: poolColumn.implicitHeight
61
+ clip: true
62
+ boundsBehavior: Flickable.StopAtBounds
63
+ activeFocusOnTab: true
64
+ Accessible.role: Accessible.Pane
65
+ Accessible.name: "Scrollable pool details"
66
+ Accessible.description: "Use Up and Down, Page Up and Page Down, Home, or End to scroll the pool summary."
67
+
68
+ function scrollTo(position) {
69
+ contentY = Math.max(0, Math.min(
70
+ position, Math.max(0, contentHeight - height)
71
+ ))
72
+ }
73
+
74
+ Keys.onPressed: function(event) {
75
+ const pageStep = Math.max(1, height - 24)
76
+ const lineStep = 40
77
+ switch (event.key) {
78
+ case Qt.Key_Up:
79
+ poolFlickable.scrollTo(contentY - lineStep)
80
+ break
81
+ case Qt.Key_Down:
82
+ poolFlickable.scrollTo(contentY + lineStep)
83
+ break
84
+ case Qt.Key_PageUp:
85
+ poolFlickable.scrollTo(contentY - pageStep)
86
+ break
87
+ case Qt.Key_PageDown:
88
+ poolFlickable.scrollTo(contentY + pageStep)
89
+ break
90
+ case Qt.Key_Home:
91
+ poolFlickable.scrollTo(0)
92
+ break
93
+ case Qt.Key_End:
94
+ poolFlickable.scrollTo(contentHeight - height)
95
+ break
96
+ default:
97
+ return
98
+ }
99
+ event.accepted = true
100
+ }
101
+
102
+ ScrollBar.vertical: ScrollBar {
103
+ objectName: "poolSummaryScrollBar"
104
+ policy: ScrollBar.AsNeeded
105
+ width: 8
106
+ Accessible.name: "Pool summary vertical scrollbar"
107
+ }
108
+ ColumnLayout {
109
+ id: poolColumn
110
+ width: parent.width
111
+ spacing: 12
112
+
113
+ RowLayout {
114
+ Layout.fillWidth: true
115
+
116
+ Label {
117
+ Layout.fillWidth: true
118
+ text: "POOL SUMMARY"
119
+ color: Theme.textMuted
120
+ font.pixelSize: Theme.textPixelSize(11)
121
+ font.bold: true
122
+ font.letterSpacing: 1.2
123
+ }
124
+
125
+ Label {
126
+ objectName: "poolCount"
127
+ text: (root.pool ? root.pool.total_cards : 0)
128
+ + " / " + (root.pool ? root.pool.target_cards : 0)
129
+ + " cards"
130
+ color: Theme.primary
131
+ font.family: fixedFontFamily
132
+ font.bold: true
133
+ }
134
+ }
135
+
136
+ Rectangle {
137
+ visible: !root.hasPool
138
+ Layout.fillWidth: true
139
+ Layout.preferredHeight: 108
140
+ color: Theme.surface
141
+ radius: Theme.radius
142
+
143
+ ColumnLayout {
144
+ anchors.centerIn: parent
145
+ width: parent.width - 24
146
+ spacing: 6
147
+
148
+ Label {
149
+ Layout.fillWidth: true
150
+ text: "No drafted cards yet"
151
+ color: Theme.text
152
+ font.bold: true
153
+ horizontalAlignment: Text.AlignHCenter
154
+ }
155
+
156
+ Label {
157
+ Layout.fillWidth: true
158
+ text: "Your pool will appear after the first pick."
159
+ color: Theme.textMuted
160
+ horizontalAlignment: Text.AlignHCenter
161
+ wrapMode: Text.WordWrap
162
+ }
163
+ }
164
+ }
165
+
166
+ ColumnLayout {
167
+ visible: root.hasPool
168
+ Layout.fillWidth: true
169
+ spacing: 8
170
+
171
+ GridLayout {
172
+ Layout.fillWidth: true
173
+ columns: 2
174
+ columnSpacing: 12
175
+ rowSpacing: 6
176
+
177
+ Label { text: "Inferred pair"; color: Theme.textMuted }
178
+ Label {
179
+ text: root.pool.inferred_pair || "Open"
180
+ color: Theme.text
181
+ font.bold: true
182
+ }
183
+
184
+ Label { text: "Commitment"; color: Theme.textMuted }
185
+ Label {
186
+ text: Math.round(Number(root.pool.commitment || 0) * 100)
187
+ + "% building"
188
+ color: Theme.warning
189
+ font.family: fixedFontFamily
190
+ }
191
+ }
192
+
193
+ Label {
194
+ text: "COLOR DISTRIBUTION"
195
+ color: Theme.textMuted
196
+ font.pixelSize: Theme.textPixelSize(11)
197
+ font.bold: true
198
+ font.letterSpacing: 1
199
+ }
200
+
201
+ Flow {
202
+ Layout.fillWidth: true
203
+ spacing: 5
204
+
205
+ Repeater {
206
+ model: root.colorDistribution
207
+
208
+ delegate: Rectangle {
209
+ required property var modelData
210
+ width: colorName.implicitWidth + colorCount.implicitWidth + 24
211
+ height: 26
212
+ radius: Theme.radius
213
+ color: Theme.surface
214
+ border.color: Theme.outline
215
+
216
+ Row {
217
+ anchors.centerIn: parent
218
+ spacing: 5
219
+
220
+ Rectangle {
221
+ width: 8
222
+ height: 8
223
+ radius: 4
224
+ anchors.verticalCenter: parent.verticalCenter
225
+ color: Theme.colorForMana(modelData[0])
226
+ }
227
+
228
+ Label {
229
+ id: colorName
230
+ text: root.colorLabel(modelData[0])
231
+ color: Theme.textMuted
232
+ font.pixelSize: Theme.textPixelSize(11)
233
+ }
234
+
235
+ Label {
236
+ id: colorCount
237
+ text: String(modelData[1])
238
+ color: Theme.text
239
+ font.family: fixedFontFamily
240
+ font.bold: true
241
+ }
242
+ }
243
+ }
244
+ }
245
+ }
246
+
247
+ RowLayout {
248
+ Layout.fillWidth: true
249
+
250
+ Label {
251
+ Layout.fillWidth: true
252
+ text: "MANA CURVE"
253
+ color: Theme.textMuted
254
+ font.pixelSize: Theme.textPixelSize(11)
255
+ font.bold: true
256
+ font.letterSpacing: 1
257
+ }
258
+
259
+ Label {
260
+ objectName: "poolManaCurveAverage"
261
+ text: root.averageManaValueText()
262
+ color: Theme.textMuted
263
+ font.pixelSize: Theme.textPixelSize(11)
264
+ Accessible.name: text
265
+ }
266
+ }
267
+
268
+ RowLayout {
269
+ Layout.fillWidth: true
270
+ Layout.preferredHeight: 94
271
+ spacing: 5
272
+
273
+ Repeater {
274
+ model: root.manaCurve
275
+
276
+ delegate: ColumnLayout {
277
+ required property int index
278
+ required property var modelData
279
+ Layout.fillWidth: true
280
+ Layout.fillHeight: true
281
+ spacing: 3
282
+
283
+ Item {
284
+ Layout.fillWidth: true
285
+ Layout.fillHeight: true
286
+
287
+ Rectangle {
288
+ anchors.left: parent.left
289
+ anchors.right: parent.right
290
+ anchors.bottom: parent.bottom
291
+ height: root.maxManaCount > 0
292
+ ? parent.height * Number(modelData)
293
+ / root.maxManaCount : 0
294
+ color: Theme.primary
295
+ opacity: index === 0 ? 0.35 : 0.7
296
+ radius: 2
297
+ }
298
+ }
299
+
300
+ Label {
301
+ Layout.fillWidth: true
302
+ text: root.manaLabel(index)
303
+ color: Theme.textMuted
304
+ font.pixelSize: Theme.textPixelSize(10)
305
+ horizontalAlignment: Text.AlignHCenter
306
+ }
307
+ }
308
+ }
309
+ }
310
+
311
+ RecentPicksGallery {
312
+ objectName: "recentPicksGallery"
313
+ Layout.fillWidth: true
314
+ pool: root.pool
315
+ narrow: root.narrow
316
+ }
317
+ }
318
+
319
+ Item { Layout.fillHeight: true }
320
+ }
321
+ }
322
+ }
@@ -0,0 +1,96 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls 2.15
3
+ import QtQuick.Layouts 1.15
4
+
5
+ Dialog {
6
+ id: root
7
+
8
+ property var returnFocusItem: null
9
+
10
+ objectName: "privacyDialog"
11
+ parent: Overlay.overlay
12
+ modal: true
13
+ focus: true
14
+ closePolicy: Popup.CloseOnEscape
15
+ title: "Privacy"
16
+ width: Math.min(420, Math.max(300, parent ? parent.width - 32 : 420))
17
+ x: parent ? Math.max(16, Math.round((parent.width - width) / 2)) : 16
18
+ y: parent ? Math.max(16, Math.round((parent.height - height) / 2)) : 16
19
+ padding: 16
20
+
21
+ Overlay.modal: Rectangle {
22
+ color: "#99000000"
23
+ }
24
+
25
+ background: Rectangle {
26
+ color: Theme.surface
27
+ border.color: Theme.outline
28
+ border.width: 1
29
+ radius: Theme.radius
30
+ }
31
+
32
+ header: Rectangle {
33
+ objectName: "privacyDialogHeader"
34
+ implicitHeight: 52
35
+ color: Theme.surfaceHigh
36
+ border.color: Theme.outline
37
+ border.width: 1
38
+
39
+ Label {
40
+ objectName: "privacyDialogTitle"
41
+ anchors.fill: parent
42
+ anchors.leftMargin: 16
43
+ anchors.rightMargin: 16
44
+ text: root.title
45
+ color: Theme.text
46
+ font.pixelSize: Theme.textPixelSize(18)
47
+ font.bold: true
48
+ verticalAlignment: Text.AlignVCenter
49
+ Accessible.name: text
50
+ }
51
+ }
52
+
53
+ onClosed: {
54
+ const opener = root.returnFocusItem
55
+ root.returnFocusItem = null
56
+ if (opener && opener.visible && opener.enabled)
57
+ opener.forceActiveFocus()
58
+ }
59
+
60
+ contentItem: ColumnLayout {
61
+ spacing: 12
62
+ implicitWidth: 360
63
+
64
+ Label {
65
+ objectName: "privacyDialogDisclosure"
66
+ Layout.fillWidth: true
67
+ text: "All user data remains on the user's computer. Draft Omen does not send your personal data to us."
68
+ color: Theme.text
69
+ wrapMode: Text.WordWrap
70
+ Accessible.name: text
71
+ }
72
+ }
73
+
74
+ footer: DialogButtonBox {
75
+ implicitHeight: 58
76
+ alignment: Qt.AlignRight
77
+ background: Rectangle {
78
+ color: Theme.surfaceHigh
79
+ border.color: Theme.outline
80
+ border.width: 1
81
+ }
82
+
83
+ DimensionalButton {
84
+ id: closeButton
85
+ objectName: "privacyDialogCloseButton"
86
+ text: "Close"
87
+ accented: false
88
+ implicitWidth: 96
89
+ activeFocusOnTab: true
90
+ focusPolicy: Qt.StrongFocus
91
+ Accessible.role: Accessible.Button
92
+ Accessible.name: "Close Privacy dialog"
93
+ onClicked: root.close()
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,83 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls 2.15
3
+
4
+ Item {
5
+ id: root
6
+
7
+ required property int pickIndex
8
+ required property var recentPick
9
+ signal hoverEntered()
10
+ signal hoverExited()
11
+
12
+ readonly property var card: root.recentPick && root.recentPick.card
13
+ ? root.recentPick.card : null
14
+ readonly property var imageState: root.recentPick && root.recentPick.image
15
+ ? root.recentPick.image : null
16
+
17
+ implicitHeight: width * 1.4
18
+ objectName: "recentPickThumbnail" + root.pickIndex
19
+ Accessible.role: Accessible.Graphic
20
+ Accessible.name: root.card ? root.card.name : "Recent pick"
21
+ Accessible.description: root.card
22
+ ? root.card.name + ". Hover for a card preview."
23
+ : "Hover for a card preview."
24
+
25
+ Rectangle {
26
+ anchors.fill: parent
27
+ color: Theme.surface
28
+ border.color: Theme.outline
29
+ border.width: 1
30
+ radius: Theme.radius
31
+ clip: true
32
+
33
+ Image {
34
+ id: thumbnailImage
35
+ objectName: "recentPickThumbnailImage"
36
+ anchors.fill: parent
37
+ anchors.margins: 2
38
+ source: root.imageState
39
+ && root.imageState.phase === "ready"
40
+ && root.imageState.image_path
41
+ ? root.imageState.image_path : ""
42
+ fillMode: Image.PreserveAspectFit
43
+ asynchronous: true
44
+ visible: status === Image.Ready
45
+ }
46
+
47
+ Label {
48
+ id: fallbackLabel
49
+ objectName: "recentPickThumbnailFallbackLabel"
50
+ anchors.fill: parent
51
+ anchors.margins: 5
52
+ text: {
53
+ if (thumbnailImage.status === Image.Error)
54
+ return "Image failed to display"
55
+ if (root.imageState && root.imageState.phase === "loading")
56
+ return "Loading image"
57
+ if (root.imageState && root.imageState.phase === "failed")
58
+ return "Image unavailable"
59
+ if (root.imageState && root.imageState.phase === "ready"
60
+ && thumbnailImage.source.toString().length > 0)
61
+ return "Loading image"
62
+ return "No image available"
63
+ }
64
+ color: Theme.textMuted
65
+ font.pixelSize: Theme.textPixelSize(10)
66
+ font.bold: true
67
+ horizontalAlignment: Text.AlignHCenter
68
+ verticalAlignment: Text.AlignVCenter
69
+ wrapMode: Text.WrapAnywhere
70
+ visible: !thumbnailImage.visible
71
+ }
72
+ }
73
+
74
+ HoverHandler {
75
+ blocking: false
76
+ onHoveredChanged: {
77
+ if (hovered)
78
+ root.hoverEntered()
79
+ else
80
+ root.hoverExited()
81
+ }
82
+ }
83
+ }