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
|
@@ -92,39 +92,7 @@ def run(results, cmdenv, tdb):
|
|
|
92
92
|
"""
|
|
93
93
|
Fetch all the data needed to display the results of a "rares"
|
|
94
94
|
command. Does not actually print anything.
|
|
95
|
-
|
|
96
|
-
Command execution is broken into two steps:
|
|
97
|
-
1. cmd.run(results, cmdenv, tdb)
|
|
98
|
-
Gather all the data required but generate no output,
|
|
99
|
-
2. cmd.render(results, cmdenv, tdb)
|
|
100
|
-
Print output to the user.
|
|
101
|
-
|
|
102
|
-
This separation of concerns allows modularity; you can write
|
|
103
|
-
a command that calls another command to fetch data for you
|
|
104
|
-
and knowing it doesn't generate any output. Then you can
|
|
105
|
-
process the data and return it and let the command parser
|
|
106
|
-
decide when to turn it into output.
|
|
107
|
-
|
|
108
|
-
It also opens a future door to commands that can present
|
|
109
|
-
their data in a GUI as well as the command line by having
|
|
110
|
-
a custom render() function.
|
|
111
|
-
|
|
112
|
-
Parameters:
|
|
113
|
-
results
|
|
114
|
-
An object to be populated and returned
|
|
115
|
-
cmdenv
|
|
116
|
-
A CommandEnv object populated with the parameters
|
|
117
|
-
for the command.
|
|
118
|
-
tdb
|
|
119
|
-
A TradeDB object to query against.
|
|
120
|
-
|
|
121
|
-
Returns:
|
|
122
|
-
None
|
|
123
|
-
End execution without any output
|
|
124
|
-
results
|
|
125
|
-
Proceed to "render" with the output.
|
|
126
95
|
"""
|
|
127
|
-
|
|
128
96
|
# Lookup the system we're currently in.
|
|
129
97
|
start = cmdenv.nearSystem
|
|
130
98
|
# Hoist the padSize, noPlanet and planetary parameter for convenience
|
|
@@ -134,140 +102,184 @@ def run(results, cmdenv, tdb):
|
|
|
134
102
|
fleet = cmdenv.fleet
|
|
135
103
|
odyssey = cmdenv.odyssey
|
|
136
104
|
# How far we're want to cast our net.
|
|
137
|
-
maxLy = float(cmdenv.maxLyPer or 0.)
|
|
138
|
-
|
|
105
|
+
maxLy = float(cmdenv.maxLyPer or 0.0)
|
|
106
|
+
|
|
139
107
|
if cmdenv.illegal:
|
|
140
108
|
wantIllegality = 'Y'
|
|
141
109
|
elif cmdenv.legal:
|
|
142
110
|
wantIllegality = 'N'
|
|
143
111
|
else:
|
|
144
112
|
wantIllegality = 'YN?'
|
|
145
|
-
|
|
113
|
+
|
|
146
114
|
awaySystems = set()
|
|
147
115
|
if cmdenv.away or cmdenv.awayFrom:
|
|
148
116
|
if not cmdenv.away or not cmdenv.awayFrom:
|
|
149
|
-
raise CommandLineError(
|
|
150
|
-
"Invalid --away/--from usage. See --help"
|
|
151
|
-
)
|
|
117
|
+
raise CommandLineError("Invalid --away/--from usage. See --help")
|
|
152
118
|
minAwayDist = cmdenv.away
|
|
153
119
|
for sysName in cmdenv.awayFrom:
|
|
154
120
|
system = tdb.lookupPlace(sysName).system
|
|
155
121
|
awaySystems.add(system)
|
|
156
|
-
|
|
122
|
+
|
|
157
123
|
# Start to build up the results data.
|
|
158
124
|
results.summary = ResultRow()
|
|
159
125
|
results.summary.near = start
|
|
160
126
|
results.summary.ly = maxLy
|
|
161
127
|
results.summary.awaySystems = awaySystems
|
|
162
|
-
|
|
128
|
+
|
|
163
129
|
distCheckFn = start.distanceTo
|
|
164
|
-
|
|
130
|
+
|
|
165
131
|
# Look through the rares list.
|
|
166
132
|
for rare in tdb.rareItemByID.values():
|
|
167
133
|
if rare.illegal not in wantIllegality:
|
|
168
134
|
continue
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if planetary
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if fleet: # do we care about fleet carrier?
|
|
176
|
-
if not rare.station.checkFleet(fleet):
|
|
177
|
-
continue
|
|
178
|
-
if odyssey: # do we care about Odyssey?
|
|
179
|
-
if not rare.station.checkOdyssey(odyssey):
|
|
180
|
-
continue
|
|
181
|
-
if noPlanet and rare.station.planetary != 'N':
|
|
135
|
+
stn = rare.station
|
|
136
|
+
if padSize and not stn.checkPadSize(padSize):
|
|
137
|
+
continue
|
|
138
|
+
if planetary and not stn.checkPlanetary(planetary):
|
|
139
|
+
continue
|
|
140
|
+
if fleet and not stn.checkFleet(fleet):
|
|
182
141
|
continue
|
|
183
|
-
|
|
184
|
-
|
|
142
|
+
if odyssey and not stn.checkOdyssey(odyssey):
|
|
143
|
+
continue
|
|
144
|
+
if noPlanet and stn.planetary != 'N':
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
rareSys = stn.system
|
|
185
148
|
dist = distCheckFn(rareSys)
|
|
186
|
-
if maxLy > 0. and dist > maxLy:
|
|
149
|
+
if maxLy > 0.0 and dist > maxLy:
|
|
187
150
|
continue
|
|
188
|
-
|
|
151
|
+
|
|
189
152
|
if awaySystems:
|
|
190
153
|
awayCheck = rareSys.distanceTo
|
|
191
154
|
if any(awayCheck(away) < minAwayDist for away in awaySystems):
|
|
192
155
|
continue
|
|
193
|
-
|
|
194
|
-
# Create a row for this item
|
|
156
|
+
|
|
195
157
|
row = ResultRow()
|
|
196
158
|
row.rare = rare
|
|
159
|
+
row.station = stn # <-- IMPORTANT: used by render()
|
|
197
160
|
row.dist = dist
|
|
198
161
|
results.rows.append(row)
|
|
199
|
-
|
|
162
|
+
|
|
200
163
|
# Was anything matched?
|
|
201
|
-
if not results:
|
|
164
|
+
if not results.rows:
|
|
202
165
|
print("No matches found.")
|
|
203
166
|
return None
|
|
204
|
-
|
|
167
|
+
|
|
168
|
+
# Sort safely even if rare.costCr is None (treat None as 0)
|
|
169
|
+
price_key = lambda row: (row.rare.costCr or 0)
|
|
170
|
+
|
|
205
171
|
if cmdenv.sortByPrice:
|
|
206
172
|
results.rows.sort(key=lambda row: row.dist)
|
|
207
|
-
results.rows.sort(key=
|
|
173
|
+
results.rows.sort(key=price_key, reverse=True)
|
|
208
174
|
else:
|
|
209
|
-
results.rows.sort(key=
|
|
175
|
+
results.rows.sort(key=price_key, reverse=True)
|
|
210
176
|
results.rows.sort(key=lambda row: row.dist)
|
|
211
|
-
|
|
177
|
+
|
|
212
178
|
if cmdenv.reverse:
|
|
213
179
|
results.rows.reverse()
|
|
214
|
-
|
|
180
|
+
|
|
215
181
|
limit = cmdenv.limit or 0
|
|
216
182
|
if limit > 0:
|
|
217
183
|
results.rows = results.rows[:limit]
|
|
218
|
-
|
|
184
|
+
|
|
219
185
|
return results
|
|
220
186
|
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
221
190
|
#######################################################################
|
|
222
191
|
## Transform result set into output
|
|
223
192
|
|
|
224
193
|
def render(results, cmdenv, tdb):
|
|
225
194
|
"""
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
the output of the command.
|
|
195
|
+
Render output for 'rares' with robust None-handling.
|
|
196
|
+
Keeps existing column order/labels.
|
|
229
197
|
"""
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
198
|
+
from ..formatting import RowFormat, max_len
|
|
199
|
+
|
|
200
|
+
rows = results.rows
|
|
201
|
+
if not rows:
|
|
202
|
+
return
|
|
203
|
+
|
|
204
|
+
# Helpers to coalesce possibly-missing attributes
|
|
205
|
+
def _cost(row):
|
|
206
|
+
try:
|
|
207
|
+
v = row.rare.costCr
|
|
208
|
+
return int(v) if v is not None else 0
|
|
209
|
+
except Exception:
|
|
210
|
+
return 0
|
|
211
|
+
|
|
212
|
+
def _rare_name(row):
|
|
213
|
+
try:
|
|
214
|
+
n = row.rare.name()
|
|
215
|
+
return n or "?"
|
|
216
|
+
except Exception:
|
|
217
|
+
return "?"
|
|
218
|
+
|
|
219
|
+
def _alloc(row):
|
|
220
|
+
val = getattr(row.rare, "allocation", None)
|
|
221
|
+
return str(val) if val not in (None, "") else "?"
|
|
222
|
+
|
|
223
|
+
def _rare_illegal(row):
|
|
224
|
+
val = getattr(row.rare, "illegal", None)
|
|
225
|
+
return val if val in ("Y", "N", "?") else "?"
|
|
226
|
+
|
|
227
|
+
def _stn_ls(row):
|
|
228
|
+
try:
|
|
229
|
+
v = row.station.distFromStar()
|
|
230
|
+
return v if v is not None else "?"
|
|
231
|
+
except Exception:
|
|
232
|
+
return "?"
|
|
233
|
+
|
|
234
|
+
def _dist(row):
|
|
235
|
+
try:
|
|
236
|
+
return float(getattr(row, "dist", 0.0) or 0.0)
|
|
237
|
+
except Exception:
|
|
238
|
+
return 0.0
|
|
239
|
+
|
|
240
|
+
def _stn_bm(row):
|
|
241
|
+
key = getattr(row.station, "blackMarket", "?")
|
|
242
|
+
return TradeDB.marketStates.get(key, key or "?")
|
|
243
|
+
|
|
244
|
+
def _pad(row):
|
|
245
|
+
key = getattr(row.station, "maxPadSize", "?")
|
|
246
|
+
return TradeDB.padSizes.get(key, key or "?")
|
|
247
|
+
|
|
248
|
+
def _plt(row):
|
|
249
|
+
key = getattr(row.station, "planetary", "?")
|
|
250
|
+
return TradeDB.planetStates.get(key, key or "?")
|
|
251
|
+
|
|
252
|
+
def _flc(row):
|
|
253
|
+
key = getattr(row.station, "fleet", "?")
|
|
254
|
+
return TradeDB.fleetStates.get(key, key or "?")
|
|
255
|
+
|
|
256
|
+
def _ody(row):
|
|
257
|
+
key = getattr(row.station, "odyssey", "?")
|
|
258
|
+
return TradeDB.odysseyStates.get(key, key or "?")
|
|
259
|
+
|
|
260
|
+
# Column widths based on safe key functions
|
|
261
|
+
max_stn = max_len(rows, key=lambda r: r.station.name())
|
|
262
|
+
max_rare = max_len(rows, key=lambda r: _rare_name(r))
|
|
263
|
+
|
|
240
264
|
rowFmt = RowFormat()
|
|
241
|
-
rowFmt.addColumn('Station', '<',
|
|
242
|
-
|
|
243
|
-
rowFmt.addColumn('
|
|
244
|
-
|
|
245
|
-
rowFmt.addColumn('
|
|
246
|
-
|
|
247
|
-
rowFmt.addColumn('
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
rowFmt.addColumn('
|
|
252
|
-
|
|
253
|
-
rowFmt.addColumn(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
key=lambda row: TradeDB.marketStates[row.rare.station.blackMarket])
|
|
257
|
-
rowFmt.addColumn("Pad", '>', '3',
|
|
258
|
-
key=lambda row: TradeDB.padSizes[row.rare.station.maxPadSize])
|
|
259
|
-
rowFmt.addColumn("Plt", '>', '3',
|
|
260
|
-
key=lambda row: TradeDB.planetStates[row.rare.station.planetary])
|
|
261
|
-
rowFmt.addColumn("Flc", '>', '3',
|
|
262
|
-
key=lambda row: TradeDB.fleetStates[row.rare.station.fleet])
|
|
263
|
-
rowFmt.addColumn("Ody", '>', '3',
|
|
264
|
-
key=lambda row: TradeDB.odysseyStates[row.rare.station.odyssey])
|
|
265
|
-
|
|
266
|
-
# Print a heading summary if the user didn't use '-q'
|
|
265
|
+
rowFmt.addColumn('Station', '<', max_stn, key=lambda r: r.station.name())
|
|
266
|
+
rowFmt.addColumn('Rare', '<', max_rare, key=lambda r: _rare_name(r))
|
|
267
|
+
rowFmt.addColumn('Cost', '>', 10, 'n', key=lambda r: _cost(r))
|
|
268
|
+
rowFmt.addColumn('DistLy', '>', 6, '.2f', key=lambda r: _dist(r))
|
|
269
|
+
rowFmt.addColumn('Alloc', '>', 5, key=lambda r: _alloc(r))
|
|
270
|
+
# First B/mkt: rare legality flag (Y/N/?)
|
|
271
|
+
rowFmt.addColumn('B/mkt', '>', 4, key=lambda r: _rare_illegal(r))
|
|
272
|
+
rowFmt.addColumn('StnLs', '>', 10, key=lambda r: _stn_ls(r))
|
|
273
|
+
# Second B/mkt: station black market availability via mapping
|
|
274
|
+
rowFmt.addColumn('B/mkt', '>', 4, key=lambda r: _stn_bm(r))
|
|
275
|
+
rowFmt.addColumn('Pad', '>', 3, key=lambda r: _pad(r))
|
|
276
|
+
rowFmt.addColumn('Plt', '>', 3, key=lambda r: _plt(r))
|
|
277
|
+
rowFmt.addColumn('Flc', '>', 3, key=lambda r: _flc(r))
|
|
278
|
+
rowFmt.addColumn('Ody', '>', 3, key=lambda r: _ody(r))
|
|
279
|
+
|
|
267
280
|
if not cmdenv.quiet:
|
|
268
281
|
heading, underline = rowFmt.heading()
|
|
269
282
|
print(heading, underline, sep='\n')
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
for row in results.rows:
|
|
283
|
+
|
|
284
|
+
for row in rows:
|
|
273
285
|
print(rowFmt.format(row))
|
|
@@ -11,6 +11,7 @@ from ..tradecalc import TradeCalc, Route, NoHopsError
|
|
|
11
11
|
|
|
12
12
|
import math
|
|
13
13
|
import sys
|
|
14
|
+
import time
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
######################################################################
|
|
@@ -385,68 +386,77 @@ def expandForJumps(tdb, cmdenv, calc, origin, jumps, srcName, purpose):
|
|
|
385
386
|
Find all the stations you could reach if you made a given
|
|
386
387
|
number of jumps away from the origin list.
|
|
387
388
|
"""
|
|
388
|
-
|
|
389
|
+
|
|
389
390
|
assert jumps
|
|
390
|
-
|
|
391
|
+
|
|
391
392
|
maxLyPer = cmdenv.emptyLyPer or cmdenv.maxLyPer
|
|
392
393
|
avoidPlaces = cmdenv.avoidPlaces
|
|
393
394
|
cmdenv.DEBUG0(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
395
|
+
"expanding {} reach from {} by {} jumps at {}ly per jump",
|
|
396
|
+
srcName,
|
|
397
|
+
origin.name(),
|
|
398
|
+
jumps,
|
|
399
|
+
maxLyPer,
|
|
399
400
|
)
|
|
400
|
-
|
|
401
|
+
|
|
402
|
+
# Preserve original behavior: --to uses stationsSelling, --from uses stationsBuying
|
|
401
403
|
if srcName == "--to":
|
|
402
404
|
tradingList = calc.stationsSelling
|
|
403
405
|
elif srcName == "--from":
|
|
404
406
|
tradingList = calc.stationsBuying
|
|
405
407
|
else:
|
|
406
408
|
raise Exception("Unknown src")
|
|
407
|
-
|
|
409
|
+
|
|
410
|
+
# Ensure O(1) membership checks regardless of the underlying container type.
|
|
411
|
+
trading_ids = tradingList if isinstance(tradingList, set) else set(tradingList)
|
|
412
|
+
|
|
408
413
|
stations = set()
|
|
409
414
|
origins, avoid = set((origin,)), set(place for place in avoidPlaces)
|
|
410
|
-
|
|
415
|
+
|
|
411
416
|
for jump in range(jumps):
|
|
412
417
|
if not origins:
|
|
413
418
|
break
|
|
414
|
-
cmdenv
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
+
if getattr(cmdenv, "debug", False):
|
|
420
|
+
cmdenv.DEBUG1(
|
|
421
|
+
"Ring {}: {}",
|
|
422
|
+
jump,
|
|
423
|
+
[sys.dbname for sys in origins]
|
|
424
|
+
)
|
|
419
425
|
thisJump, origins = origins, set()
|
|
420
426
|
for system in thisJump:
|
|
421
427
|
avoid.add(system)
|
|
422
428
|
for stn in system.stations or ():
|
|
423
|
-
if stn.ID not in
|
|
424
|
-
cmdenv
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
429
|
+
if stn.ID not in trading_ids:
|
|
430
|
+
if getattr(cmdenv, "debug", False):
|
|
431
|
+
cmdenv.DEBUG2(
|
|
432
|
+
"X {}/{} not in trading list",
|
|
433
|
+
stn.system.dbname, stn.dbname,
|
|
434
|
+
)
|
|
428
435
|
continue
|
|
429
436
|
if not checkStationSuitability(cmdenv, calc, stn):
|
|
437
|
+
if getattr(cmdenv, "debug", False):
|
|
438
|
+
cmdenv.DEBUG2(
|
|
439
|
+
"X {}/{} was not suitable",
|
|
440
|
+
stn.system.dbname, stn.dbname,
|
|
441
|
+
)
|
|
442
|
+
continue
|
|
443
|
+
if getattr(cmdenv, "debug", False):
|
|
430
444
|
cmdenv.DEBUG2(
|
|
431
|
-
"
|
|
445
|
+
"- {}/{} meets requirements",
|
|
432
446
|
stn.system.dbname, stn.dbname,
|
|
433
447
|
)
|
|
434
|
-
continue
|
|
435
|
-
cmdenv.DEBUG2(
|
|
436
|
-
"- {}/{} meets requirements",
|
|
437
|
-
stn.system.dbname, stn.dbname,
|
|
438
|
-
)
|
|
439
448
|
stations.add(stn)
|
|
440
449
|
for dest, dist in tdb.genSystemsInRange(system, maxLyPer):
|
|
441
450
|
if dest not in avoid:
|
|
442
451
|
origins.add(dest)
|
|
443
|
-
|
|
444
|
-
cmdenv
|
|
452
|
+
|
|
453
|
+
if getattr(cmdenv, "debug", False):
|
|
454
|
+
cmdenv.DEBUG0(
|
|
445
455
|
"Expanded {} stations: {}",
|
|
446
456
|
srcName,
|
|
447
457
|
[stn.name() for stn in stations]
|
|
448
|
-
|
|
449
|
-
|
|
458
|
+
)
|
|
459
|
+
|
|
450
460
|
if not stations:
|
|
451
461
|
if not cmdenv.emptyLyPer:
|
|
452
462
|
extra = (
|
|
@@ -465,12 +475,11 @@ def expandForJumps(tdb, cmdenv, calc, origin, jumps, srcName, purpose):
|
|
|
465
475
|
extra,
|
|
466
476
|
)
|
|
467
477
|
)
|
|
468
|
-
|
|
478
|
+
|
|
469
479
|
stations = list(stations)
|
|
470
480
|
stations.sort(key=lambda stn: stn.ID)
|
|
471
|
-
|
|
472
|
-
return stations
|
|
473
481
|
|
|
482
|
+
return stations
|
|
474
483
|
|
|
475
484
|
def checkForEmptyStationList(category, focusPlace, stationList, jumps):
|
|
476
485
|
if stationList:
|
|
@@ -665,41 +674,45 @@ def checkStationSuitability(cmdenv, calc, station, src = None):
|
|
|
665
674
|
def filterStationSet(src, cmdenv, calc, stnList):
|
|
666
675
|
if not stnList:
|
|
667
676
|
return stnList
|
|
668
|
-
cmdenv
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
677
|
+
if getattr(cmdenv, "debug", False):
|
|
678
|
+
cmdenv.DEBUG0(
|
|
679
|
+
"filtering {} station list: {}",
|
|
680
|
+
src,
|
|
681
|
+
",".join(station.name() for station in stnList),
|
|
672
682
|
)
|
|
673
683
|
stnList = tuple(
|
|
674
684
|
place for place in stnList
|
|
675
685
|
if isinstance(place, System) or checkStationSuitability(cmdenv, calc, place, src)
|
|
676
686
|
)
|
|
677
687
|
if not stnList:
|
|
688
|
+
# Preserve original message formatting (no behavior change)
|
|
678
689
|
raise CommandLineError("No {src} station met your criteria.")
|
|
679
690
|
return stnList
|
|
680
691
|
|
|
681
|
-
|
|
682
692
|
def checkOrigins(tdb, cmdenv, calc):
|
|
693
|
+
# Compute eligibility once: stations must both sell and buy (same as suitability with src=None).
|
|
694
|
+
eligible_ids = set(calc.stationsSelling) & set(calc.stationsBuying)
|
|
695
|
+
|
|
683
696
|
if cmdenv.origPlace:
|
|
684
697
|
if cmdenv.startJumps and cmdenv.startJumps > 0:
|
|
685
698
|
cmdenv.origins = expandForJumps(
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
699
|
+
tdb, cmdenv, calc,
|
|
700
|
+
cmdenv.origPlace.system,
|
|
701
|
+
cmdenv.startJumps,
|
|
702
|
+
"--from", "starting",
|
|
690
703
|
)
|
|
691
704
|
cmdenv.origPlace = None
|
|
692
705
|
elif isinstance(cmdenv.origPlace, System):
|
|
693
706
|
cmdenv.DEBUG0("origPlace: System: {}", cmdenv.origPlace.name())
|
|
694
707
|
if not cmdenv.origPlace.stations:
|
|
695
708
|
raise CommandLineError(
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
709
|
+
"No stations at --from system, {}"
|
|
710
|
+
.format(cmdenv.origPlace.name())
|
|
711
|
+
)
|
|
699
712
|
cmdenv.origins = tuple(
|
|
700
713
|
station
|
|
701
714
|
for station in cmdenv.origPlace.stations
|
|
702
|
-
if checkStationSuitability(cmdenv, calc, station)
|
|
715
|
+
if station.ID in eligible_ids and checkStationSuitability(cmdenv, calc, station)
|
|
703
716
|
)
|
|
704
717
|
else:
|
|
705
718
|
cmdenv.DEBUG0("origPlace: Station: {}", cmdenv.origPlace.name())
|
|
@@ -707,8 +720,8 @@ def checkOrigins(tdb, cmdenv, calc):
|
|
|
707
720
|
cmdenv.origins = (cmdenv.origPlace,)
|
|
708
721
|
cmdenv.startStation = cmdenv.origPlace
|
|
709
722
|
checkForEmptyStationList(
|
|
710
|
-
|
|
711
|
-
|
|
723
|
+
"--from", cmdenv.origPlace,
|
|
724
|
+
cmdenv.origins, cmdenv.startJumps
|
|
712
725
|
)
|
|
713
726
|
else:
|
|
714
727
|
if cmdenv.startJumps:
|
|
@@ -717,21 +730,41 @@ def checkOrigins(tdb, cmdenv, calc):
|
|
|
717
730
|
cmdenv.origins = tuple(
|
|
718
731
|
station
|
|
719
732
|
for station in tdb.stationByID.values()
|
|
720
|
-
if checkStationSuitability(cmdenv, calc, station)
|
|
733
|
+
if station.ID in eligible_ids and checkStationSuitability(cmdenv, calc, station)
|
|
721
734
|
)
|
|
722
|
-
|
|
735
|
+
|
|
723
736
|
if not cmdenv.startJumps and isinstance(cmdenv.origPlace, System):
|
|
724
737
|
cmdenv.origins = filterStationSet(
|
|
725
738
|
'--from', cmdenv, calc, cmdenv.origins
|
|
726
739
|
)
|
|
727
|
-
|
|
740
|
+
|
|
728
741
|
cmdenv.origSystems = tuple(set(
|
|
729
742
|
stn.system for stn in cmdenv.origins
|
|
730
743
|
))
|
|
731
744
|
|
|
732
|
-
|
|
733
745
|
def checkDestinations(tdb, cmdenv, calc):
|
|
734
746
|
cmdenv.destinations = None
|
|
747
|
+
showProgress = bool(getattr(cmdenv, "progress", False))
|
|
748
|
+
hb_interval = 0.5
|
|
749
|
+
last_hb = 0.0
|
|
750
|
+
spinner = ("|", "/", "-", "\\")
|
|
751
|
+
spin_i = 0
|
|
752
|
+
|
|
753
|
+
def heartbeat(seen, kept):
|
|
754
|
+
nonlocal last_hb, spin_i
|
|
755
|
+
if not showProgress:
|
|
756
|
+
return
|
|
757
|
+
now = time.time()
|
|
758
|
+
if (now - last_hb) < hb_interval:
|
|
759
|
+
return
|
|
760
|
+
last_hb = now
|
|
761
|
+
s = spinner[spin_i]
|
|
762
|
+
spin_i = (spin_i + 1) % len(spinner)
|
|
763
|
+
sys.stdout.write(
|
|
764
|
+
f"\r{s} Scanning stations… examined {seen:n} kept {kept:n}"
|
|
765
|
+
)
|
|
766
|
+
sys.stdout.flush()
|
|
767
|
+
|
|
735
768
|
if cmdenv.destPlace:
|
|
736
769
|
if cmdenv.endJumps and cmdenv.endJumps > 0:
|
|
737
770
|
cmdenv.destinations = expandForJumps(
|
|
@@ -747,11 +780,17 @@ def checkDestinations(tdb, cmdenv, calc):
|
|
|
747
780
|
cmdenv.destinations = (cmdenv.destPlace,)
|
|
748
781
|
else:
|
|
749
782
|
cmdenv.DEBUG0("destPlace: System: {}", cmdenv.destPlace.name())
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
783
|
+
# Iterate with heartbeat instead of tuple-comprehension
|
|
784
|
+
dests, seen, kept = [], 0, 0
|
|
785
|
+
for station in cmdenv.destPlace.stations:
|
|
786
|
+
seen += 1
|
|
787
|
+
if checkStationSuitability(cmdenv, calc, station):
|
|
788
|
+
dests.append(station)
|
|
789
|
+
kept += 1
|
|
790
|
+
heartbeat(seen, kept)
|
|
791
|
+
cmdenv.destinations = tuple(dests)
|
|
792
|
+
if showProgress:
|
|
793
|
+
sys.stdout.write("\n"); sys.stdout.flush()
|
|
755
794
|
checkForEmptyStationList(
|
|
756
795
|
"--to", cmdenv.destPlace,
|
|
757
796
|
cmdenv.destinations, cmdenv.endJumps
|
|
@@ -763,30 +802,40 @@ def checkDestinations(tdb, cmdenv, calc):
|
|
|
763
802
|
if cmdenv.goalSystem:
|
|
764
803
|
dest = tdb.lookupPlace(cmdenv.goalSystem)
|
|
765
804
|
cmdenv.goalSystem = dest.system
|
|
766
|
-
|
|
805
|
+
|
|
767
806
|
if cmdenv.origPlace and cmdenv.maxJumpsPer == 0:
|
|
768
807
|
stationSrc = chain.from_iterable(
|
|
769
808
|
system.stations for system in cmdenv.origSystems
|
|
770
809
|
)
|
|
771
810
|
else:
|
|
772
811
|
stationSrc = tdb.stationByID.values()
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
812
|
+
|
|
813
|
+
# Pre-filter by eligibility to skip obviously ineligible stations
|
|
814
|
+
eligible_ids = set(calc.stationsSelling) & set(calc.stationsBuying)
|
|
815
|
+
|
|
816
|
+
# Iterate with heartbeat
|
|
817
|
+
dests, seen, kept = [], 0, 0
|
|
818
|
+
for station in stationSrc:
|
|
819
|
+
seen += 1
|
|
820
|
+
if station.ID in eligible_ids and checkStationSuitability(cmdenv, calc, station):
|
|
821
|
+
dests.append(station)
|
|
822
|
+
kept += 1
|
|
823
|
+
heartbeat(seen, kept)
|
|
824
|
+
cmdenv.destinations = tuple(dests)
|
|
825
|
+
if showProgress:
|
|
826
|
+
sys.stdout.write("\n"); sys.stdout.flush()
|
|
827
|
+
|
|
780
828
|
if not cmdenv.endJumps and isinstance(cmdenv.destPlace, System):
|
|
781
829
|
cmdenv.destinations = filterStationSet(
|
|
782
830
|
'--to', cmdenv, calc, cmdenv.destinations
|
|
783
831
|
)
|
|
784
|
-
|
|
832
|
+
|
|
785
833
|
cmdenv.destSystems = tuple(set(
|
|
786
834
|
stn.system for stn in cmdenv.destinations
|
|
787
835
|
))
|
|
788
836
|
|
|
789
837
|
|
|
838
|
+
|
|
790
839
|
def validateRunArguments(tdb, cmdenv, calc):
|
|
791
840
|
"""
|
|
792
841
|
Process arguments to the 'run' option.
|
|
@@ -1140,6 +1189,9 @@ def run(results, cmdenv, tdb):
|
|
|
1140
1189
|
|
|
1141
1190
|
if tdb.tradingCount == 0:
|
|
1142
1191
|
raise NoDataError("Database does not contain any profitable trades.")
|
|
1192
|
+
|
|
1193
|
+
# Always show a friendly heads-up before heavy work begins.
|
|
1194
|
+
print("Searching for quality trades. This may take a few minutes. Please be patient.", flush=True)
|
|
1143
1195
|
|
|
1144
1196
|
# Instantiate the calculator object
|
|
1145
1197
|
calc = TradeCalc(tdb, cmdenv)
|