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,316 @@
1
+ pragma ComponentBehavior: Bound
2
+
3
+ import QtQuick 2.15
4
+ import QtQuick.Controls 2.15
5
+ import QtQuick.Layouts 1.15
6
+
7
+ Item {
8
+ id: root
9
+ objectName: "backtestView"
10
+
11
+ required property var sessionState
12
+ required property bool narrow
13
+ required property var displayPreferences
14
+ readonly property var rawReport: root.sessionState ? root.sessionState.backtest : null
15
+ readonly property bool hasReport: root.rawReport !== null && root.rawReport !== undefined
16
+ readonly property var report: root.hasReport ? root.rawReport : ({
17
+ account_id: null,
18
+ account_screen_name: null,
19
+ draft_id: null,
20
+ set_code: "",
21
+ event_name: "",
22
+ chosen_pick_count: 0,
23
+ compared_count: 0,
24
+ match_count: 0,
25
+ skipped_count: 0,
26
+ ranking_mode: "score",
27
+ rows: []
28
+ })
29
+ readonly property string accountIdentity: {
30
+ if (root.report.account_screen_name && root.report.account_id)
31
+ return root.report.account_screen_name + " (" + root.report.account_id + ")"
32
+ return root.report.account_screen_name || root.report.account_id || "Account unavailable"
33
+ }
34
+ readonly property string draftIdentity: root.report.draft_id || "Draft unavailable"
35
+
36
+ ColumnLayout {
37
+ anchors.fill: parent
38
+ spacing: Theme.gutter
39
+
40
+ RowLayout {
41
+ Layout.fillWidth: true
42
+ ColumnLayout {
43
+ Layout.fillWidth: true
44
+ spacing: 2
45
+ Label { text: "Backtest report"; color: Theme.text; font.pixelSize: Theme.textPixelSize(22); font.bold: true }
46
+ Label {
47
+ objectName: "backtestSubtitle"
48
+ Layout.fillWidth: true
49
+ text: root.hasReport
50
+ ? root.accountIdentity + " · Draft " + root.draftIdentity + " · "
51
+ + (root.report.set_code || "Set unavailable") + " · "
52
+ + (root.report.event_name || "Event unavailable") + " · "
53
+ + root.report.chosen_pick_count + " recorded picks"
54
+ : "Compare persisted picks with the active ranking"
55
+ color: Theme.textMuted
56
+ wrapMode: Text.WordWrap
57
+ }
58
+ }
59
+ DimensionalButton {
60
+ objectName: "backtestRunButton"
61
+ text: root.hasReport ? "Run again" : "Run backtest"
62
+ Accessible.name: text
63
+ Accessible.description: "Requests a backtest through the shared live session."
64
+ onClicked: sessionProvider.requestBacktest()
65
+ }
66
+ }
67
+
68
+ StateBanner { Layout.fillWidth: true; sessionState: root.sessionState }
69
+
70
+ Rectangle {
71
+ visible: !root.hasReport
72
+ Layout.fillWidth: true
73
+ Layout.fillHeight: true
74
+ color: Theme.surfaceLow
75
+ border.color: Theme.outline
76
+ border.width: 1
77
+ radius: Theme.radius
78
+ ColumnLayout {
79
+ anchors.centerIn: parent
80
+ width: Math.min(parent.width - 40, 440)
81
+ spacing: 10
82
+ Label { text: "No backtest available"; color: Theme.text; font.pixelSize: Theme.textPixelSize(20); font.bold: true }
83
+ Label { Layout.fillWidth: true; text: "Complete or recover a draft to run a backtest. Published failures can be retried or dismissed above."; color: Theme.textMuted; wrapMode: Text.WordWrap }
84
+ }
85
+ }
86
+
87
+ GridLayout {
88
+ visible: root.hasReport
89
+ Layout.fillWidth: true
90
+ columns: root.narrow ? 2 : 4
91
+ columnSpacing: 8
92
+ rowSpacing: 8
93
+ Repeater {
94
+ model: [
95
+ { label: "COMPARED", value: root.report.compared_count },
96
+ { label: "MATCHED", value: root.report.match_count },
97
+ { label: "SKIPPED", value: root.report.skipped_count },
98
+ { label: "RANKING", value: root.report.ranking_mode === "score" ? "DO Score" : root.report.ranking_mode }
99
+ ]
100
+ delegate: Rectangle {
101
+ required property var modelData
102
+ Layout.fillWidth: true
103
+ Layout.preferredHeight: 74
104
+ color: Theme.surfaceLow
105
+ border.color: Theme.outline
106
+ border.width: 1
107
+ radius: Theme.radius
108
+ Accessible.name: modelData.label + " " + modelData.value
109
+ ColumnLayout {
110
+ anchors.centerIn: parent
111
+ spacing: 3
112
+ Label { Layout.alignment: Qt.AlignHCenter; text: modelData.value; color: modelData.label === "MATCHED" ? Theme.primary : Theme.text; font.family: fixedFontFamily; font.pixelSize: Theme.textPixelSize(root.narrow ? 16 : 20); font.bold: true }
113
+ Label { Layout.alignment: Qt.AlignHCenter; text: modelData.label; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(9); font.bold: true; font.letterSpacing: 0.8 }
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ Rectangle {
120
+ visible: root.hasReport && root.report.skipped_count > 0
121
+ Layout.fillWidth: true
122
+ Layout.preferredHeight: skippedExplanation.implicitHeight + 22
123
+ color: Theme.warningDark
124
+ border.color: Theme.warning
125
+ border.width: 1
126
+ radius: Theme.radius
127
+ Label {
128
+ id: skippedExplanation
129
+ anchors.fill: parent
130
+ anchors.margins: 11
131
+ text: root.report.skipped_count + " pick" + (root.report.skipped_count === 1 ? " was" : "s were") + " skipped because their recorded offered-card history is incomplete. Skipped picks do not affect the comparison."
132
+ color: Theme.warning
133
+ wrapMode: Text.WordWrap
134
+ Accessible.name: text
135
+ }
136
+ }
137
+
138
+ Rectangle {
139
+ visible: root.hasReport
140
+ Layout.fillWidth: true
141
+ Layout.fillHeight: true
142
+ color: Theme.surfaceLow
143
+ border.color: Theme.outline
144
+ border.width: 1
145
+ radius: Theme.radius
146
+ ColumnLayout {
147
+ anchors.fill: parent
148
+ anchors.margins: 12
149
+ spacing: 6
150
+ RowLayout {
151
+ visible: !root.narrow
152
+ Layout.fillWidth: true
153
+ Label { Layout.preferredWidth: 74; text: "PICK"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(10) }
154
+ Label { Layout.fillWidth: true; text: "RECOMMENDED"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(10) }
155
+ Label { Layout.fillWidth: true; text: "ACTUAL"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(10) }
156
+ Label { Layout.preferredWidth: 84; text: "RESULT"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(10) }
157
+ Label { Layout.preferredWidth: 70; text: "DO"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(10) }
158
+ Label {
159
+ objectName: "backtestWinRateHeader"
160
+ visible: root.displayPreferences.secondaryStats
161
+ Layout.preferredWidth: 70
162
+ text: "WR"
163
+ color: Theme.textMuted
164
+ font.pixelSize: Theme.textPixelSize(10)
165
+ }
166
+ Label {
167
+ objectName: "backtestSourceHeader"
168
+ visible: root.displayPreferences.secondaryStats
169
+ Layout.preferredWidth: 90
170
+ text: "SOURCE"
171
+ color: Theme.textMuted
172
+ font.pixelSize: Theme.textPixelSize(10)
173
+ }
174
+ }
175
+ ListView {
176
+ id: backtestRows
177
+ objectName: "backtestRows"
178
+ Layout.fillWidth: true
179
+ Layout.fillHeight: true
180
+ spacing: root.displayPreferences.compactDensity ? 3 : 5
181
+ clip: true
182
+ model: root.report.rows
183
+ Accessible.name: "Backtest pick comparisons"
184
+ delegate: Rectangle {
185
+ id: backtestRow
186
+ required property int index
187
+ required property var modelData
188
+ readonly property bool skipped: modelData.match === null || modelData.match === undefined
189
+ readonly property string resultText: {
190
+ if (backtestRow.skipped)
191
+ return "Skipped"
192
+ return modelData.match ? "Match" : "Different"
193
+ }
194
+ readonly property string accessibleResult: {
195
+ if (backtestRow.skipped)
196
+ return "skipped: " + modelData.skipped_reason
197
+ return modelData.match ? "match" : "different"
198
+ }
199
+ readonly property color resultColor: {
200
+ if (modelData.match)
201
+ return Theme.primary
202
+ return backtestRow.skipped ? Theme.warning : Theme.textMuted
203
+ }
204
+ readonly property string comparisonText: {
205
+ if (!modelData.recommended)
206
+ return modelData.skipped_reason
207
+ const actualName = modelData.actual ? modelData.actual.name : "—"
208
+ return modelData.recommended.name + " → " + actualName
209
+ }
210
+ width: ListView.view.width
211
+ height: root.narrow
212
+ ? Math.max(root.displayPreferences.secondaryStats ? 108 : 76,
213
+ narrowBacktestContent.implicitHeight
214
+ + narrowBacktestContent.anchors.topMargin * 2)
215
+ : 48
216
+ color: backtestRow.skipped ? Theme.warningDark : Theme.surface
217
+ border.color: Theme.outline
218
+ border.width: 1
219
+ radius: Theme.radius
220
+ Accessible.name: "Pick " + (modelData.pack_number + 1) + ", " + (modelData.pick_number + 1) + ", " + backtestRow.accessibleResult
221
+ RowLayout {
222
+ visible: !root.narrow
223
+ anchors.fill: parent
224
+ anchors.leftMargin: 10
225
+ anchors.rightMargin: 10
226
+ spacing: 10
227
+ Label { Layout.preferredWidth: 74; text: "P" + (modelData.pack_number + 1) + " · P" + (modelData.pick_number + 1); color: Theme.textMuted; font.family: fixedFontFamily }
228
+ Label {
229
+ objectName: "backtestRecommended" + index
230
+ Layout.fillWidth: true
231
+ text: modelData.recommended ? modelData.recommended.name : modelData.skipped_reason
232
+ color: Theme.text
233
+ elide: Text.ElideRight
234
+ }
235
+ Label {
236
+ objectName: "backtestActual" + index
237
+ Layout.fillWidth: true
238
+ text: modelData.actual ? modelData.actual.name : "—"
239
+ color: Theme.textMuted
240
+ elide: Text.ElideRight
241
+ }
242
+ Label {
243
+ objectName: "backtestResult" + index
244
+ Layout.preferredWidth: 84
245
+ text: backtestRow.resultText
246
+ color: backtestRow.resultColor
247
+ font.bold: true
248
+ }
249
+ Label {
250
+ objectName: "backtestScore" + index
251
+ Layout.preferredWidth: 70
252
+ text: modelData.recommended_score !== null && modelData.recommended_score !== undefined ? modelData.recommended_score : "—"
253
+ color: Theme.text
254
+ font.family: fixedFontFamily
255
+ }
256
+ Label {
257
+ objectName: "backtestWinRate" + index
258
+ visible: root.displayPreferences.secondaryStats
259
+ Layout.preferredWidth: 70
260
+ text: modelData.recommended_win_rate !== null && modelData.recommended_win_rate !== undefined
261
+ ? (modelData.recommended_win_rate * 100).toFixed(1) + "%" : "—"
262
+ color: Theme.textMuted
263
+ font.family: fixedFontFamily
264
+ }
265
+ Label {
266
+ objectName: "backtestSource" + index
267
+ visible: root.displayPreferences.secondaryStats
268
+ Layout.preferredWidth: 90
269
+ text: modelData.data_source || "—"
270
+ color: Theme.textMuted
271
+ elide: Text.ElideRight
272
+ }
273
+ }
274
+ ColumnLayout {
275
+ id: narrowBacktestContent
276
+ visible: root.narrow
277
+ anchors.left: parent.left
278
+ anchors.right: parent.right
279
+ anchors.top: parent.top
280
+ anchors.leftMargin: 9
281
+ anchors.rightMargin: 9
282
+ anchors.topMargin: 9
283
+ spacing: 2
284
+ RowLayout {
285
+ Layout.fillWidth: true
286
+ Label { text: "P" + (modelData.pack_number + 1) + " · P" + (modelData.pick_number + 1); color: Theme.textMuted; font.family: fixedFontFamily }
287
+ Label { Layout.fillWidth: true; text: backtestRow.resultText; color: modelData.match ? Theme.primary : Theme.warning; horizontalAlignment: Text.AlignRight }
288
+ }
289
+ Label { Layout.fillWidth: true; text: backtestRow.comparisonText; color: Theme.text; elide: Text.ElideRight }
290
+ Label {
291
+ objectName: "backtestNarrowScore" + index
292
+ Layout.fillWidth: true
293
+ text: "DO " + (modelData.recommended_score !== null && modelData.recommended_score !== undefined ? modelData.recommended_score : "—")
294
+ color: Theme.textMuted
295
+ font.pixelSize: Theme.textPixelSize(10)
296
+ }
297
+ GridLayout {
298
+ objectName: "backtestNarrowSecondary" + index
299
+ visible: root.displayPreferences.secondaryStats
300
+ Layout.fillWidth: true
301
+ columns: 2
302
+ columnSpacing: 8
303
+ rowSpacing: 1
304
+ Label { text: "WR " + (modelData.recommended_win_rate !== null && modelData.recommended_win_rate !== undefined ? (modelData.recommended_win_rate * 100).toFixed(1) + "%" : "—"); color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(10) }
305
+ Label { text: "Pool " + (modelData.pool_size !== null && modelData.pool_size !== undefined ? modelData.pool_size : "—"); color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(10) }
306
+ Label { text: "Offered " + (modelData.offered_count !== null && modelData.offered_count !== undefined ? modelData.offered_count : "—"); color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(10) }
307
+ Label { Layout.columnSpan: 2; Layout.fillWidth: true; text: modelData.data_source || "History unavailable"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(10); elide: Text.ElideRight }
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+ }
314
+ Label { visible: root.hasReport; Layout.fillWidth: true; text: "This is an analytical comparison, not a player grade."; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11); wrapMode: Text.WordWrap }
315
+ }
316
+ }