tradedangerous 11.5.3__py3-none-any.whl → 12.0.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.
Potentially problematic release.
This version of tradedangerous might be problematic. Click here for more details.
- tradedangerous/cache.py +567 -395
- tradedangerous/cli.py +2 -2
- tradedangerous/commands/TEMPLATE.py +25 -26
- tradedangerous/commands/__init__.py +8 -16
- tradedangerous/commands/buildcache_cmd.py +40 -10
- tradedangerous/commands/buy_cmd.py +57 -46
- tradedangerous/commands/commandenv.py +0 -2
- tradedangerous/commands/export_cmd.py +78 -50
- tradedangerous/commands/import_cmd.py +67 -31
- tradedangerous/commands/market_cmd.py +52 -19
- tradedangerous/commands/olddata_cmd.py +120 -107
- tradedangerous/commands/rares_cmd.py +122 -110
- tradedangerous/commands/run_cmd.py +118 -66
- tradedangerous/commands/sell_cmd.py +52 -45
- tradedangerous/commands/shipvendor_cmd.py +49 -234
- tradedangerous/commands/station_cmd.py +55 -485
- tradedangerous/commands/update_cmd.py +56 -420
- tradedangerous/csvexport.py +173 -162
- tradedangerous/gui.py +2 -2
- tradedangerous/plugins/eddblink_plug.py +387 -251
- tradedangerous/plugins/spansh_plug.py +2488 -821
- tradedangerous/prices.py +124 -142
- tradedangerous/templates/TradeDangerous.sql +6 -6
- tradedangerous/tradecalc.py +1227 -1109
- tradedangerous/tradedb.py +533 -384
- tradedangerous/tradeenv.py +12 -1
- tradedangerous/version.py +1 -1
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info}/METADATA +6 -4
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info}/RECORD +33 -38
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info}/WHEEL +1 -1
- tradedangerous/commands/update_gui.py +0 -721
- tradedangerous/jsonprices.py +0 -254
- tradedangerous/plugins/edapi_plug.py +0 -1071
- tradedangerous/plugins/journal_plug.py +0 -537
- tradedangerous/plugins/netlog_plug.py +0 -316
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info}/entry_points.txt +0 -0
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info/licenses}/LICENSE +0 -0
- {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,721 +0,0 @@
|
|
|
1
|
-
# Deprecated
|
|
2
|
-
import tkinter as tk
|
|
3
|
-
import tkinter.messagebox as mbox
|
|
4
|
-
import sqlite3
|
|
5
|
-
import re
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
This is a crude attempt at a GUI for updating trade prices.
|
|
9
|
-
It needs a lot of love to be less... Shitty.
|
|
10
|
-
|
|
11
|
-
TODO:
|
|
12
|
-
. Add a way to add a new line,
|
|
13
|
-
. Add a way to reorder lines,
|
|
14
|
-
. Add a Save button,
|
|
15
|
-
. Add pre-exit validation (and don't exit if there's a problem),
|
|
16
|
-
|
|
17
|
-
Thanks to Bryan Okler for solving the focus tracking issue:
|
|
18
|
-
http://stackoverflow.com/questions/27055936/python3-tk-scrollbar-and-focus
|
|
19
|
-
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
updateUiHelp = (
|
|
23
|
-
"ABOUT THE EDITOR:\n"
|
|
24
|
-
"The columns shown here represent the columns in the "
|
|
25
|
-
"in-game Commodites Market UI and are in the same order.\n"
|
|
26
|
-
"\n"
|
|
27
|
-
"WHAT TO ENTER:\n"
|
|
28
|
-
"Leave items blank if they are NOT listed in the game UI.\n"
|
|
29
|
-
"\n"
|
|
30
|
-
"'Demand' is disabled unless you use '--use-demand'.\n"
|
|
31
|
-
"\n"
|
|
32
|
-
"'Supply' should be:\n"
|
|
33
|
-
" '-' or '0' if no units are available,\n"
|
|
34
|
-
" empty or '?' if the level is unknown,\n"
|
|
35
|
-
" or the amount of supply followed by L, M or H.\n"
|
|
36
|
-
"E.g.\n"
|
|
37
|
-
" 1L, 50M, 3000H.\n"
|
|
38
|
-
"\n"
|
|
39
|
-
"SAVING:\n"
|
|
40
|
-
"To save your data, click one of the window's close buttons.\n"
|
|
41
|
-
"\n"
|
|
42
|
-
"NAVIGATION:\n"
|
|
43
|
-
"- Use Tab, Shift-Tab, Up/Down Arrow and Enter to navigate.\n"
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
class Item:
|
|
47
|
-
""" Describe a listed, tradeable item """
|
|
48
|
-
|
|
49
|
-
def __init__(self, ID, catID, name, displayNo):
|
|
50
|
-
self.ID, self.catID, self.name = ID, catID, name
|
|
51
|
-
self.displayNo = displayNo
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class ScrollingCanvas(tk.Frame):
|
|
55
|
-
"""
|
|
56
|
-
Tk.Canvas with scrollbar.
|
|
57
|
-
"""
|
|
58
|
-
|
|
59
|
-
def __init__(self, parent):
|
|
60
|
-
super().__init__(parent)
|
|
61
|
-
self.parent = parent
|
|
62
|
-
|
|
63
|
-
self.canvas = canvas = tk.Canvas(self, borderwidth=0)
|
|
64
|
-
canvas.grid(row=0, column=0)
|
|
65
|
-
canvas.grid_rowconfigure(0, weight=1)
|
|
66
|
-
canvas.grid_columnconfigure(0, weight=1)
|
|
67
|
-
|
|
68
|
-
vsb = tk.Scrollbar(parent,
|
|
69
|
-
orient=tk.VERTICAL,
|
|
70
|
-
command=canvas.yview)
|
|
71
|
-
vsb.grid(row=0, column=1)
|
|
72
|
-
vsb.pack(side="right", fill=tk.BOTH, expand=False)
|
|
73
|
-
|
|
74
|
-
canvas.configure(yscrollcommand=vsb.set)
|
|
75
|
-
canvas.configure(yscrollincrement=4)
|
|
76
|
-
|
|
77
|
-
self.interior = interior = tk.Frame(canvas)
|
|
78
|
-
canvas.create_window(0, 0, window=interior, anchor="nw", tags="interior")
|
|
79
|
-
canvas.bind("<Configure>", self.onConfigure)
|
|
80
|
-
interior.rowconfigure(0, weight=1)
|
|
81
|
-
interior.columnconfigure(0, weight=1)
|
|
82
|
-
interior.columnconfigure(1, weight=1)
|
|
83
|
-
interior.columnconfigure(2, weight=1)
|
|
84
|
-
interior.columnconfigure(3, weight=1)
|
|
85
|
-
interior.columnconfigure(4, weight=1)
|
|
86
|
-
|
|
87
|
-
canvas.grid(row=0, column=0)
|
|
88
|
-
canvas.pack(side="left", fill=tk.BOTH, expand=True)
|
|
89
|
-
|
|
90
|
-
self.bind_all("<MouseWheel>", self.onMouseWheel)
|
|
91
|
-
canvas.bind_all("<FocusIn>", self.scrollIntoView)
|
|
92
|
-
|
|
93
|
-
self.pack(side="left", fill=tk.BOTH, expand=True)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def onConfigure(self, event):
|
|
97
|
-
""" Handle resizing """
|
|
98
|
-
|
|
99
|
-
self.canvas.configure(scrollregion=self.interior.bbox("all"))
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def onMouseWheel(self, event):
|
|
103
|
-
""" Translate mouse wheel inputs to scroll bar motions """
|
|
104
|
-
|
|
105
|
-
self.canvas.yview_scroll(int(-1 * (event.delta/4)), "units")
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def scrollIntoView(self, event):
|
|
109
|
-
""" Scroll so that a given widget is in focus """
|
|
110
|
-
|
|
111
|
-
canvas = self.canvas
|
|
112
|
-
widget_top = event.widget.winfo_y()
|
|
113
|
-
widget_bottom = widget_top + event.widget.winfo_height()
|
|
114
|
-
canvas_top = canvas.canvasy(0)
|
|
115
|
-
canvas_bottom = canvas_top + canvas.winfo_height()
|
|
116
|
-
|
|
117
|
-
if widget_bottom >= canvas_bottom:
|
|
118
|
-
delta = int(canvas_bottom - widget_bottom)
|
|
119
|
-
canvas.yview_scroll(-delta, "units")
|
|
120
|
-
elif widget_top < canvas_top:
|
|
121
|
-
delta = int(widget_top - canvas_top)
|
|
122
|
-
canvas.yview_scroll(delta, "units")
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
class UpdateGUI(ScrollingCanvas):
|
|
126
|
-
"""
|
|
127
|
-
Implements a tk canvas which displays an editor
|
|
128
|
-
for TradeDangerous Price Updates
|
|
129
|
-
"""
|
|
130
|
-
|
|
131
|
-
def __init__(self, parent, tdb, tdenv):
|
|
132
|
-
super().__init__(parent)
|
|
133
|
-
|
|
134
|
-
width = tdenv.width or 640
|
|
135
|
-
height = tdenv.height or 640
|
|
136
|
-
sticky = 1 if tdenv.alwaysOnTop else 0
|
|
137
|
-
|
|
138
|
-
self.tdb = tdb
|
|
139
|
-
self.tdenv = tdenv
|
|
140
|
-
|
|
141
|
-
self.rowNo = 0
|
|
142
|
-
self.colNo = 0
|
|
143
|
-
self.items = {}
|
|
144
|
-
self.categories = []
|
|
145
|
-
self.itemList = []
|
|
146
|
-
self.itemDisplays = []
|
|
147
|
-
self.results = None
|
|
148
|
-
self.headings = []
|
|
149
|
-
|
|
150
|
-
self.createWidgets()
|
|
151
|
-
|
|
152
|
-
self.focusOn(0, 0)
|
|
153
|
-
|
|
154
|
-
parent.geometry("{}x{}{:+n}{:+n}".format(
|
|
155
|
-
width+16, height,
|
|
156
|
-
tdenv.windowX,
|
|
157
|
-
tdenv.windowY,
|
|
158
|
-
))
|
|
159
|
-
|
|
160
|
-
# Allow the window to be always-on-top
|
|
161
|
-
parent.wm_attributes("-topmost", sticky)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
def selectWidget(self, widget, newValue=None):
|
|
165
|
-
if newValue is not None:
|
|
166
|
-
widget.val.set(newValue)
|
|
167
|
-
widget.focus_set()
|
|
168
|
-
widget.selection_range(0, tk.END)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
def focusOn(self, displayNo, pos):
|
|
172
|
-
""" Set focus to a widget and select the text in it """
|
|
173
|
-
|
|
174
|
-
row = self.itemDisplays[displayNo]
|
|
175
|
-
widget = row[pos][0]
|
|
176
|
-
self.selectWidget(widget)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
def setValue(self, row, pos, value):
|
|
180
|
-
row[pos][1].set(value)
|
|
181
|
-
return value
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def getValue(self, row, pos):
|
|
185
|
-
return row[pos][1].get()
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def validateRow(self, row):
|
|
189
|
-
paying = row[0][1].get()
|
|
190
|
-
if not paying or paying == "0":
|
|
191
|
-
paying = ""
|
|
192
|
-
for col in row:
|
|
193
|
-
col[1].set("")
|
|
194
|
-
row[1][0].configure(state=tk.DISABLED)
|
|
195
|
-
row[2][0].configure(state=tk.DISABLED)
|
|
196
|
-
else:
|
|
197
|
-
row[1][0].configure(state=tk.NORMAL)
|
|
198
|
-
if self.tdenv.useDemand:
|
|
199
|
-
row[2][0].configure(state=tk.NORMAL)
|
|
200
|
-
else:
|
|
201
|
-
row[2][0].configure(state=tk.DISABLED)
|
|
202
|
-
row[2][1].set("")
|
|
203
|
-
|
|
204
|
-
asking = row[1][1].get()
|
|
205
|
-
if asking == "0":
|
|
206
|
-
row[1][1].set("")
|
|
207
|
-
asking = ""
|
|
208
|
-
if not asking:
|
|
209
|
-
row[3][0].configure(state=tk.DISABLED)
|
|
210
|
-
row[3][1].set("")
|
|
211
|
-
else:
|
|
212
|
-
row[3][0].configure(state=tk.NORMAL)
|
|
213
|
-
if not row[3][1].get():
|
|
214
|
-
row[3][1].set("?")
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def checkValueAgainstStats(self, widget, stats):
|
|
218
|
-
widget.configure(bg = 'white')
|
|
219
|
-
minCr, avgCr, maxCr = stats
|
|
220
|
-
if not avgCr:
|
|
221
|
-
return True
|
|
222
|
-
|
|
223
|
-
cr = int(widget.val.get())
|
|
224
|
-
|
|
225
|
-
if cr < int(minCr / 1.01) and cr < int(avgCr * 0.7):
|
|
226
|
-
widget.bell()
|
|
227
|
-
ok = mbox.askokcancel(
|
|
228
|
-
"Very low price",
|
|
229
|
-
"The price you entered is very low.\n"
|
|
230
|
-
"\n"
|
|
231
|
-
"Your input was: {}\n"
|
|
232
|
-
"Previous low..: {}\n"
|
|
233
|
-
"\n"
|
|
234
|
-
"Is it correct?".format(
|
|
235
|
-
cr, minCr,
|
|
236
|
-
))
|
|
237
|
-
if not ok:
|
|
238
|
-
self.selectWidget(widget, "")
|
|
239
|
-
return False
|
|
240
|
-
widget.configure(bg = '#ff8080')
|
|
241
|
-
|
|
242
|
-
if cr >= (maxCr * 1.01) and int(cr * 0.7) > avgCr:
|
|
243
|
-
widget.bell()
|
|
244
|
-
ok = mbox.askokcancel(
|
|
245
|
-
"Very high price",
|
|
246
|
-
"The price you entered is very high.\n"
|
|
247
|
-
"\n"
|
|
248
|
-
"Your input was..: {}\n"
|
|
249
|
-
"Previous highest: {}\n"
|
|
250
|
-
"\n"
|
|
251
|
-
"Is it correct?".format(
|
|
252
|
-
cr, maxCr,
|
|
253
|
-
))
|
|
254
|
-
if not ok:
|
|
255
|
-
self.selectWidget(widget, "")
|
|
256
|
-
return False
|
|
257
|
-
widget.configure(bg = '#8080ff')
|
|
258
|
-
|
|
259
|
-
return True
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def validate(self, widget):
|
|
263
|
-
""" For checking the contents of a widget. """
|
|
264
|
-
item, row, pos = widget.item, widget.row, widget.pos
|
|
265
|
-
|
|
266
|
-
value = widget.val.get()
|
|
267
|
-
re.sub(" +", "", value)
|
|
268
|
-
re.sub(",", "", value)
|
|
269
|
-
widget.val.set(value)
|
|
270
|
-
|
|
271
|
-
self.validateRow(row)
|
|
272
|
-
|
|
273
|
-
if pos == 0:
|
|
274
|
-
if not value or value == "0":
|
|
275
|
-
widget.val.set("")
|
|
276
|
-
return True
|
|
277
|
-
|
|
278
|
-
if not re.match(r'^\d+$', value):
|
|
279
|
-
mbox.showerror(
|
|
280
|
-
"Invalid Paying price",
|
|
281
|
-
"Price must be an numeric value (e.g. 1234)"
|
|
282
|
-
)
|
|
283
|
-
return False
|
|
284
|
-
|
|
285
|
-
# is it lower than the value?
|
|
286
|
-
demandStats = self.demandStats[item.ID]
|
|
287
|
-
if not self.checkValueAgainstStats(widget, demandStats):
|
|
288
|
-
return False
|
|
289
|
-
|
|
290
|
-
if pos <= 1:
|
|
291
|
-
if not value or value == "0":
|
|
292
|
-
widget.val.set("")
|
|
293
|
-
return True
|
|
294
|
-
|
|
295
|
-
if not re.match(r'^\d+$', value):
|
|
296
|
-
mbox.showerror(
|
|
297
|
-
"Invalid Paying price",
|
|
298
|
-
"Price must be an numeric value (e.g. 1234)"
|
|
299
|
-
)
|
|
300
|
-
return False
|
|
301
|
-
|
|
302
|
-
if pos == 1:
|
|
303
|
-
# Don't allow crazy difference between prices
|
|
304
|
-
paying = int(row[0][1].get())
|
|
305
|
-
asking = int(row[1][1].get())
|
|
306
|
-
|
|
307
|
-
if asking < paying:
|
|
308
|
-
widget.bell()
|
|
309
|
-
mbox.showerror(
|
|
310
|
-
"I DOUBT THAT!",
|
|
311
|
-
"Stations never pay more for an item than "
|
|
312
|
-
"they sell it for.",
|
|
313
|
-
)
|
|
314
|
-
return False
|
|
315
|
-
|
|
316
|
-
supplyStats = self.supplyStats[item.ID]
|
|
317
|
-
if not self.checkValueAgainstStats(widget, supplyStats):
|
|
318
|
-
return False
|
|
319
|
-
|
|
320
|
-
# https://forums.frontier.co.uk/showthread.php?t=34986&p=1162429&viewfull=1#post1162429
|
|
321
|
-
# It seems that stations usually pay within 25% of the
|
|
322
|
-
# asking price as a buy-back price. If the user gives
|
|
323
|
-
# us something out of those bounds, check with them.
|
|
324
|
-
if paying < int(asking * 0.70) or \
|
|
325
|
-
paying < asking - 250:
|
|
326
|
-
widget.bell()
|
|
327
|
-
ok = mbox.askokcancel(
|
|
328
|
-
"Are you sure about that?",
|
|
329
|
-
"You've indicated that the station is:\n"
|
|
330
|
-
" PAYING: {:>10n}cr\n"
|
|
331
|
-
" ASKING: {:>10n}cr\n"
|
|
332
|
-
"for {}.\n"
|
|
333
|
-
"\n"
|
|
334
|
-
"This is outside of expected tolerances, "
|
|
335
|
-
"please check the numbers.\n"
|
|
336
|
-
"\n"
|
|
337
|
-
"If the numbers are correct, click OK and "
|
|
338
|
-
"please post a screenshot of the market UI "
|
|
339
|
-
"to the TD thread "
|
|
340
|
-
"(http://kfs.org/td/thread)."
|
|
341
|
-
.format(
|
|
342
|
-
paying, asking,
|
|
343
|
-
widget.item.name
|
|
344
|
-
),
|
|
345
|
-
default=mbox.CANCEL,
|
|
346
|
-
parent=widget,
|
|
347
|
-
)
|
|
348
|
-
if not ok:
|
|
349
|
-
self.selectWidget(widget, "")
|
|
350
|
-
return False
|
|
351
|
-
|
|
352
|
-
return True
|
|
353
|
-
|
|
354
|
-
if pos == 3:
|
|
355
|
-
value = widget.val.get()
|
|
356
|
-
if value == "0":
|
|
357
|
-
value = "-"
|
|
358
|
-
if value == "":
|
|
359
|
-
value = "?"
|
|
360
|
-
re.sub(",", "", value)
|
|
361
|
-
widget.val.set(value)
|
|
362
|
-
if re.match(r'^(-|\?|\d+[LlMmHh\?])$', value):
|
|
363
|
-
return True
|
|
364
|
-
mbox.showerror(
|
|
365
|
-
"Invalid supply value",
|
|
366
|
-
"If the item is in-supply, this field should "
|
|
367
|
-
"be the number of units followed by one of 'L', "
|
|
368
|
-
"'M' or 'H'.\n"
|
|
369
|
-
"\n"
|
|
370
|
-
"If the item is out-of-supply, use '-' or '0'.\n"
|
|
371
|
-
"\n"
|
|
372
|
-
"EXAMPLE: If the UI says '2341 LOW', type '2341L'.\n"
|
|
373
|
-
)
|
|
374
|
-
widget.val.set("?")
|
|
375
|
-
self.selectWidget(widget)
|
|
376
|
-
return False
|
|
377
|
-
|
|
378
|
-
return True
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
def onShiftTab(self, event):
|
|
382
|
-
""" Process user pressing Shift+TAB """
|
|
383
|
-
|
|
384
|
-
widget = event.widget
|
|
385
|
-
if not self.validate(widget):
|
|
386
|
-
return "break"
|
|
387
|
-
if widget.pos > 0 or widget.item.displayNo > 0:
|
|
388
|
-
# Natural flow
|
|
389
|
-
return
|
|
390
|
-
|
|
391
|
-
self.parent.bell()
|
|
392
|
-
return "break"
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
def onTab(self, event):
|
|
396
|
-
""" Process user pressing TAB """
|
|
397
|
-
|
|
398
|
-
widget = event.widget
|
|
399
|
-
if not self.validate(widget):
|
|
400
|
-
return "break"
|
|
401
|
-
if widget.pos + 1 < len(widget.row):
|
|
402
|
-
return
|
|
403
|
-
if widget.item.displayNo + 1 < len(self.itemDisplays):
|
|
404
|
-
return
|
|
405
|
-
self.parent.bell()
|
|
406
|
-
return "break"
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
def onReturn(self, event):
|
|
410
|
-
"""
|
|
411
|
-
When the user hits <Return>, advance to
|
|
412
|
-
the first cell on the next line, or if
|
|
413
|
-
we are at the bottom of the list, beep.
|
|
414
|
-
"""
|
|
415
|
-
|
|
416
|
-
widget = event.widget
|
|
417
|
-
if not self.validate(widget):
|
|
418
|
-
return "break"
|
|
419
|
-
# advance to the first entry on the next row
|
|
420
|
-
newDisplayNo = widget.item.displayNo + 1
|
|
421
|
-
if newDisplayNo >= len(self.itemDisplays):
|
|
422
|
-
self.parent.bell()
|
|
423
|
-
return "break"
|
|
424
|
-
|
|
425
|
-
self.focusOn(newDisplayNo, 0)
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
def onUp(self, event):
|
|
429
|
-
""" Handle the user pressing up, go up a row """
|
|
430
|
-
|
|
431
|
-
widget = event.widget
|
|
432
|
-
if not self.validate(widget):
|
|
433
|
-
return "break"
|
|
434
|
-
# can we go up a line?
|
|
435
|
-
if widget.item.displayNo <= 0:
|
|
436
|
-
self.parent.bell()
|
|
437
|
-
return "break"
|
|
438
|
-
|
|
439
|
-
self.focusOn(widget.item.displayNo - 1, widget.pos)
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
def onDown(self, event):
|
|
443
|
-
""" Handle the user pressing down, go down a row """
|
|
444
|
-
|
|
445
|
-
widget = event.widget
|
|
446
|
-
if not self.validate(widget):
|
|
447
|
-
return "break"
|
|
448
|
-
# can we go up a line?
|
|
449
|
-
newDisplayNo = widget.item.displayNo + 1
|
|
450
|
-
if newDisplayNo >= len(self.itemDisplays):
|
|
451
|
-
self.parent.bell()
|
|
452
|
-
return "break"
|
|
453
|
-
|
|
454
|
-
self.focusOn(newDisplayNo, widget.pos)
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
def endRow(self):
|
|
458
|
-
self.rowNo += 1
|
|
459
|
-
self.colNo = 0
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
def addWidget(self, widget, **kwargs):
|
|
463
|
-
widget.grid(row=self.rowNo, column=self.colNo, **kwargs)
|
|
464
|
-
self.colNo += 1
|
|
465
|
-
return widget
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
def addLabel(self, text):
|
|
469
|
-
lab = tk.Label(self.interior, text=text)
|
|
470
|
-
return self.addWidget(lab, sticky='W', padx=16)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
def addSection(self, text):
|
|
474
|
-
widget = tk.Label(self.interior, text=text, fg='blue')
|
|
475
|
-
widget.grid(row=self.rowNo, column=0, columnspan=5, sticky='W')
|
|
476
|
-
self.endRow()
|
|
477
|
-
return widget
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
def addInput(self, item, defValue, row):
|
|
481
|
-
pos = len(row)
|
|
482
|
-
|
|
483
|
-
inputVal = tk.StringVar()
|
|
484
|
-
inputVal.set(str(defValue))
|
|
485
|
-
|
|
486
|
-
inputEl = tk.Entry(self.interior,
|
|
487
|
-
width=10,
|
|
488
|
-
justify=tk.RIGHT,
|
|
489
|
-
textvariable=inputVal)
|
|
490
|
-
inputEl.item = item
|
|
491
|
-
inputEl.row = row
|
|
492
|
-
inputEl.pos = pos
|
|
493
|
-
inputEl.val = inputVal
|
|
494
|
-
|
|
495
|
-
inputEl.bind('<Shift-Key-Tab>', self.onShiftTab)
|
|
496
|
-
inputEl.bind('<Key-Tab>', self.onTab)
|
|
497
|
-
inputEl.bind('<Key-Return>', self.onReturn)
|
|
498
|
-
inputEl.bind('<Key-Down>', self.onDown)
|
|
499
|
-
inputEl.bind('<Key-Up>', self.onUp)
|
|
500
|
-
inputEl.bind('<FocusOut>', lambda evt: self.validateRow(evt.widget.row))
|
|
501
|
-
self.addWidget(inputEl, sticky='E')
|
|
502
|
-
row.append((inputEl, inputVal))
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
def addHeadings(self):
|
|
506
|
-
def addHeading(text):
|
|
507
|
-
self.headings.append(text)
|
|
508
|
-
lab = tk.Label(self.interior, text=text, fg='blue')
|
|
509
|
-
self.addWidget(lab, sticky='W', padx=16)
|
|
510
|
-
addHeading("Item")
|
|
511
|
-
addHeading("Paying")
|
|
512
|
-
addHeading("Asking")
|
|
513
|
-
addHeading("Demand")
|
|
514
|
-
addHeading("Supply")
|
|
515
|
-
addHeading("AvgPay")
|
|
516
|
-
addHeading("AvgAsk")
|
|
517
|
-
self.endRow()
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
def addItemRow(self, ID, catID, itemName, paying, asking, demand, supply):
|
|
521
|
-
row = []
|
|
522
|
-
|
|
523
|
-
displayNo = len(self.itemDisplays)
|
|
524
|
-
item = Item(ID, catID, itemName, displayNo)
|
|
525
|
-
self.items[itemName] = item
|
|
526
|
-
self.itemList.append(item)
|
|
527
|
-
|
|
528
|
-
demandStats = self.demandStats[item.ID]
|
|
529
|
-
supplyStats = self.supplyStats[item.ID]
|
|
530
|
-
|
|
531
|
-
self.addLabel(item.name.upper())
|
|
532
|
-
self.addInput(item, paying, row)
|
|
533
|
-
self.addInput(item, asking, row)
|
|
534
|
-
self.addInput(item, demand, row)
|
|
535
|
-
self.addInput(item, supply, row)
|
|
536
|
-
self.addLabel(demandStats[1])
|
|
537
|
-
self.addLabel(supplyStats[1])
|
|
538
|
-
|
|
539
|
-
self.itemDisplays.append(row)
|
|
540
|
-
|
|
541
|
-
self.endRow()
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
def createWidgets(self):
|
|
545
|
-
self.addHeadings()
|
|
546
|
-
|
|
547
|
-
tdb, tdenv = self.tdb, self.tdenv
|
|
548
|
-
station = tdenv.startStation
|
|
549
|
-
self.parent.title(station.name())
|
|
550
|
-
|
|
551
|
-
db = tdb.getDB()
|
|
552
|
-
db.row_factory = sqlite3.Row
|
|
553
|
-
cur = db.cursor()
|
|
554
|
-
|
|
555
|
-
self.categories = self.tdb.categoryByID
|
|
556
|
-
|
|
557
|
-
# Do we have entries for this station?
|
|
558
|
-
cur.execute("""
|
|
559
|
-
SELECT COUNT(*)
|
|
560
|
-
FROM StationItem
|
|
561
|
-
WHERE station_id = ?
|
|
562
|
-
""", [station.ID])
|
|
563
|
-
self.newStation = False if int(cur.fetchone()[0]) else True
|
|
564
|
-
|
|
565
|
-
cur.execute("""
|
|
566
|
-
SELECT item.item_id,
|
|
567
|
-
IFNULL(MIN(demand_price), 0),
|
|
568
|
-
IFNULL(AVG(demand_price), 0),
|
|
569
|
-
IFNULL(MAX(demand_price), 0)
|
|
570
|
-
FROM Item
|
|
571
|
-
LEFT OUTER JOIN StationItem AS si
|
|
572
|
-
ON (
|
|
573
|
-
item.item_id = si.item_id
|
|
574
|
-
AND si.demand_price > 0
|
|
575
|
-
)
|
|
576
|
-
GROUP BY 1
|
|
577
|
-
""")
|
|
578
|
-
self.demandStats = {
|
|
579
|
-
ID: [ minCr, int(avgCr), maxCr ]
|
|
580
|
-
for ID, minCr, avgCr, maxCr in cur
|
|
581
|
-
}
|
|
582
|
-
cur.execute("""
|
|
583
|
-
SELECT item.item_id,
|
|
584
|
-
IFNULL(MIN(supply_price), 0),
|
|
585
|
-
IFNULL(AVG(supply_price), 0),
|
|
586
|
-
IFNULL(MAX(supply_price), 0)
|
|
587
|
-
FROM Item
|
|
588
|
-
LEFT OUTER JOIN StationItem AS si
|
|
589
|
-
ON (
|
|
590
|
-
item.item_id = si.item_id
|
|
591
|
-
AND si.supply_price > 0
|
|
592
|
-
)
|
|
593
|
-
GROUP BY 1
|
|
594
|
-
""")
|
|
595
|
-
self.supplyStats = {
|
|
596
|
-
ID: [ minCr, int(avgCr), maxCr ]
|
|
597
|
-
for ID, minCr, avgCr, maxCr in cur
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
if self.newStation and not tdenv.all:
|
|
601
|
-
def splashScreen():
|
|
602
|
-
mbox.showinfo(
|
|
603
|
-
"New Station!",
|
|
604
|
-
"There is currently no price data for this station "
|
|
605
|
-
"in the database!\n\n" + updateUiHelp,
|
|
606
|
-
parent=self,
|
|
607
|
-
)
|
|
608
|
-
self.itemDisplays[0][0][0].focus_set()
|
|
609
|
-
self.parent.after(750, splashScreen)
|
|
610
|
-
|
|
611
|
-
# if the user specified "--all", force listing of all items
|
|
612
|
-
fetchAll = (self.newStation or tdenv.all)
|
|
613
|
-
siJoin = "LEFT OUTER" if fetchAll else "INNER"
|
|
614
|
-
stmt = """
|
|
615
|
-
SELECT item.category_id AS catID,
|
|
616
|
-
item.item_id AS ID,
|
|
617
|
-
item.name AS name,
|
|
618
|
-
IFNULL(si.demand_price, '') AS paying,
|
|
619
|
-
IFNULL(si.supply_price, '') AS asking,
|
|
620
|
-
IFNULL(si.demand_units, 0) AS demandUnits,
|
|
621
|
-
IFNULL(si.demand_level, 0) AS demandLevel,
|
|
622
|
-
IFNULL(si.supply_units, 0) AS supplyUnits,
|
|
623
|
-
IFNULL(si.supply_level, 0) AS supplyLevel
|
|
624
|
-
FROM (
|
|
625
|
-
Category AS cat
|
|
626
|
-
INNER JOIN Item item
|
|
627
|
-
USING (category_id)
|
|
628
|
-
) {siJoin} JOIN
|
|
629
|
-
StationItem si ON (
|
|
630
|
-
si.item_id = item.item_id AND si.station_id = ?
|
|
631
|
-
)
|
|
632
|
-
ORDER BY cat.name, item.ui_order
|
|
633
|
-
""".format(siJoin=siJoin)
|
|
634
|
-
tdenv.DEBUG1("sql: {}; bind: {}", stmt, station.ID)
|
|
635
|
-
cur.execute(stmt, [station.ID])
|
|
636
|
-
|
|
637
|
-
def describeSupply(units, level):
|
|
638
|
-
if not level:
|
|
639
|
-
return ""
|
|
640
|
-
if units == -1 and level == -1:
|
|
641
|
-
return "?"
|
|
642
|
-
if level == -1:
|
|
643
|
-
return "{}?".format(units)
|
|
644
|
-
return "{}{}".format(units, ['L', 'M', 'H'][level-1])
|
|
645
|
-
|
|
646
|
-
lastCat = None
|
|
647
|
-
for row in cur:
|
|
648
|
-
cat = row["catID"]
|
|
649
|
-
if cat != lastCat:
|
|
650
|
-
self.addSection(self.categories[cat].name())
|
|
651
|
-
lastCat = cat
|
|
652
|
-
itemName = row["name"]
|
|
653
|
-
paying, asking = row["paying"], row["asking"]
|
|
654
|
-
demand = describeSupply(row["demandUnits"], row["demandLevel"])
|
|
655
|
-
supply = describeSupply(row["supplyUnits"], row["supplyLevel"])
|
|
656
|
-
self.addItemRow(row["ID"], cat, itemName, paying, asking, demand, supply)
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
for row in self.itemDisplays:
|
|
660
|
-
self.validateRow(row)
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
def getResults(self):
|
|
664
|
-
lastCat = None
|
|
665
|
-
|
|
666
|
-
txt = (
|
|
667
|
-
"# Generated by TDGUI\n"
|
|
668
|
-
"\n"
|
|
669
|
-
"@ {stn}\n".format(
|
|
670
|
-
stn=self.tdenv.startStation.name(),
|
|
671
|
-
)
|
|
672
|
-
)
|
|
673
|
-
for item in self.itemList:
|
|
674
|
-
if item.catID != lastCat:
|
|
675
|
-
lastCat = item.catID
|
|
676
|
-
txt += (" + {}\n".format(self.categories[lastCat].dbname))
|
|
677
|
-
|
|
678
|
-
row = self.itemDisplays[item.displayNo]
|
|
679
|
-
rowvals = [ val[1].get() for val in row ]
|
|
680
|
-
|
|
681
|
-
itemName = item.name
|
|
682
|
-
paying = int(rowvals[0] or 0)
|
|
683
|
-
asking = int(rowvals[1] or 0)
|
|
684
|
-
demand = rowvals[2]
|
|
685
|
-
supply = rowvals[3]
|
|
686
|
-
|
|
687
|
-
if not paying and not asking:
|
|
688
|
-
demand, supply = "-", "-"
|
|
689
|
-
else:
|
|
690
|
-
if paying and not demand:
|
|
691
|
-
demand = "?"
|
|
692
|
-
|
|
693
|
-
if asking == 0:
|
|
694
|
-
supply = "-"
|
|
695
|
-
elif not supply:
|
|
696
|
-
supply = "?"
|
|
697
|
-
elif re.match(r"^\d+$", supply):
|
|
698
|
-
if int(supply) != 0:
|
|
699
|
-
supply += '?'
|
|
700
|
-
|
|
701
|
-
txt += (" {item:<30s} "
|
|
702
|
-
"{paying:>10} "
|
|
703
|
-
"{asking:>10} "
|
|
704
|
-
"{demand:>10} "
|
|
705
|
-
"{supply:>10}\n".format(
|
|
706
|
-
item=itemName,
|
|
707
|
-
paying=paying,
|
|
708
|
-
asking=asking,
|
|
709
|
-
demand=demand,
|
|
710
|
-
supply=supply
|
|
711
|
-
))
|
|
712
|
-
self.results = txt
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
def render(tdb, tdenv, tmpPath):
|
|
716
|
-
parent = tk.Tk()
|
|
717
|
-
gui = UpdateGUI(parent, tdb, tdenv)
|
|
718
|
-
gui.mainloop()
|
|
719
|
-
gui.getResults()
|
|
720
|
-
with tmpPath.open("w", encoding="utf-8") as fh:
|
|
721
|
-
print(gui.results, file=fh)
|