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.

Files changed (38) hide show
  1. tradedangerous/cache.py +567 -395
  2. tradedangerous/cli.py +2 -2
  3. tradedangerous/commands/TEMPLATE.py +25 -26
  4. tradedangerous/commands/__init__.py +8 -16
  5. tradedangerous/commands/buildcache_cmd.py +40 -10
  6. tradedangerous/commands/buy_cmd.py +57 -46
  7. tradedangerous/commands/commandenv.py +0 -2
  8. tradedangerous/commands/export_cmd.py +78 -50
  9. tradedangerous/commands/import_cmd.py +67 -31
  10. tradedangerous/commands/market_cmd.py +52 -19
  11. tradedangerous/commands/olddata_cmd.py +120 -107
  12. tradedangerous/commands/rares_cmd.py +122 -110
  13. tradedangerous/commands/run_cmd.py +118 -66
  14. tradedangerous/commands/sell_cmd.py +52 -45
  15. tradedangerous/commands/shipvendor_cmd.py +49 -234
  16. tradedangerous/commands/station_cmd.py +55 -485
  17. tradedangerous/commands/update_cmd.py +56 -420
  18. tradedangerous/csvexport.py +173 -162
  19. tradedangerous/gui.py +2 -2
  20. tradedangerous/plugins/eddblink_plug.py +387 -251
  21. tradedangerous/plugins/spansh_plug.py +2488 -821
  22. tradedangerous/prices.py +124 -142
  23. tradedangerous/templates/TradeDangerous.sql +6 -6
  24. tradedangerous/tradecalc.py +1227 -1109
  25. tradedangerous/tradedb.py +533 -384
  26. tradedangerous/tradeenv.py +12 -1
  27. tradedangerous/version.py +1 -1
  28. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info}/METADATA +6 -4
  29. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info}/RECORD +33 -38
  30. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info}/WHEEL +1 -1
  31. tradedangerous/commands/update_gui.py +0 -721
  32. tradedangerous/jsonprices.py +0 -254
  33. tradedangerous/plugins/edapi_plug.py +0 -1071
  34. tradedangerous/plugins/journal_plug.py +0 -537
  35. tradedangerous/plugins/netlog_plug.py +0 -316
  36. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info}/entry_points.txt +0 -0
  37. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info/licenses}/LICENSE +0 -0
  38. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.0.dist-info}/top_level.txt +0 -0
@@ -7,6 +7,7 @@ from .parsing import (
7
7
  )
8
8
  from ..tradedb import TradeDB, System, Station
9
9
  from ..formatting import RowFormat
10
+ from sqlalchemy import text
10
11
 
11
12
 
12
13
  ######################################################################
@@ -81,6 +82,14 @@ switches = [
81
82
  # Perform query and populate result set
82
83
 
83
84
  def run(results, cmdenv, tdb: TradeDB):
85
+ """
86
+ Backend-neutral implementation:
87
+ - Use SQLAlchemy text() with NAMED binds (':param') instead of '?'.
88
+ - Eagerly materialize rows via engine.connect().execute(...).fetchall()
89
+ to avoid closed-cursor errors when iterating later.
90
+ - Preserve all existing filters, sorting, and output fields.
91
+ """
92
+
84
93
  if cmdenv.lt and cmdenv.gt:
85
94
  if cmdenv.lt <= cmdenv.gt:
86
95
  raise CommandLineError("--gt must be lower than --lt")
@@ -96,37 +105,54 @@ def run(results, cmdenv, tdb: TradeDB):
96
105
  results.summary.avoidSystems = avoidSystems
97
106
  results.summary.avoidStations = avoidStations
98
107
 
108
+ # Detail: average demand price for this item (portable named bind)
99
109
  if cmdenv.detail:
100
- avgPrice = tdb.query("""
101
- SELECT AVG(si.demand_price)
102
- FROM StationItem AS si
103
- WHERE si.item_id = ? AND si.demand_price > 0
104
- """, [item.ID]).fetchone()[0]
105
- results.summary.avg = int(avgPrice)
106
-
107
- # Constraints
108
- tables = "StationItem AS si"
109
- constraints = [
110
- "(item_id = {} AND demand_price > 0)".format(item.ID),
111
- ]
112
- columns = [
113
- 'si.station_id',
114
- 'si.demand_price',
115
- 'si.demand_units',
116
- ]
117
- bindValues = []
110
+ with tdb.engine.connect() as conn:
111
+ avg_val = conn.execute(
112
+ text("""
113
+ SELECT AVG(si.demand_price)
114
+ FROM StationItem AS si
115
+ WHERE si.item_id = :item_id AND si.demand_price > 0
116
+ """),
117
+ {"item_id": item.ID},
118
+ ).scalar()
119
+ results.summary.avg = int(avg_val or 0)
118
120
 
121
+ # Build the main query with named binds
122
+ columns = "si.station_id, si.demand_price, si.demand_units"
123
+ where = ["si.item_id = :item_id", "si.demand_price > 0"]
124
+ params = {"item_id": item.ID}
125
+
119
126
  if cmdenv.demand:
120
- constraints.append("(demand_units >= ?)")
121
- bindValues.append(cmdenv.demand)
122
-
127
+ where.append("si.demand_units >= :demand")
128
+ params["demand"] = cmdenv.demand
123
129
  if cmdenv.lt:
124
- constraints.append("(demand_price < ?)")
125
- bindValues.append(cmdenv.lt)
130
+ where.append("si.demand_price < :lt")
131
+ params["lt"] = cmdenv.lt
126
132
  if cmdenv.gt:
127
- constraints.append("(demand_price > ?)")
128
- bindValues.append(cmdenv.gt)
133
+ where.append("si.demand_price > :gt")
134
+ params["gt"] = cmdenv.gt
135
+
136
+ stmt = f"""
137
+ SELECT DISTINCT {columns}
138
+ FROM StationItem AS si
139
+ WHERE {' AND '.join(where)}
140
+ """
141
+ cmdenv.DEBUG0('SQL: {} ; params={}', stmt, params)
142
+
143
+ # Execute and eagerly fetch rows
144
+ with tdb.engine.connect() as conn:
145
+ cur_rows = conn.execute(text(stmt), params).fetchall()
129
146
 
147
+ stationByID = tdb.stationByID
148
+ padSize = cmdenv.padSize
149
+ planetary = cmdenv.planetary
150
+ fleet = cmdenv.fleet
151
+ odyssey = cmdenv.odyssey
152
+ wantNoPlanet = cmdenv.noPlanet
153
+ wantBlackMarket = cmdenv.blackMarket
154
+
155
+ # System-based search
130
156
  nearSystem = cmdenv.nearSystem
131
157
  if nearSystem:
132
158
  maxLy = cmdenv.maxLyPer or tdb.maxSystemLinkLy
@@ -136,24 +162,7 @@ def run(results, cmdenv, tdb: TradeDB):
136
162
  else:
137
163
  distanceFn = None
138
164
 
139
- whereClause = ' AND '.join(constraints)
140
- stmt = """SELECT DISTINCT {columns} FROM {tables} WHERE {where}""".format(
141
- columns=','.join(columns),
142
- tables=tables,
143
- where=whereClause
144
- )
145
- cmdenv.DEBUG0('SQL: {}', stmt)
146
- cur = tdb.query(stmt, bindValues)
147
-
148
- stationByID = tdb.stationByID
149
- padSize = cmdenv.padSize
150
- planetary = cmdenv.planetary
151
- fleet = cmdenv.fleet
152
- odyssey = cmdenv.odyssey
153
- wantNoPlanet = cmdenv.noPlanet
154
- wantBlackMarket = cmdenv.blackMarket
155
-
156
- for (stationID, priceCr, demand) in cur:
165
+ for (stationID, priceCr, demand) in cur_rows:
157
166
  station = stationByID[stationID]
158
167
  if padSize and not station.checkPadSize(padSize):
159
168
  continue
@@ -187,8 +196,6 @@ def run(results, cmdenv, tdb: TradeDB):
187
196
  row.age = station.itemDataAgeStr
188
197
  results.rows.append(row)
189
198
 
190
- cur.close()
191
-
192
199
  if not results.rows:
193
200
  raise NoDataError("No available items found")
194
201
 
@@ -1,244 +1,59 @@
1
- # Deprecated
2
- # Can already use buy command for ship searching
3
- from .commandenv import ResultRow
4
- from .exceptions import CommandLineError
5
- from .parsing import MutuallyExclusiveGroup, ParseArgument
6
- from ..formatting import RowFormat, ColumnFormat, max_len
7
- from itertools import chain
8
- from ..tradedb import Station
9
-
10
- # Original by Dirk Wilhelm
11
-
12
- from .. import csvexport
1
+ # tradedangerous/commands/shipvendor_cmd.py — DEPRECATED (no-op)
2
+ # This command no longer maintains ship lists directly.
3
+ # Guidance:
4
+ # • Import authoritative data via: trade import -P spansh | trade import -P eddblink
5
+ # • Search for ships via: trade buy --near "<place>" --ly N "<ship>"
6
+
7
+ from __future__ import annotations
8
+ from .parsing import ParseArgument # to swallow arbitrary positional args
9
+
10
+ # ---- Command metadata ----
11
+ help = "DEPRECATED: no longer used. See deprecation banner when run."
12
+ name = "shipvendor"
13
+ epilog = None
14
+ acceptUnknown = True
13
15
 
14
- ######################################################################
15
- # Parser config
16
+ # No DB access needed.
17
+ wantsTradeDB = False
18
+ usesTradeData = False
16
19
 
17
- help = 'List, add or update available ships to a station'
18
- name = 'shipvendor'
19
- epilog = None
20
- arguments = [
20
+ # Accept ANY number of positional args and ignore them (prevents parser errors).
21
+ arguments = (
21
22
  ParseArgument(
22
- 'origin',
23
- help = 'Specify the full name of the station '
24
- '(SYS NAME/STN NAME is also supported).',
25
- metavar = 'STATIONNAME',
26
- type = str,
27
- ),
28
- ParseArgument(
29
- 'ship',
30
- help = 'Comma or space separated list of ship names.',
31
- type = str,
32
- nargs = '*',
33
- ),
34
- ]
35
- switches = [
36
- MutuallyExclusiveGroup(
37
- ParseArgument(
38
- '--remove', '-rm',
39
- help = 'Indicates you want to remove an entry.',
40
- action = 'store_true',
41
- ),
42
- ParseArgument(
43
- '--add', '-a',
44
- help = 'Indicates you want to add a new station.',
45
- action = 'store_true',
46
- ),
47
- ParseArgument(
48
- '--name-sort',
49
- help = 'Sort listed ships by name.',
50
- action = 'store_true',
51
- dest = 'nameSort',
52
- ),
23
+ 'args',
24
+ help="(deprecated) ignored",
25
+ nargs='*',
26
+ type=str,
53
27
  ),
54
- ParseArgument(
55
- '--no-export',
56
- help = 'Do not update the .csv files.',
57
- action = 'store_true',
58
- dest = 'noExport',
59
- ),
60
- ]
61
-
62
- ######################################################################
63
- # Helpers
64
-
65
-
66
- def addShipVendor(tdb, cmdenv, station, ship):
67
- db = tdb.getDB()
68
- db.execute("""
69
- INSERT INTO ShipVendor (
70
- ship_id, station_id
71
- ) VALUES (
72
- ?, ?
73
- )
74
- """, [
75
- ship.ID, station.ID
76
- ])
77
- db.commit()
78
- cmdenv.NOTE("At {} adding {}", station.name(), ship.name())
79
- return ship
80
-
81
-
82
- def removeShipVendor(tdb, cmdenv, station, ship):
83
- db = tdb.getDB()
84
- db.execute("""
85
- DELETE FROM ShipVendor WHERE ship_id = ? and station_id = ?
86
- """, [ship.ID, station.ID])
87
- db.commit()
88
- cmdenv.NOTE("At {} removing {}", station.name(), ship.name())
89
- return ship
90
-
91
-
92
- def maybeExportToCSV(tdb, cmdenv):
93
- if cmdenv.noExport:
94
- cmdenv.DEBUG0("no-export set, not exporting stations")
95
- return
96
-
97
- lines, csvPath = csvexport.exportTableToFile(tdb, cmdenv, "ShipVendor")
98
- cmdenv.NOTE("{} updated.", csvPath)
99
-
100
-
101
- def checkShipPresent(tdb, station, ship):
102
- # Ask the database how many rows it sees claiming
103
- # this ship is sold at that station. The value will
104
- # be zero if we don't have an entry, otherwise it
105
- # will be some positive number.
106
- cur = tdb.query("""
107
- SELECT COUNT(*)
108
- FROM ShipVendor
109
- WHERE ship_id = ? AND station_id = ?
110
- """, [ship.ID, station.ID]
111
- )
112
- # Get the first result of the row and get the first column of that row
113
- count = int(cur.fetchone()[0])
114
-
115
- # Test if count > 0, if so, we'll return True, otherwise False
116
- return (count > 0)
117
-
118
-
119
- def listShipsPresent(tdb, cmdenv, station, results):
120
- """ Populate results with a list of ships present at the given station """
121
- cur = tdb.query("""
122
- SELECT ship_id
123
- FROM ShipVendor
124
- WHERE station_id = ?
125
- """, [station.ID]
28
+ )
29
+ # No switches; unknown switches will still be rejected by the global parser.
30
+ switches = tuple()
31
+
32
+
33
+ def _banner() -> str:
34
+ return (
35
+ "\n"
36
+ "=== DEPRECATION NOTICE: shipvendor ==================================\n"
37
+ "This command is no longer used and does not modify the database.\n"
38
+ "• Import authoritative data with: trade import -P eddblink | -P spansh\n"
39
+ "• Search for ships using: trade buy --near \"<place>\" --ly N \"<ship>\"\n"
40
+ "======================================================================\n"
126
41
  )
127
-
128
- results.summary = ResultRow()
129
- results.summary.station = station
130
- ships = tdb.shipByID
131
- addShip = results.rows.append
132
-
133
- for (ship_id,) in cur:
134
- ship = ships.get(ship_id, None)
135
- if ship:
136
- addShip(ResultRow(ship = ship))
137
-
138
- if cmdenv.nameSort:
139
- results.rows.sort(key = lambda row: row.ship.name())
140
- else:
141
- results.rows.sort(key = lambda row: row.ship.cost, reverse = True)
142
-
143
- return results
144
-
145
- ######################################################################
146
- # Perform query and populate result set
147
42
 
148
43
 
149
- def run(results, cmdenv, tdb):
150
- station = cmdenv.startStation
151
- if not isinstance(station, Station):
152
- raise CommandLineError(
153
- "{} is a system, not a station"
154
- .format(station.name())
155
- )
156
- if station.shipyard == 'N' and not cmdenv.remove:
157
- raise CommandLineError(
158
- "{} is flagged as having no shipyard."
159
- .format(station.name())
160
- )
161
-
162
- if cmdenv.add:
163
- action = addShipVendor
164
- elif cmdenv.remove:
165
- action = removeShipVendor
166
- else:
167
- return listShipsPresent(tdb, cmdenv, station, results)
168
-
169
- if not cmdenv.ship:
170
- raise CommandLineError(
171
- "No ship names specified."
172
- )
173
-
174
- cmdenv.NOTE("Using local database ({})", tdb.dbPath)
175
- ships = {}
176
- shipNames = chain.from_iterable(
177
- name.split(",") for name in cmdenv.ship
178
- )
179
- for shipName in shipNames:
180
- try:
181
- ship = tdb.lookupShip(shipName)
182
- except LookupError:
183
- raise CommandLineError("Unrecognized Ship: {}".format(shipName))
184
-
185
- # Lets see if that ship sails from the specified port.
186
- shipPresent = checkShipPresent(tdb, station, ship)
187
- if cmdenv.add:
188
- if shipPresent:
189
- cmdenv.DEBUG0("{} is already listed at {}",
190
- ship.name(), station.name()
191
- )
192
- else:
193
- ships[ship.ID] = ship
194
- else:
195
- if not shipPresent:
196
- cmdenv.DEBUG0("{} is not listed at {}",
197
- ship.name(), station.name()
198
- )
199
- else:
200
- ships[ship.ID] = ship
201
-
202
- if len(ships) == 0:
203
- cmdenv.NOTE("Nothing to do.")
204
- return None
205
-
206
- # We've checked that everything should be good.
207
- dataToExport = False
208
- for ship in ships.values():
209
- if action(tdb, cmdenv, station, ship):
210
- dataToExport = True
211
-
212
- cmdenv.DEBUG0("dataToExport = {}", dataToExport)
213
-
214
- maybeExportToCSV(tdb, cmdenv)
215
-
44
+ def run(results, cmdenv, tdb=None):
45
+ """
46
+ No-op implementation: print banner and exit immediately.
47
+ All arguments/switches are ignored by design.
48
+ """
49
+ banner = _banner()
50
+ try:
51
+ cmdenv.NOTE("{}", banner)
52
+ except Exception:
53
+ print(banner)
216
54
  return None
217
55
 
218
- ######################################################################
219
- # Transform result set into output
220
-
221
56
 
222
- def render(results, cmdenv, tdb):
223
- if not results or not results.rows:
224
- raise CommandLineError(
225
- "No ships available at {}"
226
- .format(results.summary.station.name())
227
- )
228
-
229
- maxShipLen = max_len(results.rows, key = lambda row: row.ship.name())
230
-
231
- rowFmt = RowFormat().append(
232
- ColumnFormat("Ship", '<', maxShipLen,
233
- key = lambda row: row.ship.name())
234
- ).append(
235
- ColumnFormat("Cost", '>', 12, 'n',
236
- key = lambda row: row.ship.cost)
237
- )
238
-
239
- if not cmdenv.quiet:
240
- heading, underline = rowFmt.heading()
241
- print(heading, underline, sep = '\n')
242
-
243
- for row in results.rows:
244
- print(rowFmt.format(row))
57
+ def render(results, cmdenv, tdb=None):
58
+ # No output beyond the banner emitted in run().
59
+ return