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
@@ -1,497 +1,67 @@
1
- # Deprecated
2
- from .commandenv import ResultRow
3
- from .exceptions import CommandLineError
4
- from .parsing import MutuallyExclusiveGroup, ParseArgument
5
- from ..tradedb import AmbiguityError
6
- from ..tradedb import Station
7
- from ..tradedb import TradeDB
8
- from .. import utils
9
- from ..formatting import max_len
10
-
11
- from .. import cache
12
- from .. import csvexport
13
- import difflib
14
- import re
15
-
16
-
17
- ######################################################################
18
- # Parser config
19
-
20
- help='Add (or update) a station entry'
21
- name='station'
22
- epilog=None
23
- arguments = [
24
- ParseArgument(
25
- 'station',
26
- help='Specify the full name of the station (SYS NAME/STN NAME is also supported).',
27
- type=str,
28
- nargs='+',
29
- ),
30
- ]
31
- switches = [
32
- MutuallyExclusiveGroup(
33
- ParseArgument(
34
- '--update', '-u',
35
- help='Indicates you expect the entry to already exist.',
36
- action='store_true',
37
- ),
38
- ParseArgument(
39
- '--remove', '-rm',
40
- help='Indicates you want to remove an entry.',
41
- action='store_true',
42
- ),
43
- ParseArgument(
44
- '--add', '-a',
45
- help='Indicates you want to add a new station.',
46
- action='store_true',
47
- ),
48
- ),
49
- ParseArgument(
50
- '--ls-from-star',
51
- help='Number of light seconds between station and star.',
52
- type=int,
53
- dest='lsFromStar',
54
- ),
55
- # Note: these are not the usual arguments, they're asking the
56
- # user to assign rather than select values.
57
- ParseArgument(
58
- '--black-market', '--bm',
59
- help='Does the station have a black market (Y or N) or ? if unknown.',
60
- choices=['Y', 'y', 'N', 'n', '?'],
61
- dest='blackMarket',
62
- ),
63
- ParseArgument(
64
- '--market',
65
- help='Does the station have a commodities market (Y or N), ? for unknown.',
66
- choices=['Y', 'y', 'N', 'n', '?'],
67
- ),
68
- ParseArgument(
69
- '--shipyard',
70
- help='Does the station have a shipyard (Y or N) or ? if unknown.',
71
- choices=['Y', 'y', 'N', 'n', '?'],
72
- ),
73
- ParseArgument(
74
- '--pad-size',
75
- help='Maximum supported pad size (S, M, or L) or ? if unknown.',
76
- choices=['S', 's', 'M', 'm', 'L', 'l', '?'],
77
- dest='padSize',
78
- ),
79
- ParseArgument(
80
- '--outfitting',
81
- help='Does the station provide outfitting (Y or N) or ? if unknown.',
82
- choices=['Y', 'y', 'N', 'n', '?'],
83
- ),
84
- ParseArgument(
85
- '--rearm', '--arm',
86
- help='Does the station provide rearming (Y or N) or ? if unknown.',
87
- choices=['Y', 'y', 'N', 'n', '?'],
88
- ),
89
- ParseArgument(
90
- '--refuel',
91
- help='Does the station provide refueling (Y or N) or ? if unknown.',
92
- choices=['Y', 'y', 'N', 'n', '?'],
93
- ),
94
- ParseArgument(
95
- '--repair',
96
- help='Does the station provide repairs (Y or N) or ? if unknown.',
97
- choices=['Y', 'y', 'N', 'n', '?'],
98
- ),
99
- ParseArgument(
100
- '--planetary',
101
- help='Is the station on a planet (Y or N) or ? if unknown.',
102
- choices=['Y', 'y', 'N', 'n', '?'],
103
- ),
1
+ # tradedangerous/commands/station_cmd.py — DEPRECATED (no-op)
2
+ # This command no longer edits station records directly.
3
+ #
4
+ # How to maintain station data going forward:
5
+ # • Import authoritative data via: trade import -P spansh | trade import -P eddblink
6
+ # • Solo/offline players (manual capture via EDMC plugin):
7
+ # TradeDangerous DB-Update for EDMC → https://github.com/bgol/UpdateTD
8
+ #
9
+ # To inspect a station’s data, use read-only commands like:
10
+ # trade station <name> ← (legacy pattern; now deprecated)
11
+ # trade local --near "<system>" (discoverability)
12
+ # trade market --origin "<station>" (market view)
13
+
14
+ from __future__ import annotations
15
+ from .parsing import ParseArgument
16
+
17
+ # ---- Command metadata ----
18
+ help = "DEPRECATED: no longer used. See deprecation banner when run."
19
+ name = "station"
20
+ epilog = None
21
+ acceptUnknown = True
22
+
23
+ # No DB access is needed for this no-op command.
24
+ wantsTradeDB = False
25
+ usesTradeData = False
26
+
27
+ # Accept ANY number of positional args and ignore them (prevents parser errors).
28
+ arguments = (
104
29
  ParseArgument(
105
- '--fleet-carrier', '--fc',
106
- dest='fleet',
107
- help='Is the station a Fleet Carrier (Y or N) or ? if unknown.',
108
- choices=['Y', 'y', 'N', 'n', '?'],
109
- ),
110
- ParseArgument(
111
- '--odyssey', '--od',
112
- help='Is the station an Odyssey Settlement (Y or N) or ? if unknown.',
113
- choices=['Y', 'y', 'N', 'n', '?'],
114
- ),
115
- ParseArgument(
116
- '--confirm',
117
- help='For confirmation suspicious looking station names.',
118
- metavar='CONFIRMATION CODE',
30
+ 'args',
31
+ help="(deprecated) ignored",
32
+ nargs='*',
119
33
  type=str,
120
34
  ),
121
- ParseArgument(
122
- '--no-export',
123
- help='Do not update the .csv files.',
124
- action='store_true',
125
- dest='noExport',
35
+ )
36
+ # No switches; unknown switches will still be rejected by the global parser.
37
+ switches = tuple()
38
+
39
+
40
+ def _banner() -> str:
41
+ return (
42
+ "\n"
43
+ "=== DEPRECATION NOTICE: station ====================================\n"
44
+ "This command no longer edits station records and does not modify the DB.\n"
45
+ "• Import station data via: trade import -P eddblink | -P spansh\n"
46
+ "• Solo/offline capture via EDMC plugin:\n"
47
+ " TradeDangerous DB-Update → https://github.com/bgol/UpdateTD\n"
48
+ "=====================================================================\n"
126
49
  )
127
- ]
128
50
 
129
- ######################################################################
130
- # Helpers
131
51
 
132
- def makeConfirmationCode(base, candidates):
52
+ def run(results, cmdenv, tdb=None):
133
53
  """
134
- Makes a four-digit hex checksum of a station list.
54
+ No-op implementation: print banner and exit immediately.
55
+ All arguments/switches are ignored by design.
135
56
  """
136
- checksum = base
137
- for cand in candidates:
138
- for character in cand:
139
- checksum <<= 4
140
- checksum += ord(character)
141
-
142
- # python integers don't overflow, so we only need
143
- # to modulo at the end of the checksum.
144
- checksum %= 65521 # arbitrary prime < 2^32
145
-
146
- return hex(checksum).upper()[2:]
147
-
148
-
149
- def checkStationDoesNotExist(tdb, cmdenv, system, stationName):
150
- if not system.stations:
151
- return
152
-
153
- upperName = stationName.upper()
154
- similarities = set()
57
+ banner = _banner()
155
58
  try:
156
- station = tdb.lookupStation(stationName, system)
157
- if station.dbname.upper() == upperName:
158
- raise CommandLineError(
159
- "Station \"{}\" "
160
- "in system \"{}\" "
161
- "already exists.".format(
162
- stationName, system.name(),
163
- ))
164
- similarities.add(station.dbname.upper())
165
- except LookupError:
166
- pass
167
- except AmbiguityError as e:
168
- for cand in e.anyMatch:
169
- similarities.add(e.key(cand).upper())
170
-
171
- # Check to see if there are stations with somewhat
172
- # similar names, but allow the user to get around
173
- # cases where difflib matches 'X Port' to 'Y Port'.
174
- stationNames = [
175
- stn.dbname.upper()
176
- for stn in system.stations
177
- ]
178
- cmdenv.DEBUG0("Comparing {} to {}".format(
179
- upperName, list(stationNames),
180
- ))
181
- candidates = difflib.get_close_matches(
182
- upperName, stationNames, cutoff=0.6,
183
- )
184
- for cand in candidates:
185
- similarities.add(cand)
186
-
187
- if not similarities:
188
- return
189
-
190
- confCode = makeConfirmationCode(system.ID, similarities)
191
-
192
- if not cmdenv.confirm:
193
- raise CommandLineError(
194
- "\"{}\" contains similar station names:\n"
195
- " {}\n"
196
- "\n"
197
- "If you want to add this station anyway, re-run the "
198
- "command and add:\n"
199
- " --conf {}".format(
200
- system.name(),
201
- ', '.join(candidates),
202
- confCode
203
- ))
204
-
205
- if cmdenv.confirm.upper() != confCode:
206
- raise CommandLineError(
207
- "Wrong confirmation code."
208
- )
209
-
210
- cmdenv.NOTE("Confirmation code accepted.")
211
-
212
-
213
- def checkSystemAndStation(tdb, cmdenv):
214
- # In add mode, the user has to be more specific.
215
- stnName = ' '.join(cmdenv.station).strip()
216
-
217
- if not cmdenv.add:
218
- try:
219
- station = tdb.lookupPlace(stnName)
220
- except LookupError:
221
- raise CommandLineError("Unrecognized Station: {}".format(
222
- cmdenv.station
223
- ))
224
- if not isinstance(station, Station):
225
- raise CommandLineError(
226
- "Expecting a STATION, got {}".format(stnName)
227
- )
228
- cmdenv.system = station.system.name()
229
- cmdenv.station = station.dbname
230
-
231
- return station.system, station
232
-
233
- # Clean up the station name and potentially lift the system
234
- # name out of it.
235
- stnName = re.sub(r" +", " ", stnName)
236
- stnName = re.sub(r"[ /]*/[ /]*", "/", stnName)
237
- while stnName.startswith('/'):
238
- stnName = stnName[1:]
239
- slashPos = stnName.find('/')
240
- if slashPos > 0:
241
- sysName, stnName = stnName[:slashPos], stnName[slashPos+1:]
242
- sysName = sysName.upper()
243
- else:
244
- sysName = None
245
-
246
- if not stnName:
247
- raise CommandLineError("Invalid station name: {stnName}")
248
-
249
- if not sysName:
250
- raise CommandLineError("No system name specified")
251
-
252
- cmdenv.system, cmdenv.station = sysName, utils.titleFixup(stnName)
253
- try:
254
- system = tdb.lookupSystem(sysName)
255
- except LookupError:
256
- raise CommandLineError(
257
- "Unknown SYSTEM name: \"{}\"".format(
258
- sysName
259
- ))
260
-
261
- # check the station does not exist
262
- checkStationDoesNotExist(tdb, cmdenv, system, stnName)
263
-
264
- return system, None
265
-
266
-
267
- def addStation(tdb, cmdenv, system, stationName):
268
- return tdb.addLocalStation(
269
- system=system,
270
- name=stationName,
271
- lsFromStar=cmdenv.lsFromStar or 0,
272
- market=cmdenv.market or '?',
273
- blackMarket=cmdenv.blackMarket or '?',
274
- shipyard=cmdenv.shipyard or '?',
275
- outfitting=cmdenv.outfitting or '?',
276
- rearm=cmdenv.rearm or '?',
277
- refuel=cmdenv.refuel or '?',
278
- repair=cmdenv.repair or '?',
279
- maxPadSize=cmdenv.padSize or '?',
280
- planetary=cmdenv.planetary or '?',
281
- fleet=cmdenv.fleet or '?',
282
- odyssey=cmdenv.odyssey or '?',
283
- commit=True,
284
- )
285
-
286
-
287
- def updateStation(tdb, cmdenv, station):
288
- return tdb.updateLocalStation(
289
- station=station,
290
- lsFromStar=cmdenv.lsFromStar,
291
- market=cmdenv.market,
292
- blackMarket=cmdenv.blackMarket,
293
- shipyard=cmdenv.shipyard,
294
- outfitting=cmdenv.outfitting,
295
- rearm=cmdenv.rearm,
296
- refuel=cmdenv.refuel,
297
- repair=cmdenv.repair,
298
- maxPadSize=cmdenv.padSize,
299
- planetary=cmdenv.planetary,
300
- fleet=cmdenv.fleet,
301
- odyssey=cmdenv.odyssey,
302
- force=True,
303
- commit=True,
304
- )
305
-
306
-
307
- def removeStation(tdb, cmdenv, station):
308
- db = tdb.getDB()
309
- db.execute("""
310
- DELETE FROM Station WHERE station_id = ?
311
- """, [station.ID])
312
- db.commit()
313
- cmdenv.NOTE("{} (#{}) removed from {} database.",
314
- station.name(), station.ID, tdb.dbPath)
315
- cmdenv.stationItemCount = station.itemCount
316
- return True
317
-
318
-
319
- def checkResultAndExportStations(tdb, cmdenv, result):
320
- if not result:
321
- cmdenv.NOTE("No changes.")
322
- return None
323
- if cmdenv.noExport:
324
- cmdenv.DEBUG0("no-export set, not exporting stations")
325
- return None
326
-
327
- lines, csvPath = csvexport.exportTableToFile(tdb, cmdenv, "Station")
328
- cmdenv.NOTE("{} updated.", csvPath)
329
-
330
- if cmdenv.remove:
331
- if cmdenv.stationItemCount:
332
- cmdenv.NOTE("Station had items, regenerating .prices file")
333
- cache.regeneratePricesFile(tdb, cmdenv)
334
-
59
+ cmdenv.NOTE("{}", banner)
60
+ except Exception:
61
+ print(banner)
335
62
  return None
336
63
 
337
64
 
338
- ######################################################################
339
- # Perform query and populate result set
340
-
341
- def run(results, cmdenv, tdb):
342
- if cmdenv.lsFromStar and cmdenv.lsFromStar < 0:
343
- raise CommandLineError("Invalid (negative) --ls option")
344
-
345
- system, station = checkSystemAndStation(tdb, cmdenv)
346
-
347
- stationName = cmdenv.station
348
-
349
- if cmdenv.add:
350
- result = addStation(tdb, cmdenv, system, stationName)
351
- return checkResultAndExportStations(tdb, cmdenv, result)
352
- elif cmdenv.update:
353
- result = updateStation(tdb, cmdenv, station)
354
- return checkResultAndExportStations(tdb, cmdenv, result)
355
- elif cmdenv.remove:
356
- result = removeStation(tdb, cmdenv, station)
357
- return checkResultAndExportStations(tdb, cmdenv, result)
358
-
359
- # Otherwise, it's just a query
360
- results.summary = ResultRow()
361
- results.summary.system = station.system
362
- results.summary.station = station
363
-
364
- avgSell = results.summary.avgSelling = tdb.getAverageSelling()
365
- avgBuy = results.summary.avgBuying = tdb.getAverageBuying()
366
-
367
- class ItemTrade:
368
- def __init__(self, ID, price, avgAgainst):
369
- self.ID, self.item = ID, tdb.itemByID[ID]
370
- self.price = int(price)
371
- self.avgTrade = avgAgainst.get(ID, 0)
372
-
373
- # Look up all selling and buying by the station
374
- selling, buying = [], []
375
- cur = tdb.query("""
376
- SELECT item_id, demand_price, supply_price
377
- FROM StationItem
378
- WHERE station_id = ?
379
- AND (demand_price > 10 or supply_price > 10)
380
- """, [station.ID])
381
- for ID, demand_price, supply_price in cur:
382
- if demand_price > 10 and avgSell.get(ID, 0) > 10:
383
- buying.append(ItemTrade(ID, demand_price, avgSell))
384
- if supply_price > 10 and avgBuy.get(ID, 0) > 10:
385
- selling.append(ItemTrade(ID, supply_price, avgBuy))
386
- selling.sort(
387
- key=lambda item: item.price - item.avgTrade,
388
- )
389
- results.summary.selling = selling[:5]
390
- buying.sort(
391
- key=lambda item: item.avgTrade - item.price,
392
- )
393
- results.summary.buying = buying[:5]
394
-
395
- return results
396
-
397
- def render(results, cmdenv, tdb):
398
- system, station = results.summary.system, results.summary.station
399
-
400
- if cmdenv.detail:
401
- sysDetail = "(#{} @ {},{},{})".format(
402
- system.ID, system.posX, system.posY, system.posZ
403
- )
404
- else:
405
- sysDetail = "(#{})".format(system.ID)
406
-
407
- print("Station Data:")
408
- print("System....:", system.name(), sysDetail)
409
- print("Station...:", station.dbname, "(#{})".format(station.ID))
410
-
411
- if cmdenv.detail:
412
- siblings = ", ".join(
413
- stn.dbname
414
- for stn in system.stations
415
- if stn is not station
416
- )
417
- if siblings:
418
- print("Also Here.:", siblings)
419
-
420
- ls = station.distFromStar()
421
- if cmdenv.detail and ls == '?':
422
- ls = '0 [unknown]'
423
- print("Stn/Ls....:", ls)
424
-
425
- def _detail(value, source):
426
- detail = source[value]
427
- if cmdenv.detail and detail == '?':
428
- detail += ' [unknown]'
429
- return detail
430
- print("Pad Size..:", _detail(station.maxPadSize, TradeDB.padSizes))
431
- print("Market....:", _detail(station.market, TradeDB.marketStates))
432
- print("B/Market..:", _detail(station.blackMarket, TradeDB.marketStates))
433
- print("Shipyard..:", _detail(station.shipyard, TradeDB.marketStates))
434
- print("Outfitting:", _detail(station.outfitting, TradeDB.marketStates))
435
- print("Rearm.....:", _detail(station.rearm, TradeDB.marketStates))
436
- print("Refuel....:", _detail(station.refuel, TradeDB.marketStates))
437
- print("Repair....:", _detail(station.repair, TradeDB.marketStates))
438
- print("Planetary.:", _detail(station.planetary, TradeDB.planetStates))
439
- print("Fleet.....:", _detail(station.fleet, TradeDB.fleetStates))
440
- print("Odyssey...:", _detail(station.odyssey, TradeDB.odysseyStates))
441
- print("Prices....:", station.itemCount or 'None')
442
-
443
- if station.itemCount == 0:
444
- return
445
-
446
- newest, oldest = tdb.query("""
447
- SELECT JULIANDAY('NOW') - JULIANDAY(MAX(si.modified)),
448
- JULIANDAY('NOW') - JULIANDAY(MIN(si.modified))
449
- FROM StationItem si
450
- WHERE station_id = ?
451
- """, [station.ID]).fetchone()
452
- if newest or oldest:
453
- # less than a quarter hour difference? ignore?
454
- if abs(newest - oldest) < (1 / (24 * 4)):
455
- pricesAge = "{:.2f} days".format(oldest)
456
- else:
457
- pricesAge = "{:.2f}-{:.2f} days".format(newest, oldest)
458
- else:
459
- pricesAge = "[n/a]"
460
-
461
- print("Price Age.:", pricesAge)
462
-
463
- def makeBest(rows, explanation, alt, maxLen, starFn):
464
- if not rows:
465
- return "[n/a]"
466
- best = []
467
- for irow in rows:
468
- star = '*' if starFn(irow.price, irow.avgTrade) else ''
469
- best.append([irow, star])
470
-
471
- if not cmdenv.detail:
472
- return ', '.join(irow[0].item.name() + irow[1] for irow in best)
473
-
474
- bestText = "("+explanation+")"
475
- for irow in best:
476
- bestText += "\n {:<{len}} @ {:7n}cr (Avg {} {:7n}cr)".format(
477
- irow[0].item.name(cmdenv.detail) + irow[1],
478
- irow[0].price,
479
- alt,
480
- irow[0].avgTrade,
481
- len=maxLen + 1,
482
- )
483
- return bestText
484
-
485
-
486
- longestNameLen = max(
487
- max_len(results.summary.selling, key=lambda row: row.item.name(cmdenv.detail)),
488
- max_len(results.summary.buying, key=lambda row: row.item.name(cmdenv.detail)),
489
- )
490
- print("Best Buy..:", makeBest(
491
- results.summary.selling, "Buy from this station", "Sell", longestNameLen,
492
- starFn=lambda price, avgCr: price <= (avgCr * 0.9),
493
- ))
494
- print("Best Sale.:", makeBest(
495
- results.summary.buying, "Sell to this station", "Cost", longestNameLen,
496
- starFn=lambda price, avgCr: price >= (avgCr * 1.1),
497
- ))
65
+ def render(results, cmdenv, tdb=None):
66
+ # No output beyond the banner emitted in run().
67
+ return