tradedangerous 11.5.3__py3-none-any.whl → 12.0.1__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 (47) 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/db/__init__.py +27 -0
  20. tradedangerous/db/adapter.py +191 -0
  21. tradedangerous/db/config.py +95 -0
  22. tradedangerous/db/engine.py +246 -0
  23. tradedangerous/db/lifecycle.py +332 -0
  24. tradedangerous/db/locks.py +208 -0
  25. tradedangerous/db/orm_models.py +455 -0
  26. tradedangerous/db/paths.py +112 -0
  27. tradedangerous/db/utils.py +661 -0
  28. tradedangerous/gui.py +2 -2
  29. tradedangerous/plugins/eddblink_plug.py +387 -251
  30. tradedangerous/plugins/spansh_plug.py +2488 -821
  31. tradedangerous/prices.py +124 -142
  32. tradedangerous/templates/TradeDangerous.sql +6 -6
  33. tradedangerous/tradecalc.py +1227 -1109
  34. tradedangerous/tradedb.py +533 -384
  35. tradedangerous/tradeenv.py +12 -1
  36. tradedangerous/version.py +1 -1
  37. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info}/METADATA +11 -7
  38. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info}/RECORD +42 -38
  39. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info}/WHEEL +1 -1
  40. tradedangerous/commands/update_gui.py +0 -721
  41. tradedangerous/jsonprices.py +0 -254
  42. tradedangerous/plugins/edapi_plug.py +0 -1071
  43. tradedangerous/plugins/journal_plug.py +0 -537
  44. tradedangerous/plugins/netlog_plug.py +0 -316
  45. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info}/entry_points.txt +0 -0
  46. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info/licenses}/LICENSE +0 -0
  47. {tradedangerous-11.5.3.dist-info → tradedangerous-12.0.1.dist-info}/top_level.txt +0 -0
tradedangerous/prices.py CHANGED
@@ -1,7 +1,8 @@
1
1
  # --------------------------------------------------------------------
2
2
  # Copyright (C) Oliver 'kfsone' Smith 2014 <oliver@kfs.org>:
3
3
  # Copyright (C) Bernd 'Gazelle' Gollesch 2016, 2017
4
- # Copyright (C) Jonathan 'eyeonus' Jones 2018, 2019
4
+ # Copyright (C) Stefan 'Tromador' Morrell 2025
5
+ # Copyright (C) Jonathan 'eyeonus' Jones 2018-2025
5
6
  #
6
7
  # You are free to use, redistribute, or even print and eat a copy of
7
8
  # this software so long as you include this copyright notice.
@@ -10,7 +11,9 @@
10
11
  # TradeDangerous :: Modules :: Generate TradeDangerous.prices
11
12
 
12
13
  import sys
13
- import sqlite3
14
+ from sqlalchemy.orm import Session
15
+ from .db import orm_models as SA
16
+ from .tradeexcept import TradeException
14
17
 
15
18
 
16
19
  class Element: # TODO: enum?
@@ -18,118 +21,101 @@ class Element: # TODO: enum?
18
21
  supply = 1 << 1
19
22
  timestamp = 1 << 2
20
23
  full = basic | supply | timestamp
21
- blanks = 1 <<31
24
+ blanks = 1 << 31
22
25
 
23
26
 
24
27
  ######################################################################
25
28
  # Main
26
29
 
27
30
  def dumpPrices(
28
- dbPath, # Path() or str
29
- elementMask, # which columns to output
30
- stationID=None, # limits to one station
31
- file=None, # file handle to write to
32
- defaultZero=False,
33
- debug=0
34
- ):
31
+ session: Session, # SQLAlchemy session
32
+ elementMask, # which columns to output
33
+ stationID=None, # limits to one station
34
+ file=None, # file handle to write to
35
+ defaultZero=False,
36
+ debug=0,
37
+ ):
35
38
  """
36
- Generate a prices list using data from the DB.
37
- If stationID is not none, only the specified station is dumped.
38
- If file is not none, outputs to the given file handle.
39
+ Generate a prices list using data from the DB.
40
+ If stationID is not None, only the specified station is dumped.
41
+ If file is not None, outputs to the given file handle.
39
42
  """
40
-
41
- withTimes = elementMask & Element.timestamp
42
- getBlanks = elementMask & Element.blanks
43
-
44
- conn = sqlite3.connect(str(dbPath))
45
- conn.execute("PRAGMA foreign_keys=ON")
46
- cur = conn.cursor()
47
-
48
- systems = dict(cur.execute("SELECT system_id, name FROM System"))
43
+
44
+ withTimes = elementMask & Element.timestamp
45
+ getBlanks = elementMask & Element.blanks
46
+
47
+ # ORM queries to build lookup dicts
48
+ systems = dict(
49
+ session.query(SA.System.system_id, SA.System.name).all()
50
+ )
51
+
49
52
  stations = {
50
- ID: [ name, systems[sysID] ]
51
- for (ID, name, sysID)
52
- in cur.execute("SELECT station_id, name, system_id FROM Station")
53
+ ID: [name, systems.get(sysID)]
54
+ for ID, name, sysID in session.query(
55
+ SA.Station.station_id, SA.Station.name, SA.Station.system_id
56
+ ).all()
53
57
  }
54
- categories = dict(cur.execute("SELECT category_id, name FROM Category"))
58
+
59
+ categories = dict(
60
+ session.query(SA.Category.category_id, SA.Category.name).all()
61
+ )
62
+
55
63
  items = {
56
- ID: [ name, catID, categories[catID] ]
57
- for (ID, name, catID)
58
- in cur.execute("SELECT item_id, name, category_id FROM Item")
64
+ ID: [name, catID, categories[catID]]
65
+ for ID, name, catID in session.query(
66
+ SA.Item.item_id, SA.Item.name, SA.Item.category_id
67
+ ).all()
59
68
  }
60
-
61
- # find longest item name
69
+
70
+ # find longest item name (for formatting)
62
71
  longestName = max(items.values(), key=lambda ent: len(ent[0]))
63
72
  longestNameLen = len(longestName[0])
64
-
65
- if stationID:
66
- # check if there are prices for the station
67
- cur.execute("""
68
- SELECT COUNT(*)
69
- FROM StationItem
70
- WHERE station_id = {}
71
- """.format(stationID))
72
- if not cur.fetchone()[0]:
73
- getBlanks = True
74
-
73
+
75
74
  defaultDemandVal = 0 if defaultZero else -1
76
- if stationID:
77
- stationWhere = "WHERE stn.station_id = {}".format(stationID)
78
- else:
79
- stationWhere = ""
80
-
81
- if getBlanks:
82
- itemJoin = "LEFT OUTER"
83
- else:
84
- itemJoin = "INNER"
85
-
86
- cur.execute("SELECT CURRENT_TIMESTAMP")
87
- now = cur.fetchone()[0]
88
-
89
- stmt = """
90
- SELECT stn.station_id, itm.item_id
91
- , IFNULL(si.demand_price, 0)
92
- , IFNULL(si.supply_price, 0)
93
- , IFNULL(si.demand_units, {defDemand})
94
- , IFNULL(si.demand_level, {defDemand})
95
- , IFNULL(si.supply_units, {defDemand})
96
- , IFNULL(si.supply_level, {defDemand})
97
- , si.modified
98
- FROM Station stn,
99
- Category AS cat
100
- INNER JOIN Item AS itm USING (category_id)
101
- {itemJoin} JOIN StationItem AS si
102
- ON (si.station_id = stn.station_id
103
- AND si.item_id = itm.item_id)
104
- {stationWhere}
105
- ORDER BY stn.station_id, cat.name, itm.ui_order
106
- """
107
-
108
- sql = stmt.format(
109
- stationWhere=stationWhere,
110
- defDemand=defaultDemandVal,
111
- itemJoin=itemJoin,
75
+
76
+ # Build the main query
77
+ q = (
78
+ session.query(
79
+ SA.StationItem.station_id,
80
+ SA.Item.item_id,
81
+ SA.StationItem.demand_price,
82
+ SA.StationItem.supply_price,
83
+ SA.StationItem.demand_units,
84
+ SA.StationItem.demand_level,
85
+ SA.StationItem.supply_units,
86
+ SA.StationItem.supply_level,
87
+ SA.StationItem.modified,
88
+ SA.Item.name,
89
+ SA.Item.category_id,
90
+ SA.Category.name.label("category_name"),
91
+ SA.Station.name.label("station_name"),
92
+ SA.System.name.label("system_name"),
93
+ )
94
+ .join(SA.Item, SA.Item.item_id == SA.StationItem.item_id)
95
+ .join(SA.Category, SA.Category.category_id == SA.Item.category_id)
96
+ .join(SA.Station, SA.Station.station_id == SA.StationItem.station_id)
97
+ .join(SA.System, SA.System.system_id == SA.Station.system_id)
98
+ .order_by(SA.Station.station_id, SA.Category.name, SA.Item.ui_order)
112
99
  )
113
- if debug:
114
- print(sql)
115
- cur.execute(sql)
116
-
117
- lastStn, lastCat = None, None
118
-
100
+
101
+ if stationID:
102
+ q = q.filter(SA.StationItem.station_id == stationID)
103
+
104
+ # Set up output
119
105
  if not file:
120
106
  file = sys.stdout
121
-
107
+
122
108
  if stationID:
123
109
  stationSet = str(stations[stationID])
124
110
  else:
125
111
  stationSet = "ALL Systems/Stations"
126
-
112
+
127
113
  file.write(
128
114
  "# TradeDangerous prices for {}\n"
129
115
  "\n"
130
116
  "# REMOVE ITEMS THAT DON'T APPEAR IN THE UI\n"
131
117
  "# ORDER IS REMEMBERED: Move items around within categories "
132
- "to match the game UI\n"
118
+ "to match the game UI\n"
133
119
  "\n"
134
120
  "# File syntax:\n"
135
121
  "# <item name> <sell> <buy> [<demand> <supply> [<timestamp>]]\n"
@@ -138,15 +124,14 @@ def dumpPrices(
138
124
  "# Otherwise use a number followed by L, M or H, e.g.\n"
139
125
  "# 1L, 23M or 30000H\n"
140
126
  "# If you omit the timestamp, the current time will be used when "
141
- "the file is loaded.\n"
142
- "\n".format(
143
- stationSet
144
- ))
145
-
127
+ "the file is loaded.\n"
128
+ "\n".format(stationSet)
129
+ )
130
+
146
131
  levelDesc = "?0LMH"
147
132
  maxCrWidth = 7
148
133
  levelWidth = 9
149
-
134
+
150
135
  outFmt = (
151
136
  " {{:<{width}}}"
152
137
  " {{:>{crwidth}}}"
@@ -161,71 +146,68 @@ def dumpPrices(
161
146
  if withTimes:
162
147
  outFmt += " {}"
163
148
  outFmt += "\n"
164
- output = outFmt.format(
149
+ header = outFmt.format(
165
150
  "Item Name",
166
151
  "SellCr", "BuyCr",
167
152
  "Demand", "Supply",
168
153
  "Timestamp",
169
154
  )
170
- file.write('#' + output[1:])
171
-
155
+ file.write('#' + header[1:])
156
+
172
157
  naIQL = "-"
173
158
  unkIQL = "?"
174
159
  defIQL = "?" if not defaultZero else "-"
175
-
160
+
161
+ # Main loop — stream results instead of preloading
176
162
  output = ""
177
- for (stnID, itemID, fromStn, toStn, demand, demandLevel, supply, supplyLevel, modified) in cur:
178
- modified = modified or now
179
- station, system = stations[stnID]
180
- item, catID, category = items[itemID]
163
+ lastStn, lastCat = None, None
164
+ for row in q.yield_per(1000).execution_options(stream_results=True):
165
+ stnID = row.station_id
166
+ itemID = row.item_id
167
+ station = row.station_name
168
+ system = row.system_name
169
+ item = row.name
170
+ catID = row.category_id
171
+ category = row.category_name
172
+
173
+ # Guard against bad system names
174
+ if not system:
175
+ raise TradeException(
176
+ f"Station {station} (ID {stnID}) is linked to a system with no name."
177
+ )
178
+
181
179
  if stnID != lastStn:
182
180
  file.write(output)
183
- output = "\n\n@ {}/{}\n".format(system.upper(), station)
181
+ output = f"\n\n@ {system.upper()}/{station}\n"
184
182
  lastStn = stnID
185
183
  lastCat = None
186
-
187
- if catID is not lastCat:
188
- output += " + {}\n".format(category)
184
+
185
+ if catID != lastCat:
186
+ output += f" + {category}\n"
189
187
  lastCat = catID
188
+
189
+ demandCr = row.demand_price or 0
190
+ supplyCr = row.supply_price or 0
191
+ demandUnits = row.demand_units or defaultDemandVal
192
+ demandLevel = row.demand_level or defaultDemandVal
193
+ supplyUnits = row.supply_units or defaultDemandVal
194
+ supplyLevel = row.supply_level or defaultDemandVal
190
195
 
191
- # Is this item on sale?
192
- if toStn > 0:
193
- # Zero demand-price gets default demand, which will
194
- # be either unknown or zero depending on -0.
195
- # If there is a price, always default to unknown
196
- # because it can be sold here but the demand is just
197
- # not useful as data.
198
- demandStr = defIQL if fromStn <= 0 else unkIQL
199
- if supplyLevel == 0:
200
- supplyStr = naIQL
201
- elif supplyLevel < 0 and supply <= 0:
202
- supplyStr = defIQL
203
- else:
204
- units = "?" if supply < 0 else str(supply)
205
- level = levelDesc[supplyLevel + 1]
206
- supplyStr = units + level
196
+ # Demand/supply formatting
197
+ if supplyCr > 0:
198
+ demandStr = defIQL if demandCr <= 0 else unkIQL
199
+ supplyStr = (
200
+ naIQL if supplyLevel == 0
201
+ else (f"{supplyUnits if supplyUnits >= 0 else '?'}{levelDesc[supplyLevel+1]}")
202
+ )
207
203
  else:
208
- if fromStn == 0 or demandLevel == 0:
209
- demandStr = naIQL
210
- elif demandLevel < 0 and demand <= 0:
211
- demandStr = defIQL
212
- else:
213
- units = "?" if demand < 0 else str(demand)
214
- level = levelDesc[demandLevel + 1]
215
- demandStr = units + level
204
+ demandStr = (
205
+ naIQL if demandCr == 0 or demandLevel == 0
206
+ else (f"{demandUnits if demandUnits >= 0 else '?'}{levelDesc[demandLevel+1]}")
207
+ )
216
208
  supplyStr = naIQL
217
- output += outFmt.format(
218
- item,
219
- fromStn, toStn,
220
- demandStr, supplyStr,
221
- modified
222
- )
223
-
224
- file.write(output)
225
209
 
210
+ modified = row.modified or ""
211
+ output += outFmt.format(item, demandCr, supplyCr, demandStr, supplyStr, modified)
226
212
 
227
- # if __name__ == "__main__":
228
- # import tradedb
229
- #
230
- # tdb = tradedb.TradeDB(load=False)
231
- # dumpPrices(tdb.dbPath, elementMask=Element.full)
213
+ file.write(output)
@@ -44,7 +44,7 @@ CREATE TABLE Added
44
44
 
45
45
  CREATE TABLE System
46
46
  (
47
- system_id INTEGER PRIMARY KEY,
47
+ system_id BIGINT PRIMARY KEY,
48
48
  name VARCHAR(40) COLLATE nocase,
49
49
  pos_x DOUBLE NOT NULL,
50
50
  pos_y DOUBLE NOT NULL,
@@ -63,9 +63,9 @@ CREATE INDEX idx_system_by_pos ON System (pos_x, pos_y, pos_z, system_id);
63
63
 
64
64
  CREATE TABLE Station
65
65
  (
66
- station_id INTEGER PRIMARY KEY,
66
+ station_id BIGINT PRIMARY KEY,
67
67
  name VARCHAR(40) COLLATE nocase,
68
- system_id INTEGER NOT NULL,
68
+ system_id BIGINT NOT NULL,
69
69
  ls_from_star INTEGER NOT NULL DEFAULT 0
70
70
  CHECK (ls_from_star >= 0),
71
71
  blackmarket TEXT(1) NOT NULL DEFAULT '?'
@@ -111,7 +111,7 @@ CREATE TABLE Ship
111
111
  CREATE TABLE ShipVendor
112
112
  (
113
113
  ship_id INTEGER NOT NULL,
114
- station_id INTEGER NOT NULL,
114
+ station_id BIGINT NOT NULL,
115
115
  modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
116
116
 
117
117
  PRIMARY KEY (ship_id, station_id),
@@ -141,7 +141,7 @@ CREATE TABLE Upgrade
141
141
  CREATE TABLE UpgradeVendor
142
142
  (
143
143
  upgrade_id INTEGER NOT NULL,
144
- station_id INTEGER NOT NULL,
144
+ station_id BIGINT NOT NULL,
145
145
  modified DATETIME NOT NULL,
146
146
 
147
147
  PRIMARY KEY (upgrade_id, station_id),
@@ -209,7 +209,7 @@ CREATE TABLE Item
209
209
 
210
210
  CREATE TABLE StationItem
211
211
  (
212
- station_id INTEGER NOT NULL,
212
+ station_id BIGINT NOT NULL,
213
213
  item_id INTEGER NOT NULL,
214
214
  demand_price INT NOT NULL,
215
215
  demand_units INT NOT NULL,