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
@@ -4,6 +4,8 @@ from .parsing import (
4
4
  ParseArgument, MutuallyExclusiveGroup,
5
5
  )
6
6
  from ..formatting import RowFormat
7
+ from sqlalchemy import select, table, column
8
+ from sqlalchemy.orm import Session
7
9
 
8
10
 
9
11
  ######################################################################
@@ -50,35 +52,63 @@ def render_units(units, level):
50
52
 
51
53
 
52
54
  def run(results, cmdenv, tdb):
55
+ # Lazy import to avoid any import-time tangles elsewhere.
56
+ from tradedangerous.db.utils import age_in_days
57
+
53
58
  origin = cmdenv.startStation
54
59
  if not origin.itemCount:
55
60
  raise CommandLineError(
56
61
  "No trade data available for {}".format(origin.name())
57
62
  )
58
-
63
+
59
64
  buying, selling = cmdenv.buying, cmdenv.selling
60
-
65
+
61
66
  results.summary = ResultRow()
62
67
  results.summary.origin = origin
63
68
  results.summary.buying = cmdenv.buying
64
69
  results.summary.selling = cmdenv.selling
65
-
70
+
71
+ # Precompute averages (unchanged)
66
72
  tdb.getAverageSelling()
67
73
  tdb.getAverageBuying()
68
- cur = tdb.query("""
69
- SELECT item_id,
70
- demand_price, demand_units, demand_level,
71
- supply_price, supply_units, supply_level,
72
- JULIANDAY('now') - JULIANDAY(modified)
73
- FROM StationItem
74
- WHERE station_id = ?
75
- """, [origin.ID])
76
-
77
- for row in cur:
78
- it = iter(row)
74
+
75
+ # --- Backend-neutral query using SQLAlchemy Core + age_in_days ---
76
+ si = table(
77
+ "StationItem",
78
+ column("item_id"),
79
+ column("station_id"),
80
+ column("demand_price"),
81
+ column("demand_units"),
82
+ column("demand_level"),
83
+ column("supply_price"),
84
+ column("supply_units"),
85
+ column("supply_level"),
86
+ column("modified"),
87
+ )
88
+
89
+ # Build session bound to current engine (needed by age_in_days)
90
+ session = Session(bind=tdb.engine)
91
+
92
+ stmt = (
93
+ select(
94
+ si.c.item_id,
95
+ si.c.demand_price, si.c.demand_units, si.c.demand_level,
96
+ si.c.supply_price, si.c.supply_units, si.c.supply_level,
97
+ age_in_days(session, si.c.modified).label("age_days"),
98
+ )
99
+ .where(si.c.station_id == origin.ID)
100
+ )
101
+
102
+ rows = session.execute(stmt).fetchall()
103
+ session.close()
104
+
105
+ for r in rows:
106
+ it = iter(r)
79
107
  item = tdb.itemByID[next(it)]
108
+
80
109
  row = ResultRow()
81
110
  row.item = item
111
+
82
112
  row.buyCr = int(next(it) or 0)
83
113
  row.avgBuy = tdb.avgBuying.get(item.ID, 0)
84
114
  units, level = int(next(it) or 0), int(next(it) or 0)
@@ -89,6 +119,7 @@ def run(results, cmdenv, tdb):
89
119
  hasBuy = (row.buyCr or units or level)
90
120
  else:
91
121
  hasBuy = False
122
+
92
123
  row.sellCr = int(next(it) or 0)
93
124
  row.avgSell = tdb.avgSelling.get(item.ID, 0)
94
125
  units, level = int(next(it) or 0), int(next(it) or 0)
@@ -99,17 +130,19 @@ def run(results, cmdenv, tdb):
99
130
  hasSell = (row.sellCr or units or level)
100
131
  else:
101
132
  hasSell = False
102
- row.age = float(next(it) or 0)
103
-
133
+
134
+ age_days = next(it)
135
+ row.age = float(age_days or 0.0)
136
+
104
137
  if hasBuy or hasSell:
105
138
  results.rows.append(row)
106
-
139
+
107
140
  if not results.rows:
108
141
  raise CommandLineError("No items found")
109
-
142
+
110
143
  results.rows.sort(key=lambda row: row.item.dbname)
111
144
  results.rows.sort(key=lambda row: row.item.category.dbname)
112
-
145
+
113
146
  return results
114
147
 
115
148
  #######################################################################
@@ -4,6 +4,8 @@ from .parsing import (
4
4
  )
5
5
  from ..tradedb import TradeDB
6
6
  from ..tradeexcept import TradeException
7
+ from sqlalchemy import select, table, column, func, literal
8
+ from sqlalchemy.orm import Session
7
9
 
8
10
 
9
11
  ######################################################################
@@ -61,150 +63,161 @@ switches = [
61
63
  # Perform query and populate result set
62
64
 
63
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
+ """
64
74
  from .commandenv import ResultRow
65
-
75
+ from tradedangerous.db.utils import age_in_days
76
+
66
77
  cmdenv = results.cmdenv
67
78
  tdb = cmdenv.tdb
68
-
79
+
69
80
  results.summary = ResultRow()
70
81
  results.limit = cmdenv.limit
71
-
72
- fields = [
73
- "si.station_id",
74
- "JULIANDAY('NOW') - JULIANDAY(MAX(si.modified))",
75
- "stn.ls_from_star",
76
- ]
77
-
78
- joins = []
79
- wheres = []
80
- havings = []
81
-
82
- if cmdenv.minAge:
83
- wheres.append(
84
- "(JULIANDAY('NOW') - JULIANDAY(si.modified) >= {})"
85
- .format(cmdenv.minAge)
86
- )
87
-
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²
88
111
  nearSys = cmdenv.nearSystem
112
+ join_sys = False
89
113
  if nearSys:
90
- maxLy = cmdenv.maxLyPer or tdb.maxSystemLinkLy
91
- maxLy2 = maxLy ** 2
92
- fields.append(
93
- "dist2("
94
- "sys.pos_x, sys.pos_y, sys.pos_z,"
95
- "{}, {}, {}"
96
- ") AS d2".format(
97
- nearSys.posX,
98
- nearSys.posY,
99
- nearSys.posZ,
100
- ))
101
- joins.append("INNER JOIN System sys USING (system_id)")
102
- wheres.append("""(
103
- sys.pos_x BETWEEN {} and {}
104
- AND sys.pos_y BETWEEN {} and {}
105
- AND sys.pos_z BETWEEN {} and {}
106
- )""".format(
107
- nearSys.posX - maxLy,
108
- nearSys.posX + maxLy,
109
- nearSys.posY - maxLy,
110
- nearSys.posY + maxLy,
111
- nearSys.posZ - maxLy,
112
- nearSys.posZ + maxLy,
113
- ))
114
- havings.append("d2 <= {}".format(maxLy2))
114
+ dx = (sys_tbl.c.pos_x - literal(nearSys.posX))
115
+ dy = (sys_tbl.c.pos_y - literal(nearSys.posY))
116
+ dz = (sys_tbl.c.pos_z - literal(nearSys.posZ))
117
+ dist2_expr = (dx * dx + dy * dy + dz * dz).label("d2")
115
118
  else:
116
- fields.append("0")
117
-
118
- fieldStr = ','.join(fields)
119
-
120
- if joins:
121
- joinStr = ' '.join(joins)
122
- else:
123
- joinStr = ''
124
-
125
- if wheres:
126
- whereStr = 'WHERE ' + ' AND '.join(wheres)
127
- else:
128
- whereStr = ''
129
-
130
- if havings:
131
- haveStr = 'HAVING ' + ' AND '.join(havings)
132
- else:
133
- haveStr = ''
134
-
135
- stmt = """
136
- SELECT {fields}
137
- FROM StationItem as si
138
- INNER JOIN Station stn USING (station_id)
139
- {joins}
140
- {wheres}
141
- GROUP BY 1
142
- {having}
143
- ORDER BY 2 DESC
144
- """.format(
145
- fields=fieldStr,
146
- joins=joinStr,
147
- wheres=whereStr,
148
- having=haveStr,
119
+ dist2_expr = literal(0.0).label("d2")
120
+
121
+ stmt = (
122
+ select(
123
+ si.c.station_id,
124
+ age_expr,
125
+ stn.c.ls_from_star,
126
+ dist2_expr,
127
+ )
128
+ .select_from(
129
+ si.join(stn, stn.c.station_id == si.c.station_id)
130
+ .join(sys_tbl, sys_tbl.c.system_id == stn.c.system_id) if nearSys
131
+ else si.join(stn, stn.c.station_id == si.c.station_id)
132
+ )
133
+ .group_by(si.c.station_id, stn.c.ls_from_star, dist2_expr)
134
+ .order_by(age_expr.desc())
149
135
  )
150
-
151
- cmdenv.DEBUG1(stmt)
152
-
136
+
137
+ # Bounding box for near (keeps scan small, mirrors original)
138
+ if nearSys:
139
+ maxLy = cmdenv.maxLyPer or tdb.maxSystemLinkLy
140
+ # Bounding box predicate
141
+ stmt = stmt.where(
142
+ sys_tbl.c.pos_x.between(nearSys.posX - maxLy, nearSys.posX + maxLy),
143
+ sys_tbl.c.pos_y.between(nearSys.posY - maxLy, nearSys.posY + maxLy),
144
+ sys_tbl.c.pos_z.between(nearSys.posZ - maxLy, nearSys.posZ + maxLy),
145
+ )
146
+ # Radius filter: HAVING dist2 <= maxLy^2
147
+ stmt = stmt.having(dist2_expr <= (maxLy * maxLy))
148
+
149
+ # Min-age filter (apply to aggregated age of MAX(modified))
150
+ if cmdenv.minAge:
151
+ stmt = stmt.having(age_expr >= float(cmdenv.minAge))
152
+
153
+ # Execute and materialize rows
154
+ rows = session.execute(stmt).fetchall()
155
+ session.close()
156
+
157
+ # Downstream filters (unchanged)
153
158
  padSize = cmdenv.padSize
154
159
  planetary = cmdenv.planetary
155
160
  fleet = cmdenv.fleet
156
161
  odyssey = cmdenv.odyssey
157
162
  noPlanet = cmdenv.noPlanet
158
163
  mls = cmdenv.maxLs
159
-
160
- for (stnID, age, ls, dist2) in tdb.query(stmt):
164
+
165
+ for (stnID, age, ls, dist2) in rows:
161
166
  cmdenv.DEBUG2("{}:{}:{}", stnID, age, ls)
162
167
  row = ResultRow()
163
168
  row.station = tdb.stationByID[stnID]
164
- row.age = age
165
- if ls:
166
- row.ls = "{:n}".format(ls)
167
- else:
168
- row.ls = "?"
169
- row.dist = dist2 ** 0.5
170
- if not padSize or row.station.checkPadSize(padSize):
171
- if not planetary or row.station.checkPlanetary(planetary):
172
- if not fleet or row.station.checkFleet(fleet):
173
- if not odyssey or row.station.checkOdyssey(odyssey):
174
- if not noPlanet or row.station.planetary == 'N':
175
- if not mls or row.station.lsFromStar <= mls:
176
- results.rows.append(row)
177
-
169
+ row.age = float(age or 0.0)
170
+ row.ls = "{:n}".format(ls) if ls else "?"
171
+ row.dist = (float(dist2) ** 0.5) if dist2 else 0.0
172
+
173
+ if padSize and not row.station.checkPadSize(padSize):
174
+ continue
175
+ if planetary and not row.station.checkPlanetary(planetary):
176
+ continue
177
+ if fleet and not row.station.checkFleet(fleet):
178
+ continue
179
+ if odyssey and not row.station.checkOdyssey(odyssey):
180
+ continue
181
+ if noPlanet and row.station.planetary != 'N':
182
+ continue
183
+ if mls and row.station.lsFromStar > mls:
184
+ continue
185
+
186
+ results.rows.append(row)
187
+
188
+ # Route optimization and limiting (unchanged)
178
189
  if cmdenv.route and len(results.rows) > 1:
179
- def walk(startNode, dist):
180
- rows = results.rows
181
- startNode = rows[startNode]
182
- openList = set(rows)
190
+ def walk(start_idx, dist):
191
+ rows_ = results.rows
192
+ startNode = rows_[start_idx]
193
+ openList = set(rows_)
183
194
  path = [startNode]
184
195
  openList.remove(startNode)
185
- while len(path) < len(rows):
196
+ while len(path) < len(rows_):
186
197
  lastNode = path[-1]
187
198
  distFn = lastNode.station.system.distanceTo
188
- nearest = min(openList, key=lambda row: distFn(row.station.system))
199
+ nearest = min(openList, key=lambda r: distFn(r.station.system))
189
200
  openList.remove(nearest)
190
201
  path.append(nearest)
191
202
  dist += distFn(nearest.station.system)
192
203
  return (path, dist)
204
+
193
205
  if cmdenv.near:
194
206
  bestPath = walk(0, results.rows[0].dist)
195
207
  else:
196
208
  bestPath = (results.rows, float("inf"))
197
209
  for i in range(len(results.rows)):
198
- path = walk(i, 0)
199
- if path[1] < bestPath[1]:
200
- bestPath = path
210
+ candidate = walk(i, 0)
211
+ if candidate[1] < bestPath[1]:
212
+ bestPath = candidate
201
213
  results.rows[:] = bestPath[0]
202
-
214
+
203
215
  if cmdenv.limit:
204
216
  results.rows[:] = results.rows[:cmdenv.limit]
205
-
217
+
206
218
  return results
207
219
 
220
+
208
221
  ######################################################################
209
222
  # Transform result set into output
210
223