tradedangerous 12.7.6__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.
- py.typed +1 -0
- trade.py +49 -0
- tradedangerous/__init__.py +43 -0
- tradedangerous/cache.py +1381 -0
- tradedangerous/cli.py +136 -0
- tradedangerous/commands/TEMPLATE.py +74 -0
- tradedangerous/commands/__init__.py +244 -0
- tradedangerous/commands/buildcache_cmd.py +102 -0
- tradedangerous/commands/buy_cmd.py +427 -0
- tradedangerous/commands/commandenv.py +372 -0
- tradedangerous/commands/exceptions.py +94 -0
- tradedangerous/commands/export_cmd.py +150 -0
- tradedangerous/commands/import_cmd.py +222 -0
- tradedangerous/commands/local_cmd.py +243 -0
- tradedangerous/commands/market_cmd.py +207 -0
- tradedangerous/commands/nav_cmd.py +252 -0
- tradedangerous/commands/olddata_cmd.py +270 -0
- tradedangerous/commands/parsing.py +221 -0
- tradedangerous/commands/rares_cmd.py +298 -0
- tradedangerous/commands/run_cmd.py +1521 -0
- tradedangerous/commands/sell_cmd.py +262 -0
- tradedangerous/commands/shipvendor_cmd.py +60 -0
- tradedangerous/commands/station_cmd.py +68 -0
- tradedangerous/commands/trade_cmd.py +181 -0
- tradedangerous/commands/update_cmd.py +67 -0
- tradedangerous/corrections.py +55 -0
- tradedangerous/csvexport.py +234 -0
- tradedangerous/db/__init__.py +27 -0
- tradedangerous/db/adapter.py +192 -0
- tradedangerous/db/config.py +107 -0
- tradedangerous/db/engine.py +259 -0
- tradedangerous/db/lifecycle.py +332 -0
- tradedangerous/db/locks.py +208 -0
- tradedangerous/db/orm_models.py +500 -0
- tradedangerous/db/paths.py +113 -0
- tradedangerous/db/utils.py +661 -0
- tradedangerous/edscupdate.py +565 -0
- tradedangerous/edsmupdate.py +474 -0
- tradedangerous/formatting.py +210 -0
- tradedangerous/fs.py +156 -0
- tradedangerous/gui.py +1146 -0
- tradedangerous/mapping.py +133 -0
- tradedangerous/mfd/__init__.py +103 -0
- tradedangerous/mfd/saitek/__init__.py +3 -0
- tradedangerous/mfd/saitek/directoutput.py +678 -0
- tradedangerous/mfd/saitek/x52pro.py +195 -0
- tradedangerous/misc/checkpricebounds.py +287 -0
- tradedangerous/misc/clipboard.py +49 -0
- tradedangerous/misc/coord64.py +83 -0
- tradedangerous/misc/csvdialect.py +57 -0
- tradedangerous/misc/derp-sentinel.py +35 -0
- tradedangerous/misc/diff-system-csvs.py +159 -0
- tradedangerous/misc/eddb.py +81 -0
- tradedangerous/misc/eddn.py +349 -0
- tradedangerous/misc/edsc.py +437 -0
- tradedangerous/misc/edsm.py +121 -0
- tradedangerous/misc/importeddbstats.py +54 -0
- tradedangerous/misc/prices-json-exp.py +179 -0
- tradedangerous/misc/progress.py +194 -0
- tradedangerous/plugins/__init__.py +249 -0
- tradedangerous/plugins/edcd_plug.py +371 -0
- tradedangerous/plugins/eddblink_plug.py +861 -0
- tradedangerous/plugins/edmc_batch_plug.py +133 -0
- tradedangerous/plugins/spansh_plug.py +2647 -0
- tradedangerous/prices.py +211 -0
- tradedangerous/submit-distances.py +422 -0
- tradedangerous/templates/Added.csv +37 -0
- tradedangerous/templates/Category.csv +17 -0
- tradedangerous/templates/RareItem.csv +143 -0
- tradedangerous/templates/TradeDangerous.sql +338 -0
- tradedangerous/tools.py +40 -0
- tradedangerous/tradecalc.py +1302 -0
- tradedangerous/tradedb.py +2320 -0
- tradedangerous/tradeenv.py +313 -0
- tradedangerous/tradeenv.pyi +109 -0
- tradedangerous/tradeexcept.py +131 -0
- tradedangerous/tradeorm.py +183 -0
- tradedangerous/transfers.py +192 -0
- tradedangerous/utils.py +243 -0
- tradedangerous/version.py +16 -0
- tradedangerous-12.7.6.dist-info/METADATA +106 -0
- tradedangerous-12.7.6.dist-info/RECORD +87 -0
- tradedangerous-12.7.6.dist-info/WHEEL +5 -0
- tradedangerous-12.7.6.dist-info/entry_points.txt +3 -0
- tradedangerous-12.7.6.dist-info/licenses/LICENSE +373 -0
- tradedangerous-12.7.6.dist-info/top_level.txt +2 -0
- tradegui.py +24 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
from .commandenv import ResultRow
|
|
2
|
+
from .parsing import (
|
|
3
|
+
AvoidPlacesArgument, FleetCarrierArgument, MutuallyExclusiveGroup,
|
|
4
|
+
NoPlanetSwitch, OdysseyArgument, PadSizeArgument, ParseArgument,
|
|
5
|
+
PlanetaryArgument,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from tradedangerous import TradeDB, TradeException
|
|
9
|
+
from tradedangerous.tradedb import Station, System
|
|
10
|
+
from tradedangerous.formatting import RowFormat, ColumnFormat
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
######################################################################
|
|
14
|
+
# Parser config
|
|
15
|
+
|
|
16
|
+
help='Calculate a route between two systems.'
|
|
17
|
+
name='nav'
|
|
18
|
+
epilog=None
|
|
19
|
+
wantsTradeDB=True
|
|
20
|
+
arguments = [
|
|
21
|
+
ParseArgument('starting', help='System to start from', type=str),
|
|
22
|
+
ParseArgument('ending', help='System to end at', type=str),
|
|
23
|
+
]
|
|
24
|
+
switches = [
|
|
25
|
+
ParseArgument('--ly-per',
|
|
26
|
+
help='Maximum light years per jump.',
|
|
27
|
+
dest='maxLyPer',
|
|
28
|
+
metavar='N.NN',
|
|
29
|
+
type=float,
|
|
30
|
+
),
|
|
31
|
+
AvoidPlacesArgument(),
|
|
32
|
+
ParseArgument('--via',
|
|
33
|
+
help='Require specified systems/stations to be en-route (in order).',
|
|
34
|
+
action='append',
|
|
35
|
+
metavar='PLACE[,PLACE,...]',
|
|
36
|
+
),
|
|
37
|
+
ParseArgument('--stations', '-S',
|
|
38
|
+
help='Include station details.',
|
|
39
|
+
action='store_true',
|
|
40
|
+
),
|
|
41
|
+
ParseArgument('--refuel-jumps',
|
|
42
|
+
help='Require a station after this many jumps',
|
|
43
|
+
type=int,
|
|
44
|
+
dest='stationInterval',
|
|
45
|
+
),
|
|
46
|
+
PadSizeArgument(),
|
|
47
|
+
MutuallyExclusiveGroup(
|
|
48
|
+
NoPlanetSwitch(),
|
|
49
|
+
PlanetaryArgument(),
|
|
50
|
+
),
|
|
51
|
+
FleetCarrierArgument(),
|
|
52
|
+
OdysseyArgument(),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
######################################################################
|
|
56
|
+
# Helpers
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class NoRouteError(TradeException):
|
|
60
|
+
""" Exception denoting specifically a route could not be found. """
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
######################################################################
|
|
64
|
+
# Perform query and populate result set
|
|
65
|
+
|
|
66
|
+
def run(results, cmdenv, tdb):
|
|
67
|
+
srcSystem, dstSystem = cmdenv.origPlace, cmdenv.destPlace
|
|
68
|
+
if isinstance(srcSystem, Station):
|
|
69
|
+
srcSystem = srcSystem.system
|
|
70
|
+
if isinstance(dstSystem, Station):
|
|
71
|
+
dstSystem = dstSystem.system
|
|
72
|
+
|
|
73
|
+
maxLyPer = cmdenv.maxLyPer or cmdenv.maxSystemLinkLy
|
|
74
|
+
|
|
75
|
+
cmdenv.DEBUG0("Route from {} to {} with max {}ly per jump.",
|
|
76
|
+
srcSystem.name(), dstSystem.name(), maxLyPer)
|
|
77
|
+
|
|
78
|
+
# Build a list of src->dst pairs
|
|
79
|
+
hops = [ [ srcSystem, None ] ]
|
|
80
|
+
if cmdenv.viaPlaces:
|
|
81
|
+
for hop in cmdenv.viaPlaces:
|
|
82
|
+
hops[-1][1] = hop
|
|
83
|
+
hops.append([hop, None])
|
|
84
|
+
hops[-1][1] = dstSystem
|
|
85
|
+
|
|
86
|
+
avoiding = [
|
|
87
|
+
avoid for avoid in cmdenv.avoidPlaces
|
|
88
|
+
if isinstance(avoid, System)
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
route = [ ]
|
|
92
|
+
stationInterval = cmdenv.stationInterval
|
|
93
|
+
for hop in hops:
|
|
94
|
+
try:
|
|
95
|
+
hopRoute = list(tdb.getRoute(
|
|
96
|
+
hop[0], hop[1],
|
|
97
|
+
maxLyPer,
|
|
98
|
+
avoiding,
|
|
99
|
+
stationInterval=stationInterval,
|
|
100
|
+
))
|
|
101
|
+
except TypeError:
|
|
102
|
+
raise NoRouteError(
|
|
103
|
+
"No route found between {} and {} "
|
|
104
|
+
"with a max {}ly/jump limit.".format(
|
|
105
|
+
hop[0].name(), hop[1].name(),
|
|
106
|
+
maxLyPer,
|
|
107
|
+
))
|
|
108
|
+
route = route[:-1] + hopRoute
|
|
109
|
+
|
|
110
|
+
results.summary = ResultRow(
|
|
111
|
+
fromSys=srcSystem,
|
|
112
|
+
toSys=dstSystem,
|
|
113
|
+
maxLy=maxLyPer,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
lastSys, totalLy, dirLy = srcSystem, 0.00, 0.00
|
|
117
|
+
maxPadSize = cmdenv.padSize
|
|
118
|
+
planetary = cmdenv.planetary
|
|
119
|
+
fleet = cmdenv.fleet
|
|
120
|
+
odyssey = cmdenv.odyssey
|
|
121
|
+
noPlanet = cmdenv.noPlanet
|
|
122
|
+
|
|
123
|
+
for (jumpSys, dist) in route:
|
|
124
|
+
jumpLy = lastSys.distanceTo(jumpSys)
|
|
125
|
+
totalLy += jumpLy
|
|
126
|
+
if cmdenv.detail:
|
|
127
|
+
dirLy = jumpSys.distanceTo(dstSystem)
|
|
128
|
+
row = ResultRow(
|
|
129
|
+
action='Via',
|
|
130
|
+
system=jumpSys,
|
|
131
|
+
jumpLy=jumpLy,
|
|
132
|
+
totalLy=totalLy,
|
|
133
|
+
dirLy=dirLy,
|
|
134
|
+
)
|
|
135
|
+
row.stations = []
|
|
136
|
+
if cmdenv.stations:
|
|
137
|
+
for (station) in jumpSys.stations:
|
|
138
|
+
if maxPadSize and not station.checkPadSize(maxPadSize):
|
|
139
|
+
continue
|
|
140
|
+
if planetary and not station.checkPlanetary(planetary):
|
|
141
|
+
continue
|
|
142
|
+
if fleet and not station.checkFleet(fleet):
|
|
143
|
+
continue
|
|
144
|
+
if odyssey and not station.checkOdyssey(odyssey):
|
|
145
|
+
continue
|
|
146
|
+
if noPlanet and station.planetary != 'N':
|
|
147
|
+
continue
|
|
148
|
+
rr = ResultRow(
|
|
149
|
+
station=station,
|
|
150
|
+
age=station.itemDataAgeStr,
|
|
151
|
+
)
|
|
152
|
+
row.stations.append(rr)
|
|
153
|
+
results.rows.append(row)
|
|
154
|
+
lastSys = jumpSys
|
|
155
|
+
results.rows[0].action='Depart'
|
|
156
|
+
results.rows[-1].action='Arrive'
|
|
157
|
+
|
|
158
|
+
return results
|
|
159
|
+
|
|
160
|
+
######################################################################
|
|
161
|
+
# Transform result set into output
|
|
162
|
+
|
|
163
|
+
def render(results, cmdenv, tdb):
|
|
164
|
+
if cmdenv.quiet > 1:
|
|
165
|
+
print(','.join(row.system.name() for row in results.rows))
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
longestNamed = max(results.rows,
|
|
169
|
+
key=lambda row: len(row.system.name()))
|
|
170
|
+
longestNameLen = len(longestNamed.system.name())
|
|
171
|
+
|
|
172
|
+
rowFmt = RowFormat()
|
|
173
|
+
if cmdenv.detail:
|
|
174
|
+
rowFmt.addColumn("Action", '<', 6, post=":", key=lambda row: row.action)
|
|
175
|
+
rowFmt.addColumn("System", '<', longestNameLen,
|
|
176
|
+
key=lambda row: row.system.name())
|
|
177
|
+
rowFmt.addColumn("JumpLy", '>', '7', '.2f',
|
|
178
|
+
key=lambda row: row.jumpLy)
|
|
179
|
+
if cmdenv.detail:
|
|
180
|
+
rowFmt.addColumn("Stations", '>', 2,
|
|
181
|
+
key=lambda row: len(row.system.stations))
|
|
182
|
+
if cmdenv.detail:
|
|
183
|
+
rowFmt.addColumn("DistLy", '>', '7', '.2f',
|
|
184
|
+
key=lambda row: row.totalLy)
|
|
185
|
+
if cmdenv.detail > 1:
|
|
186
|
+
rowFmt.addColumn("DirLy", '>', 7, '.2f',
|
|
187
|
+
key=lambda row: row.dirLy)
|
|
188
|
+
|
|
189
|
+
showStations = cmdenv.stations
|
|
190
|
+
if showStations:
|
|
191
|
+
stnRowFmt = RowFormat(prefix=' / ').append(
|
|
192
|
+
ColumnFormat("Station", '<', 38,
|
|
193
|
+
key=lambda row: row.station.dbname)
|
|
194
|
+
).append(
|
|
195
|
+
ColumnFormat("StnLs", '>', '10',
|
|
196
|
+
key=lambda row: row.station.distFromStar())
|
|
197
|
+
).append(
|
|
198
|
+
ColumnFormat("Age/days", '>', 7,
|
|
199
|
+
key=lambda row: row.age)
|
|
200
|
+
).append(
|
|
201
|
+
ColumnFormat('Mkt', '>', '3',
|
|
202
|
+
key=lambda row: TradeDB.marketStates[row.station.market])
|
|
203
|
+
).append(
|
|
204
|
+
ColumnFormat("BMk", '>', '3',
|
|
205
|
+
key=lambda row: TradeDB.marketStates[row.station.blackMarket])
|
|
206
|
+
).append(
|
|
207
|
+
ColumnFormat("Shp", '>', '3',
|
|
208
|
+
key=lambda row: TradeDB.marketStates[row.station.shipyard])
|
|
209
|
+
).append(
|
|
210
|
+
ColumnFormat("Out", '>', '3',
|
|
211
|
+
key=lambda row: TradeDB.marketStates[row.station.outfitting])
|
|
212
|
+
).append(
|
|
213
|
+
ColumnFormat("Arm", '>', '3',
|
|
214
|
+
key=lambda row: TradeDB.marketStates[row.station.rearm])
|
|
215
|
+
).append(
|
|
216
|
+
ColumnFormat("Ref", '>', '3',
|
|
217
|
+
key=lambda row: TradeDB.marketStates[row.station.refuel])
|
|
218
|
+
).append(
|
|
219
|
+
ColumnFormat("Rep", '>', '3',
|
|
220
|
+
key=lambda row: TradeDB.marketStates[row.station.repair])
|
|
221
|
+
).append(
|
|
222
|
+
ColumnFormat("Pad", '>', '3',
|
|
223
|
+
key=lambda row: TradeDB.padSizes[row.station.maxPadSize])
|
|
224
|
+
).append(
|
|
225
|
+
ColumnFormat("Plt", '>', '3',
|
|
226
|
+
key=lambda row: TradeDB.planetStates[row.station.planetary])
|
|
227
|
+
).append(
|
|
228
|
+
ColumnFormat("Flc", '>', '3',
|
|
229
|
+
key=lambda row: TradeDB.fleetStates[row.station.fleet])
|
|
230
|
+
).append(
|
|
231
|
+
ColumnFormat("Ody", '>', '3',
|
|
232
|
+
key=lambda row: TradeDB.odysseyStates[row.station.odyssey])
|
|
233
|
+
)
|
|
234
|
+
if cmdenv.detail > 1:
|
|
235
|
+
stnRowFmt.append(
|
|
236
|
+
ColumnFormat("Itms", ">", 4,
|
|
237
|
+
key=lambda row: row.station.itemCount)
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
if not cmdenv.quiet:
|
|
241
|
+
heading, underline = rowFmt.heading()
|
|
242
|
+
if showStations:
|
|
243
|
+
print(heading)
|
|
244
|
+
heading, underline = stnRowFmt.heading()
|
|
245
|
+
print(heading, underline, sep='\n')
|
|
246
|
+
|
|
247
|
+
for row in results.rows:
|
|
248
|
+
print(rowFmt.format(row))
|
|
249
|
+
for stnRow in row.stations:
|
|
250
|
+
print(stnRowFmt.format(stnRow))
|
|
251
|
+
|
|
252
|
+
return results
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
from .parsing import (
|
|
2
|
+
FleetCarrierArgument, MutuallyExclusiveGroup, NoPlanetSwitch,
|
|
3
|
+
OdysseyArgument, ParseArgument, PadSizeArgument, PlanetaryArgument,
|
|
4
|
+
)
|
|
5
|
+
from ..tradedb import TradeDB
|
|
6
|
+
from ..tradeexcept import TradeException
|
|
7
|
+
from sqlalchemy import select, table, column, func, literal
|
|
8
|
+
from sqlalchemy.orm import Session
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
######################################################################
|
|
12
|
+
# Parser config
|
|
13
|
+
|
|
14
|
+
name='olddata'
|
|
15
|
+
help='Show oldest data in database.'
|
|
16
|
+
epilog=None
|
|
17
|
+
wantsTradeDB=True
|
|
18
|
+
arguments = [
|
|
19
|
+
]
|
|
20
|
+
switches = [
|
|
21
|
+
ParseArgument('--limit',
|
|
22
|
+
help='Maximum number of results to show',
|
|
23
|
+
default=20,
|
|
24
|
+
type=int,
|
|
25
|
+
),
|
|
26
|
+
ParseArgument('--near',
|
|
27
|
+
help='Find sellers within jump range of this system.',
|
|
28
|
+
type=str
|
|
29
|
+
),
|
|
30
|
+
ParseArgument('--ly',
|
|
31
|
+
help='[Requires --near] Systems within this range of --near.',
|
|
32
|
+
default=None,
|
|
33
|
+
dest='maxLyPer',
|
|
34
|
+
metavar='N.NN',
|
|
35
|
+
type=float,
|
|
36
|
+
),
|
|
37
|
+
ParseArgument('--route',
|
|
38
|
+
help='Sort to shortest path',
|
|
39
|
+
action='store_true',
|
|
40
|
+
),
|
|
41
|
+
ParseArgument('--min-age',
|
|
42
|
+
help='List data older than this number of days.',
|
|
43
|
+
type=float,
|
|
44
|
+
dest='minAge',
|
|
45
|
+
),
|
|
46
|
+
PadSizeArgument(),
|
|
47
|
+
MutuallyExclusiveGroup(
|
|
48
|
+
NoPlanetSwitch(),
|
|
49
|
+
PlanetaryArgument(),
|
|
50
|
+
),
|
|
51
|
+
FleetCarrierArgument(),
|
|
52
|
+
OdysseyArgument(),
|
|
53
|
+
ParseArgument('--ls-max',
|
|
54
|
+
help='Only consider stations upto this many ls from their star.',
|
|
55
|
+
metavar='LS',
|
|
56
|
+
dest='maxLs',
|
|
57
|
+
type=int,
|
|
58
|
+
default=0,
|
|
59
|
+
),
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
######################################################################
|
|
63
|
+
# Perform query and populate result set
|
|
64
|
+
|
|
65
|
+
def run(results, cmdenv, tdb):
|
|
66
|
+
"""
|
|
67
|
+
Backend-neutral rework:
|
|
68
|
+
- Use SQLAlchemy Core to compute age and (optionally) distance².
|
|
69
|
+
- age: age_in_days(session, MAX(StationItem.modified)) AS age_days
|
|
70
|
+
- dist2: (sys.pos_x - x0)^2 + (sys.pos_y - y0)^2 + (sys.pos_z - z0)^2 (when --near)
|
|
71
|
+
- Materialize rows to avoid closed-cursor errors.
|
|
72
|
+
- Preserve downstream filters/sorting/limit exactly as before.
|
|
73
|
+
"""
|
|
74
|
+
from .commandenv import ResultRow
|
|
75
|
+
from tradedangerous.db.utils import age_in_days
|
|
76
|
+
|
|
77
|
+
cmdenv = results.cmdenv
|
|
78
|
+
tdb = cmdenv.tdb
|
|
79
|
+
|
|
80
|
+
results.summary = ResultRow()
|
|
81
|
+
results.limit = cmdenv.limit
|
|
82
|
+
|
|
83
|
+
# SQLAlchemy lightweight table defs
|
|
84
|
+
si = table(
|
|
85
|
+
"StationItem",
|
|
86
|
+
column("station_id"),
|
|
87
|
+
column("item_id"),
|
|
88
|
+
column("modified"),
|
|
89
|
+
)
|
|
90
|
+
stn = table(
|
|
91
|
+
"Station",
|
|
92
|
+
column("station_id"),
|
|
93
|
+
column("system_id"),
|
|
94
|
+
column("ls_from_star"),
|
|
95
|
+
)
|
|
96
|
+
sys_tbl = table(
|
|
97
|
+
"System",
|
|
98
|
+
column("system_id"),
|
|
99
|
+
column("pos_x"),
|
|
100
|
+
column("pos_y"),
|
|
101
|
+
column("pos_z"),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Build session bound to current engine (age_in_days needs the session)
|
|
105
|
+
session = Session(bind=tdb.engine)
|
|
106
|
+
|
|
107
|
+
# Base SELECT: station_id, ls_from_star, age_days
|
|
108
|
+
age_expr = age_in_days(session, func.max(si.c.modified)).label("age_days")
|
|
109
|
+
|
|
110
|
+
# Optional near-system distance²
|
|
111
|
+
nearSys = cmdenv.nearSystem
|
|
112
|
+
if nearSys:
|
|
113
|
+
dx = (sys_tbl.c.pos_x - literal(nearSys.posX))
|
|
114
|
+
dy = (sys_tbl.c.pos_y - literal(nearSys.posY))
|
|
115
|
+
dz = (sys_tbl.c.pos_z - literal(nearSys.posZ))
|
|
116
|
+
dist2_expr = (dx * dx + dy * dy + dz * dz).label("d2")
|
|
117
|
+
else:
|
|
118
|
+
dist2_expr = literal(0.0).label("d2")
|
|
119
|
+
|
|
120
|
+
stmt = (
|
|
121
|
+
select(
|
|
122
|
+
si.c.station_id,
|
|
123
|
+
age_expr,
|
|
124
|
+
stn.c.ls_from_star,
|
|
125
|
+
dist2_expr,
|
|
126
|
+
)
|
|
127
|
+
.select_from(
|
|
128
|
+
si.join(stn, stn.c.station_id == si.c.station_id)
|
|
129
|
+
.join(sys_tbl, sys_tbl.c.system_id == stn.c.system_id) if nearSys
|
|
130
|
+
else si.join(stn, stn.c.station_id == si.c.station_id)
|
|
131
|
+
)
|
|
132
|
+
.group_by(si.c.station_id, stn.c.ls_from_star, dist2_expr)
|
|
133
|
+
.order_by(age_expr.desc())
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Bounding box for near (keeps scan small, mirrors original)
|
|
137
|
+
if nearSys:
|
|
138
|
+
maxLy = cmdenv.maxLyPer or cmdenv.maxSystemLinkLy
|
|
139
|
+
# Bounding box predicate
|
|
140
|
+
stmt = stmt.where(
|
|
141
|
+
sys_tbl.c.pos_x.between(nearSys.posX - maxLy, nearSys.posX + maxLy),
|
|
142
|
+
sys_tbl.c.pos_y.between(nearSys.posY - maxLy, nearSys.posY + maxLy),
|
|
143
|
+
sys_tbl.c.pos_z.between(nearSys.posZ - maxLy, nearSys.posZ + maxLy),
|
|
144
|
+
)
|
|
145
|
+
# Radius filter: HAVING dist2 <= maxLy^2
|
|
146
|
+
stmt = stmt.having(dist2_expr <= (maxLy * maxLy))
|
|
147
|
+
|
|
148
|
+
# Min-age filter (apply to aggregated age of MAX(modified))
|
|
149
|
+
if cmdenv.minAge:
|
|
150
|
+
stmt = stmt.having(age_expr >= float(cmdenv.minAge))
|
|
151
|
+
|
|
152
|
+
# Execute and materialize rows
|
|
153
|
+
rows = session.execute(stmt).fetchall()
|
|
154
|
+
session.close()
|
|
155
|
+
|
|
156
|
+
# Downstream filters (unchanged)
|
|
157
|
+
padSize = cmdenv.padSize
|
|
158
|
+
planetary = cmdenv.planetary
|
|
159
|
+
fleet = cmdenv.fleet
|
|
160
|
+
odyssey = cmdenv.odyssey
|
|
161
|
+
noPlanet = cmdenv.noPlanet
|
|
162
|
+
mls = cmdenv.maxLs
|
|
163
|
+
|
|
164
|
+
for (stnID, age, ls, dist2) in rows:
|
|
165
|
+
cmdenv.DEBUG2("{}:{}:{}", stnID, age, ls)
|
|
166
|
+
row = ResultRow()
|
|
167
|
+
row.station = tdb.stationByID[stnID]
|
|
168
|
+
row.age = float(age or 0.0)
|
|
169
|
+
row.ls = "{:n}".format(ls) if ls else "?"
|
|
170
|
+
row.dist = (float(dist2) ** 0.5) if dist2 else 0.0
|
|
171
|
+
|
|
172
|
+
if padSize and not row.station.checkPadSize(padSize):
|
|
173
|
+
continue
|
|
174
|
+
if planetary and not row.station.checkPlanetary(planetary):
|
|
175
|
+
continue
|
|
176
|
+
if fleet and not row.station.checkFleet(fleet):
|
|
177
|
+
continue
|
|
178
|
+
if odyssey and not row.station.checkOdyssey(odyssey):
|
|
179
|
+
continue
|
|
180
|
+
if noPlanet and row.station.planetary != 'N':
|
|
181
|
+
continue
|
|
182
|
+
if mls and row.station.lsFromStar > mls:
|
|
183
|
+
continue
|
|
184
|
+
|
|
185
|
+
results.rows.append(row)
|
|
186
|
+
|
|
187
|
+
# Route optimization and limiting (unchanged)
|
|
188
|
+
if cmdenv.route and len(results.rows) > 1:
|
|
189
|
+
def walk(start_idx, dist):
|
|
190
|
+
rows_ = results.rows
|
|
191
|
+
startNode = rows_[start_idx]
|
|
192
|
+
openList = set(rows_)
|
|
193
|
+
path = [startNode]
|
|
194
|
+
openList.remove(startNode)
|
|
195
|
+
while len(path) < len(rows_):
|
|
196
|
+
lastNode = path[-1]
|
|
197
|
+
distFn = lastNode.station.system.distanceTo
|
|
198
|
+
nearest = min(openList, key=lambda r: distFn(r.station.system))
|
|
199
|
+
openList.remove(nearest)
|
|
200
|
+
path.append(nearest)
|
|
201
|
+
dist += distFn(nearest.station.system)
|
|
202
|
+
return (path, dist)
|
|
203
|
+
|
|
204
|
+
if cmdenv.near:
|
|
205
|
+
bestPath = walk(0, results.rows[0].dist)
|
|
206
|
+
else:
|
|
207
|
+
bestPath = (results.rows, float("inf"))
|
|
208
|
+
for i in range(len(results.rows)):
|
|
209
|
+
candidate = walk(i, 0)
|
|
210
|
+
if candidate[1] < bestPath[1]:
|
|
211
|
+
bestPath = candidate
|
|
212
|
+
results.rows[:] = bestPath[0]
|
|
213
|
+
|
|
214
|
+
if cmdenv.limit:
|
|
215
|
+
results.rows[:] = results.rows[:cmdenv.limit]
|
|
216
|
+
|
|
217
|
+
return results
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
######################################################################
|
|
221
|
+
# Transform result set into output
|
|
222
|
+
|
|
223
|
+
def render(results, cmdenv, tdb):
|
|
224
|
+
from ..formatting import RowFormat, ColumnFormat
|
|
225
|
+
|
|
226
|
+
if not results or not results.rows:
|
|
227
|
+
raise TradeException("No data found")
|
|
228
|
+
|
|
229
|
+
# Compare system names so we can tell
|
|
230
|
+
longestNamed = max(results.rows,
|
|
231
|
+
key=lambda row: len(row.station.name()))
|
|
232
|
+
longestNameLen = len(longestNamed.station.name())
|
|
233
|
+
|
|
234
|
+
rowFmt = RowFormat().append(
|
|
235
|
+
ColumnFormat("Station", '<', longestNameLen,
|
|
236
|
+
key=lambda row: row.station.name())
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
if cmdenv.quiet < 2:
|
|
240
|
+
if cmdenv.nearSystem:
|
|
241
|
+
rowFmt.addColumn('DistLy', '>', 6, '.2f',
|
|
242
|
+
key=lambda row: row.dist
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
rowFmt.append(
|
|
246
|
+
ColumnFormat("Age/days", '>', '8', '.2f',
|
|
247
|
+
key=lambda row: row.age)
|
|
248
|
+
).append(
|
|
249
|
+
ColumnFormat("StnLs", '>', '10',
|
|
250
|
+
key=lambda row: row.station.distFromStar())
|
|
251
|
+
).append(
|
|
252
|
+
ColumnFormat("Pad", '>', '3',
|
|
253
|
+
key=lambda row: TradeDB.padSizes[row.station.maxPadSize])
|
|
254
|
+
).append(
|
|
255
|
+
ColumnFormat("Plt", '>', '3',
|
|
256
|
+
key=lambda row: TradeDB.planetStates[row.station.planetary])
|
|
257
|
+
).append(
|
|
258
|
+
ColumnFormat("Flc", '>', '3',
|
|
259
|
+
key=lambda row: TradeDB.fleetStates[row.station.fleet])
|
|
260
|
+
).append(
|
|
261
|
+
ColumnFormat("Ody", '>', '3',
|
|
262
|
+
key=lambda row: TradeDB.odysseyStates[row.station.odyssey])
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
if not cmdenv.quiet:
|
|
266
|
+
heading, underline = rowFmt.heading()
|
|
267
|
+
print(heading, underline, sep='\n')
|
|
268
|
+
|
|
269
|
+
for row in results.rows:
|
|
270
|
+
print(rowFmt.format(row))
|